Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 531

kernel-2.6.18-238.el5.src.rpm

From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 27 Aug 2008 10:18:37 +1000
Subject: [CRYPTO] cryptomgr - Test ciphers using ECB
Message-id: E1KY8k9-0002Hl-00@gondolin.me.apana.org.au
O-Subject: [PATCH 15/19] crypto: cryptomgr - Test ciphers using ECB
Bugzilla: 446522

RHEL5 bugzilla #446522

crypto: cryptomgr - Test ciphers using ECB

As it is we only test ciphers when combined with a mode.  That means
users that do not invoke a mode of operations may get an untested
cipher.

This patch tests all ciphers using the ECB mode so that simple cipher
users such as ansi-cprng are also protected.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

diff --git a/crypto/Makefile b/crypto/Makefile
index 23e36b9..2e20c06 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o
 obj-$(CONFIG_CRYPTO_API) += crypto_api.o
 obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
 obj-$(CONFIG_CRYPTO_MANAGER) += testmgr.o
+obj-$(CONFIG_CRYPTO_MANAGER) += testmgr_cipher.o
 obj-$(CONFIG_CRYPTO_HMAC) += hmac_old.o
 obj-$(CONFIG_CRYPTO_NHMAC) += hmac.o
 obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
diff --git a/crypto/api.c b/crypto/api.c
index aa3552f..43fb654 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -15,7 +15,7 @@
  *
  */
 
-#include <linux/compiler.h>
+#include <linux/completion.h>
 #include <linux/init.h>
 #include <linux/crypto.h>
 #include <linux/errno.h>
@@ -30,6 +30,9 @@
 LIST_HEAD(crypto_alg_list);
 DECLARE_RWSEM(crypto_alg_sem);
 
+BLOCKING_NOTIFIER_HEAD(ocrypto_chain);
+EXPORT_SYMBOL_GPL(ocrypto_chain);
+
 static inline int crypto_alg_get(struct crypto_alg *alg)
 {
 	return try_module_get(alg->cra_module);
@@ -254,12 +257,35 @@ static inline int crypto_set_driver_name(struct crypto_alg *alg)
 	return 0;
 }
 
+static int ocrypto_probing_notify(unsigned long val, void *v)
+{
+	int ok;
+
+	ok = blocking_notifier_call_chain(&ocrypto_chain, val, v);
+	if (ok == NOTIFY_DONE) {
+		request_module("testmgr_cipher");
+		ok = blocking_notifier_call_chain(&ocrypto_chain, val, v);
+	}
+
+	return ok;
+}
+
+static DECLARE_COMPLETION(test_done);
+static int test_err;
+
+void ocrypto_alg_tested(const char *name, int err)
+{
+	test_err = err;
+	complete(&test_done);
+}
+EXPORT_SYMBOL_GPL(ocrypto_alg_tested);
+
 int crypto_register_alg(struct crypto_alg *alg)
 {
 	int ret;
 	struct crypto_alg *q;
-	char name[CRYPTO_MAX_ALG_NAME];
-	DEFINE_MUTEX(test_lock);
+	struct crypto_cipher_test param;
+	static DEFINE_MUTEX(test_lock);
 
 	if (alg->cra_alignmask & (alg->cra_alignmask + 1))
 		return -EINVAL;
@@ -277,7 +303,8 @@ int crypto_register_alg(struct crypto_alg *alg)
 	if (unlikely(ret))
 		return ret;
 
-	memcpy(name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
+	param.alg = alg;
+	memcpy(param.name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
 	memcpy(alg->cra_name, "untested", sizeof("untested"));
 
 	mutex_lock(&test_lock);
@@ -299,8 +326,27 @@ out:
 		goto out2;
 
 	switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
+	case CRYPTO_ALG_TYPE_CIPHER:
+		INIT_COMPLETION(test_done);
+		ret = ocrypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, &param);
+		if (ret != NOTIFY_STOP) {
+			if (unlikely(ret != NOTIFY_DONE)) {
+				WARN_ON(1);
+				ret = -EINVAL;
+				break;
+			}
+			ocrypto_alg_tested(alg->cra_driver_name, 0);
+		}
+
+		ret = wait_for_completion_interruptible(&test_done);
+		if (unlikely(ret))
+			WARN_ON(1);
+		else
+			ret = test_err;
+		break;
+
 	case CRYPTO_ALG_TYPE_DIGEST:
-		ret = digest_test(alg->cra_driver_name, name);
+		ret = digest_test(alg->cra_driver_name, param.name);
 		break;
 	}
 
@@ -308,7 +354,7 @@ out:
 		crypto_unregister_alg(alg);
 
 out2:
-	memcpy(alg->cra_name, name, CRYPTO_MAX_ALG_NAME);
+	memcpy(alg->cra_name, param.name, CRYPTO_MAX_ALG_NAME);
 	mutex_unlock(&test_lock);
 	return ret;
 }
