1869 lines · 44.9 KB · C
RawDownload
/* * Quick & dirty crypto testing module. * * This will only exist until we have a better testing mechanism * (e.g. a char device). * * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> * Copyright (c) 2002 Jean-Francois Dive <jef@linuxbe.org> * Copyright (c) 2007 Nokia Siemens Networks * * Updated RFC4106 AES-GCM testing. *    Authors: Aidan O'Mahony (aidan.o.mahony@intel.com) *             Adrian Hoban <adrian.hoban@intel.com> *             Gabriele Paoloni <gabriele.paoloni@intel.com> *             Tadeusz Struk (tadeusz.struk@intel.com) *             Copyright (c) 2010, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include <crypto/hash.h>#include <linux/err.h>#include <linux/init.h>#include <linux/gfp.h>#include <linux/module.h>#include <linux/scatterlist.h>#include <linux/string.h>#include <linux/moduleparam.h>#include <linux/jiffies.h>#include <linux/timex.h>#include <linux/interrupt.h>#include "tcrypt.h"#include "internal.h" /* * Need slab memory for testing (size in number of pages). */#define TVMEMSIZE	4 /** Used by test_cipher_speed()*/#define ENCRYPT 1#define DECRYPT 0 /* * Used by test_cipher_speed() */static unsigned int sec; static char *alg = NULL;static u32 type;static u32 mask;static int mode;static char *tvmem[TVMEMSIZE]; static char *check[] = {	"des", "md5", "des3_ede", "rot13", "sha1", "sha224", "sha256",	"blowfish", "twofish", "serpent", "sha384", "sha512", "md4", "aes",	"cast6", "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea",	"khazad", "wp512", "wp384", "wp256", "tnepres", "xeta",  "fcrypt",	"camellia", "seed", "salsa20", "rmd128", "rmd160", "rmd256", "rmd320",	"lzo", "cts", "zlib", NULL}; static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc,			       struct scatterlist *sg, int blen, int sec){	unsigned long start, end;	int bcount;	int ret; 	for (start = jiffies, end = start + sec * HZ, bcount = 0;	     time_before(jiffies, end); bcount++) {		if (enc)			ret = crypto_blkcipher_encrypt(desc, sg, sg, blen);		else			ret = crypto_blkcipher_decrypt(desc, sg, sg, blen); 		if (ret)			return ret;	} 	printk("%d operations in %d seconds (%ld bytes)\n",	       bcount, sec, (long)bcount * blen);	return 0;} static int test_cipher_cycles(struct blkcipher_desc *desc, int enc,			      struct scatterlist *sg, int blen){	unsigned long cycles = 0;	int ret = 0;	int i; 	local_irq_disable(); 	/* Warm-up run. */	for (i = 0; i < 4; i++) {		if (enc)			ret = crypto_blkcipher_encrypt(desc, sg, sg, blen);		else			ret = crypto_blkcipher_decrypt(desc, sg, sg, blen); 		if (ret)			goto out;	} 	/* The real thing. */	for (i = 0; i < 8; i++) {		cycles_t start, end; 		start = get_cycles();		if (enc)			ret = crypto_blkcipher_encrypt(desc, sg, sg, blen);		else			ret = crypto_blkcipher_decrypt(desc, sg, sg, blen);		end = get_cycles(); 		if (ret)			goto out; 		cycles += end - start;	} out:	local_irq_enable(); 	if (ret == 0)		printk("1 operation in %lu cycles (%d bytes)\n",		       (cycles + 4) / 8, blen); 	return ret;} static u32 block_sizes[] = { 16, 64, 256, 1024, 8192, 0 }; static void test_cipher_speed(const char *algo, int enc, unsigned int sec,			      struct cipher_speed_template *template,			      unsigned int tcount, u8 *keysize){	unsigned int ret, i, j, iv_len;	const char *key;	char iv[128];	struct crypto_blkcipher *tfm;	struct blkcipher_desc desc;	const char *e;	u32 *b_size; 	if (enc == ENCRYPT)	        e = "encryption";	else		e = "decryption"; 	printk("\ntesting speed of %s %s\n", algo, e); 	tfm = crypto_alloc_blkcipher(algo, 0, CRYPTO_ALG_ASYNC); 	if (IS_ERR(tfm)) {		printk("failed to load transform for %s: %ld\n", algo,		       PTR_ERR(tfm));		return;	}	desc.tfm = tfm;	desc.flags = 0; 	i = 0;	do { 		b_size = block_sizes;		do {			struct scatterlist sg[TVMEMSIZE]; 			if ((*keysize + *b_size) > TVMEMSIZE * PAGE_SIZE) {				printk("template (%u) too big for "				       "tvmem (%lu)\n", *keysize + *b_size,				       TVMEMSIZE * PAGE_SIZE);				goto out;			} 			printk("test %u (%d bit key, %d byte blocks): ", i,					*keysize * 8, *b_size); 			memset(tvmem[0], 0xff, PAGE_SIZE); 			/* set key, plain text and IV */			key = tvmem[0];			for (j = 0; j < tcount; j++) {				if (template[j].klen == *keysize) {					key = template[j].key;					break;				}			} 			ret = crypto_blkcipher_setkey(tfm, key, *keysize);			if (ret) {				printk("setkey() failed flags=%x\n",						crypto_blkcipher_get_flags(tfm));				goto out;			} 			sg_init_table(sg, TVMEMSIZE);			sg_set_buf(sg, tvmem[0] + *keysize,				   PAGE_SIZE - *keysize);			for (j = 1; j < TVMEMSIZE; j++) {				sg_set_buf(sg + j, tvmem[j], PAGE_SIZE);				memset (tvmem[j], 0xff, PAGE_SIZE);			} 			iv_len = crypto_blkcipher_ivsize(tfm);			if (iv_len) {				memset(&iv, 0xff, iv_len);				crypto_blkcipher_set_iv(tfm, iv, iv_len);			} 			if (sec)				ret = test_cipher_jiffies(&desc, enc, sg,							  *b_size, sec);			else				ret = test_cipher_cycles(&desc, enc, sg,							 *b_size); 			if (ret) {				printk("%s() failed flags=%x\n", e, desc.flags);				break;			}			b_size++;			i++;		} while (*b_size);		keysize++;	} while (*keysize); out:	crypto_free_blkcipher(tfm);} static int test_hash_jiffies_digest(struct hash_desc *desc,				    struct scatterlist *sg, int blen,				    char *out, int sec){	unsigned long start, end;	int bcount;	int ret; 	for (start = jiffies, end = start + sec * HZ, bcount = 0;	     time_before(jiffies, end); bcount++) {		ret = crypto_hash_digest(desc, sg, blen, out);		if (ret)			return ret;	} 	printk("%6u opers/sec, %9lu bytes/sec\n",	       bcount / sec, ((long)bcount * blen) / sec); 	return 0;} static int test_hash_jiffies(struct hash_desc *desc, struct scatterlist *sg,			     int blen, int plen, char *out, int sec){	unsigned long start, end;	int bcount, pcount;	int ret; 	if (plen == blen)		return test_hash_jiffies_digest(desc, sg, blen, out, sec); 	for (start = jiffies, end = start + sec * HZ, bcount = 0;	     time_before(jiffies, end); bcount++) {		ret = crypto_hash_init(desc);		if (ret)			return ret;		for (pcount = 0; pcount < blen; pcount += plen) {			ret = crypto_hash_update(desc, sg, plen);			if (ret)				return ret;		}		/* we assume there is enough space in 'out' for the result */		ret = crypto_hash_final(desc, out);		if (ret)			return ret;	} 	printk("%6u opers/sec, %9lu bytes/sec\n",	       bcount / sec, ((long)bcount * blen) / sec); 	return 0;} static int test_hash_cycles_digest(struct hash_desc *desc,				   struct scatterlist *sg, int blen, char *out){	unsigned long cycles = 0;	int i;	int ret; 	local_irq_disable(); 	/* Warm-up run. */	for (i = 0; i < 4; i++) {		ret = crypto_hash_digest(desc, sg, blen, out);		if (ret)			goto out;	} 	/* The real thing. */	for (i = 0; i < 8; i++) {		cycles_t start, end; 		start = get_cycles(); 		ret = crypto_hash_digest(desc, sg, blen, out);		if (ret)			goto out; 		end = get_cycles(); 		cycles += end - start;	} out:	local_irq_enable(); 	if (ret)		return ret; 	printk("%6lu cycles/operation, %4lu cycles/byte\n",	       cycles / 8, cycles / (8 * blen)); 	return 0;} static int test_hash_cycles(struct hash_desc *desc, struct scatterlist *sg,			    int blen, int plen, char *out){	unsigned long cycles = 0;	int i, pcount;	int ret; 	if (plen == blen)		return test_hash_cycles_digest(desc, sg, blen, out); 	local_irq_disable(); 	/* Warm-up run. */	for (i = 0; i < 4; i++) {		ret = crypto_hash_init(desc);		if (ret)			goto out;		for (pcount = 0; pcount < blen; pcount += plen) {			ret = crypto_hash_update(desc, sg, plen);			if (ret)				goto out;		}		ret = crypto_hash_final(desc, out);		if (ret)			goto out;	} 	/* The real thing. */	for (i = 0; i < 8; i++) {		cycles_t start, end; 		start = get_cycles(); 		ret = crypto_hash_init(desc);		if (ret)			goto out;		for (pcount = 0; pcount < blen; pcount += plen) {			ret = crypto_hash_update(desc, sg, plen);			if (ret)				goto out;		}		ret = crypto_hash_final(desc, out);		if (ret)			goto out; 		end = get_cycles(); 		cycles += end - start;	} out:	local_irq_enable(); 	if (ret)		return ret; 	printk("%6lu cycles/operation, %4lu cycles/byte\n",	       cycles / 8, cycles / (8 * blen)); 	return 0;} static void test_hash_sg_init(struct scatterlist *sg){	int i; 	sg_init_table(sg, TVMEMSIZE);	for (i = 0; i < TVMEMSIZE; i++) {		sg_set_buf(sg + i, tvmem[i], PAGE_SIZE);		memset(tvmem[i], 0xff, PAGE_SIZE);	}} static void test_hash_speed(const char *algo, unsigned int sec,			    struct hash_speed *speed){	struct scatterlist sg[TVMEMSIZE];	struct crypto_hash *tfm;	struct hash_desc desc;	static char output[1024];	int i;	int ret; 	printk(KERN_INFO "\ntesting speed of %s\n", algo); 	tfm = crypto_alloc_hash(algo, 0, CRYPTO_ALG_ASYNC); 	if (IS_ERR(tfm)) {		printk(KERN_ERR "failed to load transform for %s: %ld\n", algo,		       PTR_ERR(tfm));		return;	} 	desc.tfm = tfm;	desc.flags = 0; 	if (crypto_hash_digestsize(tfm) > sizeof(output)) {		printk(KERN_ERR "digestsize(%u) > outputbuffer(%zu)\n",		       crypto_hash_digestsize(tfm), sizeof(output));		goto out;	} 	test_hash_sg_init(sg);	for (i = 0; speed[i].blen != 0; i++) {		if (speed[i].blen > TVMEMSIZE * PAGE_SIZE) {			printk(KERN_ERR			       "template (%u) too big for tvmem (%lu)\n",			       speed[i].blen, TVMEMSIZE * PAGE_SIZE);			goto out;		} 		if (speed[i].klen)			crypto_hash_setkey(tfm, tvmem[0], speed[i].klen); 		printk(KERN_INFO "test%3u "		       "(%5u byte blocks,%5u bytes per update,%4u updates): ",		       i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen); 		if (sec)			ret = test_hash_jiffies(&desc, sg, speed[i].blen,						speed[i].plen, output, sec);		else			ret = test_hash_cycles(&desc, sg, speed[i].blen,					       speed[i].plen, output); 		if (ret) {			printk(KERN_ERR "hashing failed ret=%d\n", ret);			break;		}	} out:	crypto_free_hash(tfm);} struct tcrypt_result {	struct completion completion;	int err;}; static void tcrypt_complete(struct crypto_async_request *req, int err){	struct tcrypt_result *res = req->data; 	if (err == -EINPROGRESS)		return; 	res->err = err;	complete(&res->completion);} static inline int do_one_ahash_op(struct ahash_request *req, int ret){	if (ret == -EINPROGRESS || ret == -EBUSY) {		struct tcrypt_result *tr = req->base.data; 		ret = wait_for_completion_interruptible(&tr->completion);		if (!ret)			ret = tr->err;		reinit_completion(&tr->completion);	}	return ret;} 

Lines 1–500 of 1869.