3729 lines · 83.6 KB · C
RawDownload
/* * Algorithm testing framework and tests. * * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> * Copyright (c) 2002 Jean-Francois Dive <jef@linuxbe.org> * Copyright (c) 2007 Nokia Siemens Networks * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au> * * 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/aead.h>#include <crypto/hash.h>#include <crypto/skcipher.h>#include <linux/err.h>#include <linux/fips.h>#include <linux/module.h>#include <linux/scatterlist.h>#include <linux/slab.h>#include <linux/string.h>#include <crypto/rng.h>#include <crypto/drbg.h>#include <crypto/akcipher.h>#include <crypto/kpp.h>#include <crypto/acompress.h> #include "internal.h" static bool notests;module_param(notests, bool, 0644);MODULE_PARM_DESC(notests, "disable crypto self-tests"); #ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS /* a perfect nop */int alg_test(const char *driver, const char *alg, u32 type, u32 mask){	return 0;} #else #include "testmgr.h" /* * Need slab memory for testing (size in number of pages). */#define XBUFSIZE	8 /* * Indexes into the xbuf to simulate cross-page access. */#define IDX1		32#define IDX2		32400#define IDX3		1511#define IDX4		8193#define IDX5		22222#define IDX6		17101#define IDX7		27333#define IDX8		3000 /** Used by test_cipher()*/#define ENCRYPT 1#define DECRYPT 0 struct tcrypt_result {	struct completion completion;	int err;}; struct aead_test_suite {	struct {		const struct aead_testvec *vecs;		unsigned int count;	} enc, dec;}; struct cipher_test_suite {	struct {		const struct cipher_testvec *vecs;		unsigned int count;	} enc, dec;}; struct comp_test_suite {	struct {		const struct comp_testvec *vecs;		unsigned int count;	} comp, decomp;}; struct hash_test_suite {	const struct hash_testvec *vecs;	unsigned int count;}; struct cprng_test_suite {	const struct cprng_testvec *vecs;	unsigned int count;}; struct drbg_test_suite {	const struct drbg_testvec *vecs;	unsigned int count;}; struct akcipher_test_suite {	const struct akcipher_testvec *vecs;	unsigned int count;}; struct kpp_test_suite {	const struct kpp_testvec *vecs;	unsigned int count;}; struct alg_test_desc {	const char *alg;	int (*test)(const struct alg_test_desc *desc, const char *driver,		    u32 type, u32 mask);	int fips_allowed;	/* set if alg is allowed in fips mode */ 	union {		struct aead_test_suite aead;		struct cipher_test_suite cipher;		struct comp_test_suite comp;		struct hash_test_suite hash;		struct cprng_test_suite cprng;		struct drbg_test_suite drbg;		struct akcipher_test_suite akcipher;		struct kpp_test_suite kpp;	} suite;}; static const unsigned int IDX[8] = {	IDX1, IDX2, IDX3, IDX4, IDX5, IDX6, IDX7, IDX8 }; static void hexdump(unsigned char *buf, unsigned int len){	print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,			16, 1,			buf, len, false);} 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 int testmgr_alloc_buf(char *buf[XBUFSIZE]){	int i; 	for (i = 0; i < XBUFSIZE; i++) {		buf[i] = (void *)__get_free_page(GFP_KERNEL);		if (!buf[i])			goto err_free_buf;	} 	return 0; err_free_buf:	while (i-- > 0)		free_page((unsigned long)buf[i]); 	return -ENOMEM;} static void testmgr_free_buf(char *buf[XBUFSIZE]){	int i; 	for (i = 0; i < XBUFSIZE; i++)		free_page((unsigned long)buf[i]);} static int wait_async_op(struct tcrypt_result *tr, int ret){	if (ret == -EINPROGRESS || ret == -EBUSY) {		wait_for_completion(&tr->completion);		reinit_completion(&tr->completion);		ret = tr->err;	}	return ret;} static int ahash_partial_update(struct ahash_request **preq,	struct crypto_ahash *tfm, const struct hash_testvec *template,	void *hash_buff, int k, int temp, struct scatterlist *sg,	const char *algo, char *result, struct tcrypt_result *tresult){	char *state;	struct ahash_request *req;	int statesize, ret = -EINVAL;	const char guard[] = { 0x00, 0xba, 0xad, 0x00 }; 	req = *preq;	statesize = crypto_ahash_statesize(			crypto_ahash_reqtfm(req));	state = kmalloc(statesize + sizeof(guard), GFP_KERNEL);	if (!state) {		pr_err("alg: hash: Failed to alloc state for %s\n", algo);		goto out_nostate;	}	memcpy(state + statesize, guard, sizeof(guard));	ret = crypto_ahash_export(req, state);	WARN_ON(memcmp(state + statesize, guard, sizeof(guard)));	if (ret) {		pr_err("alg: hash: Failed to export() for %s\n", algo);		goto out;	}	ahash_request_free(req);	req = ahash_request_alloc(tfm, GFP_KERNEL);	if (!req) {		pr_err("alg: hash: Failed to alloc request for %s\n", algo);		goto out_noreq;	}	ahash_request_set_callback(req,		CRYPTO_TFM_REQ_MAY_BACKLOG,		tcrypt_complete, tresult); 	memcpy(hash_buff, template->plaintext + temp,		template->tap[k]);	sg_init_one(&sg[0], hash_buff, template->tap[k]);	ahash_request_set_crypt(req, sg, result, template->tap[k]);	ret = crypto_ahash_import(req, state);	if (ret) {		pr_err("alg: hash: Failed to import() for %s\n", algo);		goto out;	}	ret = wait_async_op(tresult, crypto_ahash_update(req));	if (ret)		goto out;	*preq = req;	ret = 0;	goto out_noreq;out:	ahash_request_free(req);out_noreq:	kfree(state);out_nostate:	return ret;} static int __test_hash(struct crypto_ahash *tfm,		       const struct hash_testvec *template, unsigned int tcount,		       bool use_digest, const int align_offset){	const char *algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm));	size_t digest_size = crypto_ahash_digestsize(tfm);	unsigned int i, j, k, temp;	struct scatterlist sg[8];	char *result;	char *key;	struct ahash_request *req;	struct tcrypt_result tresult;	void *hash_buff;	char *xbuf[XBUFSIZE];	int ret = -ENOMEM; 	result = kmalloc(digest_size, GFP_KERNEL);	if (!result)		return ret;	key = kmalloc(MAX_KEYLEN, GFP_KERNEL);	if (!key)		goto out_nobuf;	if (testmgr_alloc_buf(xbuf))		goto out_nobuf; 	init_completion(&tresult.completion); 	req = ahash_request_alloc(tfm, GFP_KERNEL);	if (!req) {		printk(KERN_ERR "alg: hash: Failed to allocate request for "		       "%s\n", algo);		goto out_noreq;	}	ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,				   tcrypt_complete, &tresult); 	j = 0;	for (i = 0; i < tcount; i++) {		if (template[i].np)			continue; 		ret = -EINVAL;		if (WARN_ON(align_offset + template[i].psize > PAGE_SIZE))			goto out; 		j++;		memset(result, 0, digest_size); 		hash_buff = xbuf[0];		hash_buff += align_offset; 		memcpy(hash_buff, template[i].plaintext, template[i].psize);		sg_init_one(&sg[0], hash_buff, template[i].psize); 		if (template[i].ksize) {			crypto_ahash_clear_flags(tfm, ~0);			if (template[i].ksize > MAX_KEYLEN) {				pr_err("alg: hash: setkey failed on test %d for %s: key size %d > %d\n",				       j, algo, template[i].ksize, MAX_KEYLEN);				ret = -EINVAL;				goto out;			}			memcpy(key, template[i].key, template[i].ksize);			ret = crypto_ahash_setkey(tfm, key, template[i].ksize);			if (ret) {				printk(KERN_ERR "alg: hash: setkey failed on "				       "test %d for %s: ret=%d\n", j, algo,				       -ret);				goto out;			}		} 		ahash_request_set_crypt(req, sg, result, template[i].psize);		if (use_digest) {			ret = wait_async_op(&tresult, crypto_ahash_digest(req));			if (ret) {				pr_err("alg: hash: digest failed on test %d "				       "for %s: ret=%d\n", j, algo, -ret);				goto out;			}		} else {			ret = wait_async_op(&tresult, crypto_ahash_init(req));			if (ret) {				pr_err("alg: hash: init failed on test %d "				       "for %s: ret=%d\n", j, algo, -ret);				goto out;			}			ret = wait_async_op(&tresult, crypto_ahash_update(req));			if (ret) {				pr_err("alg: hash: update failed on test %d "				       "for %s: ret=%d\n", j, algo, -ret);				goto out;			}			ret = wait_async_op(&tresult, crypto_ahash_final(req));			if (ret) {				pr_err("alg: hash: final failed on test %d "				       "for %s: ret=%d\n", j, algo, -ret);				goto out;			}		} 		if (memcmp(result, template[i].digest,			   crypto_ahash_digestsize(tfm))) {			printk(KERN_ERR "alg: hash: Test %d failed for %s\n",			       j, algo);			hexdump(result, crypto_ahash_digestsize(tfm));			ret = -EINVAL;			goto out;		}	} 	j = 0;	for (i = 0; i < tcount; i++) {		/* alignment tests are only done with continuous buffers */		if (align_offset != 0)			break; 		if (!template[i].np)			continue; 		j++;		memset(result, 0, digest_size); 		temp = 0;		sg_init_table(sg, template[i].np);		ret = -EINVAL;		for (k = 0; k < template[i].np; k++) {			if (WARN_ON(offset_in_page(IDX[k]) +				    template[i].tap[k] > PAGE_SIZE))				goto out;			sg_set_buf(&sg[k],				   memcpy(xbuf[IDX[k] >> PAGE_SHIFT] +					  offset_in_page(IDX[k]),					  template[i].plaintext + temp,					  template[i].tap[k]),				   template[i].tap[k]);			temp += template[i].tap[k];		} 		if (template[i].ksize) {			if (template[i].ksize > MAX_KEYLEN) {				pr_err("alg: hash: setkey failed on test %d for %s: key size %d > %d\n",				       j, algo, template[i].ksize, MAX_KEYLEN);				ret = -EINVAL;				goto out;			}			crypto_ahash_clear_flags(tfm, ~0);			memcpy(key, template[i].key, template[i].ksize);			ret = crypto_ahash_setkey(tfm, key, template[i].ksize); 			if (ret) {				printk(KERN_ERR "alg: hash: setkey "				       "failed on chunking test %d "				       "for %s: ret=%d\n", j, algo, -ret);				goto out;			}		} 		ahash_request_set_crypt(req, sg, result, template[i].psize);		ret = crypto_ahash_digest(req);		switch (ret) {		case 0:			break;		case -EINPROGRESS:		case -EBUSY:			wait_for_completion(&tresult.completion);			reinit_completion(&tresult.completion);			ret = tresult.err;			if (!ret)				break;			/* fall through */		default:			printk(KERN_ERR "alg: hash: digest failed "			       "on chunking test %d for %s: "			       "ret=%d\n", j, algo, -ret);			goto out;		} 		if (memcmp(result, template[i].digest,			   crypto_ahash_digestsize(tfm))) {			printk(KERN_ERR "alg: hash: Chunking test %d "			       "failed for %s\n", j, algo);			hexdump(result, crypto_ahash_digestsize(tfm));			ret = -EINVAL;			goto out;		}	} 	/* partial update exercise */	j = 0;	for (i = 0; i < tcount; i++) {		/* alignment tests are only done with continuous buffers */		if (align_offset != 0)			break; 		if (template[i].np < 2)			continue; 		j++;		memset(result, 0, digest_size); 		ret = -EINVAL;		hash_buff = xbuf[0];		memcpy(hash_buff, template[i].plaintext,			template[i].tap[0]);		sg_init_one(&sg[0], hash_buff, template[i].tap[0]); 		if (template[i].ksize) {			crypto_ahash_clear_flags(tfm, ~0);			if (template[i].ksize > MAX_KEYLEN) {				pr_err("alg: hash: setkey failed on test %d for %s: key size %d > %d\n",					j, algo, template[i].ksize, MAX_KEYLEN);				ret = -EINVAL;				goto out;			}			memcpy(key, template[i].key, template[i].ksize);			ret = crypto_ahash_setkey(tfm, key, template[i].ksize);			if (ret) {				pr_err("alg: hash: setkey failed on test %d for %s: ret=%d\n",					j, algo, -ret);				goto out;			}		} 		ahash_request_set_crypt(req, sg, result, template[i].tap[0]);		ret = wait_async_op(&tresult, crypto_ahash_init(req));		if (ret) {			pr_err("alg: hash: init failed on test %d for %s: ret=%d\n",				j, algo, -ret);			goto out;		}		ret = wait_async_op(&tresult, crypto_ahash_update(req));		if (ret) {			pr_err("alg: hash: update failed on test %d for %s: ret=%d\n",				j, algo, -ret);			goto out;		}

Lines 1–500 of 3729.