@@ -346,6 +392,18 @@ int crypto_alg_available(const char *name, u32 flags)
 	return ret;
 }
 
+int ocrypto_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&ocrypto_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ocrypto_register_notifier);
+
+int ocrypto_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&ocrypto_chain, nb);
+}
+EXPORT_SYMBOL_GPL(ocrypto_unregister_notifier);
+
 static int __init init_crypto(void)
 {
 	printk(KERN_INFO "Initializing Cryptographic API\n");
diff --git a/crypto/internal.h b/crypto/internal.h
index d942449..c7c68f4 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -19,12 +19,28 @@
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/kernel.h>
+#include <linux/notifier.h>
 #include <linux/rwsem.h>
 #include <linux/slab.h>
 #include <asm/kmap_types.h>
 
+/* Crypto notification events. */
+enum {
+	CRYPTO_MSG_ALG_REQUEST,
+	CRYPTO_MSG_ALG_REGISTER,
+	CRYPTO_MSG_ALG_UNREGISTER,
+	CRYPTO_MSG_TMPL_REGISTER,
+	CRYPTO_MSG_TMPL_UNREGISTER,
+};
+
+struct crypto_cipher_test {
+	struct crypto_alg *alg;
+	char name[CRYPTO_MAX_ALG_NAME];
+};
+
 extern struct list_head crypto_alg_list;
 extern struct rw_semaphore crypto_alg_sem;
+extern struct blocking_notifier_head ocrypto_chain;
 
 extern enum km_type crypto_km_types[];
 
@@ -108,6 +124,10 @@ void crypto_exit_digest_ops(struct crypto_tfm *tfm);
 void crypto_exit_cipher_ops(struct crypto_tfm *tfm);
 void crypto_exit_compress_ops(struct crypto_tfm *tfm);
 
+int ocrypto_register_notifier(struct notifier_block *nb);
+int ocrypto_unregister_notifier(struct notifier_block *nb);
+void ocrypto_alg_tested(const char *name, int err);
+
 int digest_test(const char *driver, const char *alg);
 
 #endif	/* _CRYPTO_INTERNAL_H */
diff --git a/crypto/ninternal.h b/crypto/ninternal.h
index 07d729c..0ed1e96 100644
--- a/crypto/ninternal.h
+++ b/crypto/ninternal.h
@@ -18,15 +18,6 @@
 
 #include "internal.h"
 
-/* Crypto notification events. */
-enum {
-	CRYPTO_MSG_ALG_REQUEST,
-	CRYPTO_MSG_ALG_REGISTER,
-	CRYPTO_MSG_ALG_UNREGISTER,
-	CRYPTO_MSG_TMPL_REGISTER,
-	CRYPTO_MSG_TMPL_UNREGISTER,
-};
-
 struct crypto_larval {
 	struct ncrypto_alg alg;
 	struct ncrypto_alg *adult;
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 1c93fc7..721bfba 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -516,9 +516,74 @@ out:
 	return ret;
 }
 
-static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
+static int test_cipher(struct crypto_cipher *tfm, int enc,
 		       struct cipher_testvec *template, unsigned int tcount)
 {
+	const char *algo = crypto_tfm_alg_driver_name(crypto_cipher_tfm(tfm));
+	unsigned int i, j, k;
+	int ret;
+	char *q;
+	const char *e;
+	void *data;
+
+	if (enc == ENCRYPT)
+	        e = "encryption";
+	else
+		e = "decryption";
+
+	j = 0;
+	for (i = 0; i < tcount; i++) {
+		if (template[i].np)
+			continue;
+
+		j++;
+
+		data = xbuf[0];
+		memcpy(data, template[i].input, template[i].ilen);
+
+		crypto_cipher_clear_flags(tfm, ~0);
+		if (template[i].wk)
+			crypto_cipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+
+		ret = crypto_cipher_setkey(tfm, template[i].key,
+					   template[i].klen);
+		if (!ret == template[i].fail) {
+			printk(KERN_ERR "alg: cipher: setkey failed "
+			       "on test %d for %s: flags=%x\n", j,
+			       algo, crypto_cipher_get_flags(tfm));
+			goto out;
+		} else if (ret)
+			continue;
+
+		for (k = 0; k < template[i].ilen;
+		     k += crypto_cipher_blocksize(tfm)) {
+			if (enc)
+				crypto_cipher_encrypt_one(tfm, data + k,
+							  data + k);
+			else
+				crypto_cipher_decrypt_one(tfm, data + k,
+							  data + k);
+		}
+
+		q = data;
+		if (memcmp(q, template[i].result, template[i].rlen)) {
+			printk(KERN_ERR "alg: cipher: Test %d failed "
+			       "on %s for %s\n", j, e, algo);
+			hexdump(q, template[i].rlen);
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	ret = 0;
+
+out:
+	return ret;
+}
+
+static int test_skcipher(struct crypto_ablkcipher *tfm, int enc,
+			 struct cipher_testvec *template, unsigned int tcount)
+{
 	const char *algo =
 		ncrypto_tfm_alg_driver_name(crypto_ablkcipher_tfm(tfm));
 	unsigned int i, j, k, n, temp;
@@ -540,8 +605,8 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 
 	req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
 	if (!req) {
-		printk(KERN_ERR "alg: cipher: Failed to allocate request for "
-		       "%s\n", algo);
+		printk(KERN_ERR "alg: skcipher: Failed to allocate request "
+		       "for %s\n", algo);
 		ret = -ENOMEM;
 		goto out;
 	}
@@ -570,7 +635,7 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 			ret = crypto_ablkcipher_setkey(tfm, template[i].key,
 						       template[i].klen);
 			if (!ret == template[i].fail) {
-				printk(KERN_ERR "alg: cipher: setkey failed "
+				printk(KERN_ERR "alg: skcipher: setkey failed "
 				       "on test %d for %s: flags=%x\n", j,
 				       algo, crypto_ablkcipher_get_flags(tfm));
 				goto out;
@@ -598,7 +663,7 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 				}
 				/* fall through */
 			default:
-				printk(KERN_ERR "alg: cipher: %s failed on "
+				printk(KERN_ERR "alg: skcipher: %s failed on "
 				       "test %d for %s: ret=%d\n", e, j, algo,
 				       -ret);
 				goto out;
@@ -606,8 +671,8 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 
 			q = data;
 			if (memcmp(q, template[i].result, template[i].rlen)) {
-				printk(KERN_ERR "alg: cipher: Test %d failed "
-				       "on %s for %s\n", j, e, algo);
+				printk(KERN_ERR "alg: skcipher: Test %d "
+				       "failed on %s for %s\n", j, e, algo);
 				hexdump(q, template[i].rlen);
 				ret = -EINVAL;
 				goto out;
@@ -634,7 +699,7 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 			ret = crypto_ablkcipher_setkey(tfm, template[i].key,
 						       template[i].klen);
 			if (!ret == template[i].fail) {
-				printk(KERN_ERR "alg: cipher: setkey failed "
+				printk(KERN_ERR "alg: skcipher: setkey failed "
 				       "on chunk test %d for %s: flags=%x\n",
 				       j, algo,
 				       crypto_ablkcipher_get_flags(tfm));
@@ -687,7 +752,7 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 				}
 				/* fall through */
 			default:
-				printk(KERN_ERR "alg: cipher: %s failed on "
+				printk(KERN_ERR "alg: skcipher: %s failed on "
 				       "chunk test %d for %s: ret=%d\n", e, j,
 				       algo, -ret);
 				goto out;
@@ -701,7 +766,7 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 
 				if (memcmp(q, template[i].result + temp,
 					   template[i].tap[k])) {
-					printk(KERN_ERR "alg: cipher: Chunk "
+					printk(KERN_ERR "alg: skcipher: Chunk "
 					       "test %d failed on %s at page "
 					       "%u for %s\n", j, e, k, algo);
 					hexdump(q, template[i].tap[k]);
@@ -712,7 +777,7 @@ static int test_cipher(struct crypto_ablkcipher *tfm, int enc,
 				for (n = 0; offset_in_page(q + n) && q[n]; n++)
 					;
 				if (n) {
-					printk(KERN_ERR "alg: cipher: "
+					printk(KERN_ERR "alg: skcipher: "
 					       "Result buffer corruption in "
 					       "chunk test %d on %s at page "
 					       "%u for %s: %u bytes:\n", j, e,
@@ -764,10 +829,10 @@ out:
 static int alg_test_cipher(const struct alg_test_desc *desc,
 			   const char *driver, u32 type, u32 mask)
 {
-	struct crypto_ablkcipher *tfm;
+	struct crypto_cipher *tfm;
 	int err = 0;
 
-	tfm = crypto_alloc_ablkcipher(driver, type, mask);
+	tfm = crypto_alloc_cipher(driver, type, mask);
 	if (IS_ERR(tfm)) {
 		printk(KERN_ERR "alg: cipher: Failed to load transform for "
 		       "%s: %ld\n", driver, PTR_ERR(tfm));
@@ -786,6 +851,35 @@ static int alg_test_cipher(const struct alg_test_desc *desc,
 				  desc->suite.cipher.dec.count);
 
 out:
+	crypto_free_cipher(tfm);
+	return err;
+}
+
+static int alg_test_skcipher(const struct alg_test_desc *desc,
+			     const char *driver, u32 type, u32 mask)
+{
+	struct crypto_ablkcipher *tfm;
+	int err = 0;
+
+	tfm = crypto_alloc_ablkcipher(driver, type, mask);
+	if (IS_ERR(tfm)) {
+		printk(KERN_ERR "alg: skcipher: Failed to load transform for "
+		       "%s: %ld\n", driver, PTR_ERR(tfm));
+		return PTR_ERR(tfm);
+	}
+
+	if (desc->suite.cipher.enc.vecs) {
+		err = test_skcipher(tfm, ENCRYPT, desc->suite.cipher.enc.vecs,
+				    desc->suite.cipher.enc.count);
+		if (err)
+			goto out;
+	}
+
+	if (desc->suite.cipher.dec.vecs)
+		err = test_skcipher(tfm, DECRYPT, desc->suite.cipher.dec.vecs,
+				    desc->suite.cipher.dec.count);
+
+out:
 	crypto_free_ablkcipher(tfm);
 	return err;
 }
@@ -813,7 +907,7 @@ static int alg_test_hash(const struct alg_test_desc *desc, const char *driver,
 static const struct alg_test_desc alg_test_descs[] = {
 	{
 		.alg = "cbc(aes)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -828,7 +922,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "cbc(anubis)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -843,7 +937,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "cbc(blowfish)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -858,7 +952,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "cbc(des)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -873,7 +967,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "cbc(des3_ede)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -888,7 +982,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "cbc(twofish)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -918,7 +1012,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(aes)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -933,7 +1027,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(anubis)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -948,7 +1042,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(arc4)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -963,7 +1057,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(blowfish)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -978,7 +1072,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(cast5)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -993,7 +1087,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(cast6)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1008,7 +1102,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(des)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1023,7 +1117,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(des3_ede)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1038,7 +1132,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(khazad)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1053,7 +1147,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(serpent)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1068,7 +1162,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(tea)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1083,7 +1177,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(tnepres)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1098,7 +1192,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(twofish)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1113,7 +1207,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(xeta)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1128,7 +1222,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "ecb(xtea)",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1170,7 +1264,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 		}
 	}, {
 		.alg = "rfc3686(ctr(aes))",
-		.test = alg_test_cipher,
+		.test = alg_test_skcipher,
 		.suite = {
 			.cipher = {
 				.enc = {
@@ -1195,7 +1289,7 @@ static const struct alg_test_desc alg_test_descs[] = {
 	}
 };
 
-int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
+static int alg_find_test(const char *alg)
 {
 	int start = 0;
 	int end = ARRAY_SIZE(alg_test_descs);
@@ -1214,10 +1308,38 @@ int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
 			continue;
 		}
 
-		return alg_test_descs[i].test(alg_test_descs + i, driver,
-					      type, mask);
+		return i;
+	}
+
+	return -1;
+}
+
+int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
+{
+	int i;
+
+	if ((type & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_CIPHER) {
+		char nalg[CRYPTO_MAX_ALG_NAME];
+
+		if (snprintf(nalg, sizeof(nalg), "ecb(%s)", alg) >=
+		    sizeof(nalg))
+			return -ENAMETOOLONG;
+
+		i = alg_find_test(nalg);
+		if (i < 0)
+			goto notest;
+
+		return alg_test_cipher(alg_test_descs + i, driver, type, mask);
 	}
 
+	i = alg_find_test(alg);
+	if (i < 0)
+		goto notest;
+
+	return alg_test_descs[i].test(alg_test_descs + i, driver,
+				      type, mask);
+
+notest:
 	printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver);
 	return 0;
 }
@@ -1233,10 +1355,7 @@ static int cryptomgr_test(void *data)
 	      NCRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & NCRYPTO_ALG_GENIV))
 		goto skiptest;
 
-	if ((type & NCRYPTO_ALG_TYPE_MASK) == NCRYPTO_ALG_TYPE_CIPHER)
-		goto skiptest;
-
-	err = alg_test(param->driver, param->alg, 0, NCRYPTO_ALG_TESTED);
+	err = alg_test(param->driver, param->alg, type, NCRYPTO_ALG_TESTED);
 
 skiptest:
 	crypto_alg_tested(param->driver, err);
diff --git a/crypto/testmgr_cipher.c b/crypto/testmgr_cipher.c
new file mode 100644
index 0000000..fec2cca
--- /dev/null
+++ b/crypto/testmgr_cipher.c
@@ -0,0 +1,109 @@
+/*
+ * 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>
+ *
+ * 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 <linux/err.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "ninternal.h"
+
+struct crypto_test_param {
+	char driver[CRYPTO_MAX_ALG_NAME];
+	char alg[CRYPTO_MAX_ALG_NAME];
+	u32 type;
+};
+
+static int cryptomgr_test_cipher(void *data)
+{
+	struct crypto_test_param *param = data;
+	u32 type = param->type;
+
+	ocrypto_alg_tested(param->driver,
+			   alg_test(param->driver, param->alg, type,
+				    NCRYPTO_ALG_TESTED));
+
+	kfree(param);
+	module_put_and_exit(0);
+}
+
+static int cryptomgr_schedule_test_cipher(struct crypto_cipher_test *test)
+{
+	struct crypto_alg *alg = test->alg;
+	struct task_struct *thread;
+	struct crypto_test_param *param;
+
+	if (!try_module_get(THIS_MODULE))
+		goto err;
+
+	param = kzalloc(sizeof(*param), GFP_KERNEL);
+	if (!param)
+		goto err_put_module;
+
+	memcpy(param->driver, alg->cra_driver_name, sizeof(param->driver));
+	memcpy(param->alg, test->name, sizeof(param->alg));
+	param->type = alg->cra_flags;
+
+	thread = kthread_run(cryptomgr_test_cipher, param,
+			     "cryptomgr_test_cipher");
+	if (IS_ERR(thread))
+		goto err_free_param;
+
+	return NOTIFY_STOP;
+
+err_free_param:
+	kfree(param);
+err_put_module:
+	module_put(THIS_MODULE);
+err:
+	return NOTIFY_OK;
+}
+
+static int otestmgr_notify(struct notifier_block *this, unsigned long msg,
+			   void *data)
+{
+	switch (msg) {
+	case CRYPTO_MSG_ALG_REGISTER:
+		return cryptomgr_schedule_test_cipher(data);
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block otestmgr_notifier = {
+	.notifier_call = otestmgr_notify,
+};
+
+static int __init testmgr_cipher_init(void)
+{
+	return ocrypto_register_notifier(&otestmgr_notifier);
+}
+
+static void __exit testmgr_cipher_exit(void)
+{
+	int err;
+
+	err = ocrypto_unregister_notifier(&otestmgr_notifier);
+	BUG_ON(err);
+}
+
+subsys_initcall(testmgr_cipher_init);
+module_exit(testmgr_cipher_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Algorithm Test Manager for Ciphers");
diff --git a/crypto/testmgr_cipher.h b/crypto/testmgr_cipher.h
new file mode 100644
index 0000000..2226e04
--- /dev/null
+++ b/crypto/testmgr_cipher.h
@@ -0,0 +1,22 @@
+/*
+ * 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>
+ *
+ * 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.
+ *
+ */
+#ifndef _CRYPTO_TESTMGR_CIPHER_H
+#define _CRYPTO_TESTMGR_CIPHER_H
+
+#include <linux/notifier.h>
+
+extern struct notifier_block otestmgr_notifier;
+
+#endif	/* _CRYPTO_TESTMGR_CIPHER_H */