summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-10-10 11:20:42 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2008-10-10 11:20:42 -0700
commitf6bccf695431da0e9bd773550ae91b8cb9ffb227 (patch)
treeff81689bd9bf849b4827da6fbd69fce471842057 /crypto
parent3af73d392c9c414ca527bab9c5d4c2a97698acbd (diff)
parenta0f000ec9b61b99111757df138b11144236fc59b (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6: crypto: skcipher - Use RNG interface instead of get_random_bytes crypto: rng - RNG interface and implementation crypto: api - Add fips_enable flag crypto: skcipher - Move IV generators into their own modules crypto: cryptomgr - Test ciphers using ECB crypto: api - Use test infrastructure crypto: cryptomgr - Add test infrastructure crypto: tcrypt - Add alg_test interface crypto: tcrypt - Abort and only log if there is an error crypto: crc32c - Use Intel CRC32 instruction crypto: tcrypt - Avoid using contiguous pages crypto: api - Display larval objects properly crypto: api - Export crypto_alg_lookup instead of __crypto_alg_lookup crypto: Kconfig - Replace leading spaces with tabs
Diffstat (limited to 'crypto')
-rw-r--r--crypto/Kconfig127
-rw-r--r--crypto/Makefile12
-rw-r--r--crypto/algapi.c147
-rw-r--r--crypto/algboss.c (renamed from crypto/cryptomgr.c)92
-rw-r--r--crypto/ansi_cprng.c417
-rw-r--r--crypto/api.c81
-rw-r--r--crypto/blkcipher.c29
-rw-r--r--crypto/chainiv.c44
-rw-r--r--crypto/eseqiv.c35
-rw-r--r--crypto/fips.c27
-rw-r--r--crypto/internal.h18
-rw-r--r--crypto/krng.c66
-rw-r--r--crypto/proc.c57
-rw-r--r--crypto/rng.c126
-rw-r--r--crypto/seqiv.c27
-rw-r--r--crypto/tcrypt.c1347
-rw-r--r--crypto/tcrypt.h8709
-rw-r--r--crypto/testmgr.c1868
-rw-r--r--crypto/testmgr.h8738
19 files changed, 11914 insertions, 10053 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig
index d83185915eee..39dbd8e4dde1 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -21,6 +21,14 @@ if CRYPTO
comment "Crypto core or helper"
+config CRYPTO_FIPS
+ bool "FIPS 200 compliance"
+ help
+ This options enables the fips boot option which is
+ required if you want to system to operate in a FIPS 200
+ certification. You should say no unless you know what
+ this is.
+
config CRYPTO_ALGAPI
tristate
help
@@ -33,14 +41,21 @@ config CRYPTO_AEAD
config CRYPTO_BLKCIPHER
tristate
select CRYPTO_ALGAPI
+ select CRYPTO_RNG
config CRYPTO_HASH
tristate
select CRYPTO_ALGAPI
+config CRYPTO_RNG
+ tristate
+ select CRYPTO_ALGAPI
+
config CRYPTO_MANAGER
tristate "Cryptographic algorithm manager"
- select CRYPTO_ALGAPI
+ select CRYPTO_AEAD
+ select CRYPTO_HASH
+ select CRYPTO_BLKCIPHER
help
Create default cryptographic template instantiations such as
cbc(aes).
@@ -85,9 +100,7 @@ config CRYPTO_AUTHENC
config CRYPTO_TEST
tristate "Testing module"
depends on m
- select CRYPTO_ALGAPI
- select CRYPTO_AEAD
- select CRYPTO_BLKCIPHER
+ select CRYPTO_MANAGER
help
Quick & dirty crypto test module.
@@ -113,6 +126,7 @@ config CRYPTO_SEQIV
tristate "Sequence Number IV Generator"
select CRYPTO_AEAD
select CRYPTO_BLKCIPHER
+ select CRYPTO_RNG
help
This IV generator generates an IV based on a sequence number by
xoring it with a salt. This algorithm is mainly useful for CTR
@@ -219,7 +233,19 @@ config CRYPTO_CRC32C
Castagnoli, et al Cyclic Redundancy-Check Algorithm. Used
by iSCSI for header and data digests and by others.
See Castagnoli93. This implementation uses lib/libcrc32c.
- Module will be crc32c.
+ Module will be crc32c.
+
+config CRYPTO_CRC32C_INTEL
+ tristate "CRC32c INTEL hardware acceleration"
+ depends on X86
+ select CRYPTO_HASH
+ help
+ In Intel processor with SSE4.2 supported, the processor will
+ support CRC32C implementation using hardware accelerated CRC32
+ instruction. This option will create 'crc32c-intel' module,
+ which will enable any routine to use the CRC32 instruction to
+ gain performance compared with software implementation.
+ Module will be crc32c-intel.
config CRYPTO_MD4
tristate "MD4 digest algorithm"
@@ -243,55 +269,58 @@ config CRYPTO_MICHAEL_MIC
of the algorithm.
config CRYPTO_RMD128
- tristate "RIPEMD-128 digest algorithm"
- select CRYPTO_ALGAPI
- help
- RIPEMD-128 (ISO/IEC 10118-3:2004).
+ tristate "RIPEMD-128 digest algorithm"
+ select CRYPTO_ALGAPI
+ help
+ RIPEMD-128 (ISO/IEC 10118-3:2004).
- RIPEMD-128 is a 128-bit cryptographic hash function. It should only
- to be used as a secure replacement for RIPEMD. For other use cases
- RIPEMD-160 should be used.
+ RIPEMD-128 is a 128-bit cryptographic hash function. It should only
+ to be used as a secure replacement for RIPEMD. For other use cases
+ RIPEMD-160 should be used.
- Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
- See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+ Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+ See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
config CRYPTO_RMD160
- tristate "RIPEMD-160 digest algorithm"
- select CRYPTO_ALGAPI
- help
- RIPEMD-160 (ISO/IEC 10118-3:2004).
+ tristate "RIPEMD-160 digest algorithm"
+ select CRYPTO_ALGAPI
+ help
+ RIPEMD-160 (ISO/IEC 10118-3:2004).
- RIPEMD-160 is a 160-bit cryptographic hash function. It is intended
- to be used as a secure replacement for the 128-bit hash functions
- MD4, MD5 and it's predecessor RIPEMD (not to be confused with RIPEMD-128).
+ RIPEMD-160 is a 160-bit cryptographic hash function. It is intended
+ to be used as a secure replacement for the 128-bit hash functions
+ MD4, MD5 and it's predecessor RIPEMD
+ (not to be confused with RIPEMD-128).
- It's speed is comparable to SHA1 and there are no known attacks against
- RIPEMD-160.
+ It's speed is comparable to SHA1 and there are no known attacks
+ against RIPEMD-160.
- Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
- See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+ Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+ See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
config CRYPTO_RMD256
- tristate "RIPEMD-256 digest algorithm"
- select CRYPTO_ALGAPI
- help
- RIPEMD-256 is an optional extension of RIPEMD-128 with a 256 bit hash.
- It is intended for applications that require longer hash-results, without
- needing a larger security level (than RIPEMD-128).
+ tristate "RIPEMD-256 digest algorithm"
+ select CRYPTO_ALGAPI
+ help
+ RIPEMD-256 is an optional extension of RIPEMD-128 with a
+ 256 bit hash. It is intended for applications that require
+ longer hash-results, without needing a larger security level
+ (than RIPEMD-128).
- Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
- See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+ Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+ See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
config CRYPTO_RMD320
- tristate "RIPEMD-320 digest algorithm"
- select CRYPTO_ALGAPI
- help
- RIPEMD-320 is an optional extension of RIPEMD-160 with a 320 bit hash.
- It is intended for applications that require longer hash-results, without
- needing a larger security level (than RIPEMD-160).
+ tristate "RIPEMD-320 digest algorithm"
+ select CRYPTO_ALGAPI
+ help
+ RIPEMD-320 is an optional extension of RIPEMD-160 with a
+ 320 bit hash. It is intended for applications that require
+ longer hash-results, without needing a larger security level
+ (than RIPEMD-160).
- Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
- See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+ Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+ See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
config CRYPTO_SHA1
tristate "SHA1 digest algorithm"
@@ -308,8 +337,8 @@ config CRYPTO_SHA256
This version of SHA implements a 256 bit hash with 128 bits of
security against collision attacks.
- This code also includes SHA-224, a 224 bit hash with 112 bits
- of security against collision attacks.
+ This code also includes SHA-224, a 224 bit hash with 112 bits
+ of security against collision attacks.
config CRYPTO_SHA512
tristate "SHA384 and SHA512 digest algorithms"
@@ -666,6 +695,18 @@ config CRYPTO_LZO
help
This is the LZO algorithm.
+comment "Random Number Generation"
+
+config CRYPTO_ANSI_CPRNG
+ tristate "Pseudo Random Number Generation for Cryptographic modules"
+ select CRYPTO_AES
+ select CRYPTO_RNG
+ select CRYPTO_FIPS
+ help
+ This option enables the generic pseudo random number generator
+ for cryptographic modules. Uses the Algorithm specified in
+ ANSI X9.31 A.2.4
+
source "drivers/crypto/Kconfig"
endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index d4f3ed857df0..5862b807334e 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -5,6 +5,8 @@
obj-$(CONFIG_CRYPTO) += crypto.o
crypto-objs := api.o cipher.o digest.o compress.o
+obj-$(CONFIG_CRYPTO_FIPS) += fips.o
+
crypto_algapi-$(CONFIG_PROC_FS) += proc.o
crypto_algapi-objs := algapi.o scatterwalk.o $(crypto_algapi-y)
obj-$(CONFIG_CRYPTO_ALGAPI) += crypto_algapi.o
@@ -13,15 +15,17 @@ obj-$(CONFIG_CRYPTO_AEAD) += aead.o
crypto_blkcipher-objs := ablkcipher.o
crypto_blkcipher-objs += blkcipher.o
-crypto_blkcipher-objs += chainiv.o
-crypto_blkcipher-objs += eseqiv.o
obj-$(CONFIG_CRYPTO_BLKCIPHER) += crypto_blkcipher.o
+obj-$(CONFIG_CRYPTO_BLKCIPHER) += chainiv.o
+obj-$(CONFIG_CRYPTO_BLKCIPHER) += eseqiv.o
obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
crypto_hash-objs := hash.o
crypto_hash-objs += ahash.o
obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o
+cryptomgr-objs := algboss.o testmgr.o
+
obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
@@ -69,7 +73,9 @@ obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o
obj-$(CONFIG_CRYPTO_LZO) += lzo.o
-
+obj-$(CONFIG_CRYPTO_RNG) += rng.o
+obj-$(CONFIG_CRYPTO_RNG) += krng.o
+obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
#
diff --git a/crypto/algapi.c b/crypto/algapi.c
index e65cb50cf4af..7c41e7405c41 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -21,15 +21,15 @@
#include "internal.h"
+static void crypto_remove_final(struct list_head *list);
+
static LIST_HEAD(crypto_template_list);
void crypto_larval_error(const char *name, u32 type, u32 mask)
{
struct crypto_alg *alg;
- down_read(&crypto_alg_sem);
- alg = __crypto_alg_lookup(name, type, mask);
- up_read(&crypto_alg_sem);
+ alg = crypto_alg_lookup(name, type, mask);
if (alg) {
if (crypto_is_larval(alg)) {
@@ -128,23 +128,97 @@ static void crypto_remove_spawns(struct list_head *spawns,
}
}
-static int __crypto_register_alg(struct crypto_alg *alg,
- struct list_head *list)
+static struct crypto_larval *__crypto_register_alg(struct crypto_alg *alg)
{
struct crypto_alg *q;
+ struct crypto_larval *larval;
int ret = -EAGAIN;
if (crypto_is_dead(alg))
- goto out;
+ goto err;
INIT_LIST_HEAD(&alg->cra_users);
+ /* No cheating! */
+ alg->cra_flags &= ~CRYPTO_ALG_TESTED;
+
ret = -EEXIST;
atomic_set(&alg->cra_refcnt, 1);
list_for_each_entry(q, &crypto_alg_list, cra_list) {
if (q == alg)
- goto out;
+ goto err;
+
+ if (crypto_is_larval(q)) {
+ if (!strcmp(alg->cra_driver_name, q->cra_driver_name))
+ goto err;
+ continue;
+ }
+
+ if (!strcmp(q->cra_driver_name, alg->cra_name) ||
+ !strcmp(q->cra_name, alg->cra_driver_name))
+ goto err;
+ }
+
+ larval = crypto_larval_alloc(alg->cra_name,
+ alg->cra_flags | CRYPTO_ALG_TESTED, 0);
+ if (IS_ERR(larval))
+ goto out;
+
+ ret = -ENOENT;
+ larval->adult = crypto_mod_get(alg);
+ if (!larval->adult)
+ goto free_larval;
+
+ atomic_set(&larval->alg.cra_refcnt, 1);
+ memcpy(larval->alg.cra_driver_name, alg->cra_driver_name,
+ CRYPTO_MAX_ALG_NAME);
+ larval->alg.cra_priority = alg->cra_priority;
+
+ list_add(&alg->cra_list, &crypto_alg_list);
+ list_add(&larval->alg.cra_list, &crypto_alg_list);
+
+out:
+ return larval;
+
+free_larval:
+ kfree(larval);
+err:
+ larval = ERR_PTR(ret);
+ goto out;
+}
+
+void crypto_alg_tested(const char *name, int err)
+{
+ struct crypto_larval *test;
+ struct crypto_alg *alg;
+ struct crypto_alg *q;
+ LIST_HEAD(list);
+
+ down_write(&crypto_alg_sem);
+ list_for_each_entry(q, &crypto_alg_list, cra_list) {
+ if (!crypto_is_larval(q))
+ continue;
+
+ test = (struct crypto_larval *)q;
+
+ if (!strcmp(q->cra_driver_name, name))
+ goto found;
+ }
+
+ printk(KERN_ERR "alg: Unexpected test result for %s: %d\n", name, err);
+ goto unlock;
+
+found:
+ alg = test->adult;
+ if (err || list_empty(&alg->cra_list))
+ goto complete;
+
+ alg->cra_flags |= CRYPTO_ALG_TESTED;
+
+ list_for_each_entry(q, &crypto_alg_list, cra_list) {
+ if (q == alg)
+ continue;
if (crypto_is_moribund(q))
continue;
@@ -180,17 +254,18 @@ static int __crypto_register_alg(struct crypto_alg *alg,
q->cra_priority > alg->cra_priority)
continue;
- crypto_remove_spawns(&q->cra_users, list, alg->cra_flags);
+ crypto_remove_spawns(&q->cra_users, &list, alg->cra_flags);
}
-
- list_add(&alg->cra_list, &crypto_alg_list);
- crypto_notify(CRYPTO_MSG_ALG_REGISTER, alg);
- ret = 0;
+complete:
+ complete_all(&test->completion);
-out:
- return ret;
+unlock:
+ up_write(&crypto_alg_sem);
+
+ crypto_remove_final(&list);
}
+EXPORT_SYMBOL_GPL(crypto_alg_tested);
static void crypto_remove_final(struct list_head *list)
{
@@ -203,9 +278,27 @@ static void crypto_remove_final(struct list_head *list)
}
}
+static void crypto_wait_for_test(struct crypto_larval *larval)
+{
+ int err;
+
+ err = crypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, larval->adult);
+ if (err != NOTIFY_STOP) {
+ if (WARN_ON(err != NOTIFY_DONE))
+ goto out;
+ crypto_alg_tested(larval->alg.cra_driver_name, 0);
+ }
+
+ err = wait_for_completion_interruptible(&larval->completion);
+ WARN_ON(err);
+
+out:
+ crypto_larval_kill(&larval->alg);
+}
+
int crypto_register_alg(struct crypto_alg *alg)
{
- LIST_HEAD(list);
+ struct crypto_larval *larval;
int err;
err = crypto_check_alg(alg);
@@ -213,11 +306,14 @@ int crypto_register_alg(struct crypto_alg *alg)
return err;
down_write(&crypto_alg_sem);
- err = __crypto_register_alg(alg, &list);
+ larval = __crypto_register_alg(alg);
up_write(&crypto_alg_sem);
- crypto_remove_final(&list);
- return err;
+ if (IS_ERR(larval))
+ return PTR_ERR(larval);
+
+ crypto_wait_for_test(larval);
+ return 0;
}
EXPORT_SYMBOL_GPL(crypto_register_alg);
@@ -335,8 +431,8 @@ EXPORT_SYMBOL_GPL(crypto_lookup_template);
int crypto_register_instance(struct crypto_template *tmpl,
struct crypto_instance *inst)
{
- LIST_HEAD(list);
- int err = -EINVAL;
+ struct crypto_larval *larval;
+ int err;
err = crypto_check_alg(&inst->alg);
if (err)
@@ -346,8 +442,8 @@ int crypto_register_instance(struct crypto_template *tmpl,
down_write(&crypto_alg_sem);
- err = __crypto_register_alg(&inst->alg, &list);
- if (err)
+ larval = __crypto_register_alg(&inst->alg);
+ if (IS_ERR(larval))
goto unlock;
hlist_add_head(&inst->list, &tmpl->instances);
@@ -356,7 +452,12 @@ int crypto_register_instance(struct crypto_template *tmpl,
unlock:
up_write(&crypto_alg_sem);
- crypto_remove_final(&list);
+ err = PTR_ERR(larval);
+ if (IS_ERR(larval))
+ goto err;
+
+ crypto_wait_for_test(larval);
+ err = 0;
err:
return err;
diff --git a/crypto/cryptomgr.c b/crypto/algboss.c
index e5e3cf848d42..4601e4267c88 100644
--- a/crypto/cryptomgr.c
+++ b/crypto/algboss.c
@@ -45,6 +45,15 @@ struct cryptomgr_param {
char larval[CRYPTO_MAX_ALG_NAME];
char template[CRYPTO_MAX_ALG_NAME];
+
+ u32 otype;
+ u32 omask;
+};
+
+struct crypto_test_param {
+ char driver[CRYPTO_MAX_ALG_NAME];
+ char alg[CRYPTO_MAX_ALG_NAME];
+ u32 type;
};
static int cryptomgr_probe(void *data)
@@ -76,8 +85,7 @@ out:
module_put_and_exit(0);
err:
- crypto_larval_error(param->larval, param->type.data.type,
- param->type.data.mask);
+ crypto_larval_error(param->larval, param->otype, param->omask);
goto out;
}
@@ -169,13 +177,65 @@ static int cryptomgr_schedule_probe(struct crypto_larval *larval)
param->type.attr.rta_len = sizeof(param->type);
param->type.attr.rta_type = CRYPTOA_TYPE;
- param->type.data.type = larval->alg.cra_flags;
- param->type.data.mask = larval->mask;
+ param->type.data.type = larval->alg.cra_flags & ~CRYPTO_ALG_TESTED;
+ param->type.data.mask = larval->mask & ~CRYPTO_ALG_TESTED;
param->tb[0] = &param->type.attr;
+ param->otype = larval->alg.cra_flags;
+ param->omask = larval->mask;
+
memcpy(param->larval, larval->alg.cra_name, CRYPTO_MAX_ALG_NAME);
- thread = kthread_run(cryptomgr_probe, param, "cryptomgr");
+ thread = kthread_run(cryptomgr_probe, param, "cryptomgr_probe");
+ 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 cryptomgr_test(void *data)
+{
+ struct crypto_test_param *param = data;
+ u32 type = param->type;
+ int err = 0;
+
+ if (!((type ^ CRYPTO_ALG_TYPE_BLKCIPHER) &
+ CRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & CRYPTO_ALG_GENIV))
+ goto skiptest;
+
+ err = alg_test(param->driver, param->alg, type, CRYPTO_ALG_TESTED);
+
+skiptest:
+ crypto_alg_tested(param->driver, err);
+
+ kfree(param);
+ module_put_and_exit(0);
+}
+
+static int cryptomgr_schedule_test(struct crypto_alg *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, alg->cra_name, sizeof(param->alg));
+ param->type = alg->cra_flags;
+
+ thread = kthread_run(cryptomgr_test, param, "cryptomgr_test");
if (IS_ERR(thread))
goto err_free_param;
@@ -195,6 +255,8 @@ static int cryptomgr_notify(struct notifier_block *this, unsigned long msg,
switch (msg) {
case CRYPTO_MSG_ALG_REQUEST:
return cryptomgr_schedule_probe(data);
+ case CRYPTO_MSG_ALG_REGISTER:
+ return cryptomgr_schedule_test(data);
}
return NOTIFY_DONE;
@@ -206,16 +268,32 @@ static struct notifier_block cryptomgr_notifier = {
static int __init cryptomgr_init(void)
{
- return crypto_register_notifier(&cryptomgr_notifier);
+ int err;
+
+ err = testmgr_init();
+ if (err)
+ return err;
+
+ err = crypto_register_notifier(&cryptomgr_notifier);
+ if (err)
+ goto free_testmgr;
+
+ return 0;
+
+free_testmgr:
+ testmgr_exit();
+ return err;
}
static void __exit cryptomgr_exit(void)
{
int err = crypto_unregister_notifier(&cryptomgr_notifier);
BUG_ON(err);
+
+ testmgr_exit();
}
-module_init(cryptomgr_init);
+subsys_initcall(cryptomgr_init);
module_exit(cryptomgr_exit);
MODULE_LICENSE("GPL");
diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
new file mode 100644
index 000000000000..72db0fd763cc
--- /dev/null
+++ b/crypto/ansi_cprng.c
@@ -0,0 +1,417 @@
+/*
+ * PRNG: Pseudo Random Number Generator
+ * Based on NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4 using
+ * AES 128 cipher
+ *
+ * (C) Neil Horman <nhorman@tuxdriver.com>
+ *
+ * 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
+ * any later version.
+ *
+ *
+ */
+
+#include <crypto/internal/rng.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+
+#include "internal.h"
+
+#define DEFAULT_PRNG_KEY "0123456789abcdef"
+#define DEFAULT_PRNG_KSZ 16
+#define DEFAULT_BLK_SZ 16
+#define DEFAULT_V_SEED "zaybxcwdveuftgsh"
+
+/*
+ * Flags for the prng_context flags field
+ */
+
+#define PRNG_FIXED_SIZE 0x1
+#define PRNG_NEED_RESET 0x2
+
+/*
+ * Note: DT is our counter value
+ * I is our intermediate value
+ * V is our seed vector
+ * See http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
+ * for implementation details
+ */
+
+
+struct prng_context {
+ spinlock_t prng_lock;
+ unsigned char rand_data[DEFAULT_BLK_SZ];
+ unsigned char last_rand_data[DEFAULT_BLK_SZ];
+ unsigned char DT[DEFAULT_BLK_SZ];
+ unsigned char I[DEFAULT_BLK_SZ];
+ unsigned char V[DEFAULT_BLK_SZ];
+ u32 rand_data_valid;
+ struct crypto_cipher *tfm;
+ u32 flags;
+};
+
+static int dbg;
+
+static void hexdump(char *note, unsigned char *buf, unsigned int len)
+{
+ if (dbg) {
+ printk(KERN_CRIT "%s", note);
+ print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,
+ 16, 1,
+ buf, len, false);
+ }
+}
+
+#define dbgprint(format, args...) do {\
+if (dbg)\
+ printk(format, ##args);\
+} while (0)
+
+static void xor_vectors(unsigned char *in1, unsigned char *in2,
+ unsigned char *out, unsigned int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ out[i] = in1[i] ^ in2[i];
+
+}
+/*
+ * Returns DEFAULT_BLK_SZ bytes of random data per call
+ * returns 0 if generation succeded, <0 if something went wrong
+ */
+static int _get_more_prng_bytes(struct prng_context *ctx)
+{
+ int i;
+ unsigned char tmp[DEFAULT_BLK_SZ];
+ unsigned char *output = NULL;
+
+
+ dbgprint(KERN_CRIT "Calling _get_more_prng_bytes for context %p\n",
+ ctx);
+
+ hexdump("Input DT: ", ctx->DT, DEFAULT_BLK_SZ);
+ hexdump("Input I: ", ctx->I, DEFAULT_BLK_SZ);
+ hexdump("Input V: ", ctx->V, DEFAULT_BLK_SZ);
+
+ /*
+ * This algorithm is a 3 stage state machine
+ */
+ for (i = 0; i < 3; i++) {
+
+ switch (i) {
+ case 0:
+ /*
+ * Start by encrypting the counter value
+ * This gives us an intermediate value I
+ */
+ memcpy(tmp, ctx->DT, DEFAULT_BLK_SZ);
+ output = ctx->I;
+ hexdump("tmp stage 0: ", tmp, DEFAULT_BLK_SZ);
+ break;
+ case 1:
+
+ /*
+ * Next xor I with our secret vector V
+ * encrypt that result to obtain our
+ * pseudo random data which we output
+ */
+ xor_vectors(ctx->I, ctx->V, tmp, DEFAULT_BLK_SZ);
+ hexdump("tmp stage 1: ", tmp, DEFAULT_BLK_SZ);
+ output = ctx->rand_data;
+ break;
+ case 2:
+ /*
+ * First check that we didn't produce the same
+ * random data that we did last time around through this
+ */
+ if (!memcmp(ctx->rand_data, ctx->last_rand_data,
+ DEFAULT_BLK_SZ)) {
+ printk(KERN_ERR
+ "ctx %p Failed repetition check!\n",
+ ctx);
+ ctx->flags |= PRNG_NEED_RESET;
+ return -EINVAL;
+ }
+ memcpy(ctx->last_rand_data, ctx->rand_data,
+ DEFAULT_BLK_SZ);
+
+ /*
+ * Lastly xor the random data with I
+ * and encrypt that to obtain a new secret vector V
+ */
+ xor_vectors(ctx->rand_data, ctx->I, tmp,
+ DEFAULT_BLK_SZ);
+ output = ctx->V;
+ hexdump("tmp stage 2: ", tmp, DEFAULT_BLK_SZ);
+ break;
+ }
+
+
+ /* do the encryption */
+ crypto_cipher_encrypt_one(ctx->tfm, output, tmp);
+
+ }
+
+ /*
+ * Now update our DT value
+ */
+ for (i = 0; i < DEFAULT_BLK_SZ; i++) {
+ ctx->DT[i] += 1;
+ if (ctx->DT[i] != 0)
+ break;
+ }
+
+ dbgprint("Returning new block for context %p\n", ctx);
+ ctx->rand_data_valid = 0;
+
+ hexdump("Output DT: ", ctx->DT, DEFAULT_BLK_SZ);
+ hexdump("Output I: ", ctx->I, DEFAULT_BLK_SZ);
+ hexdump("Output V: ", ctx->V, DEFAULT_BLK_SZ);
+ hexdump("New Random Data: ", ctx->rand_data, DEFAULT_BLK_SZ);
+
+ return 0;
+}
+
+/* Our exported functions */
+static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx)
+{
+ unsigned long flags;
+ unsigned char *ptr = buf;
+ unsigned int byte_count = (unsigned int)nbytes;
+ int err;
+
+
+ if (nbytes < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ctx->prng_lock, flags);
+
+ err = -EINVAL;
+ if (ctx->flags & PRNG_NEED_RESET)
+ goto done;
+
+ /*
+ * If the FIXED_SIZE flag is on, only return whole blocks of
+ * pseudo random data
+ */
+ err = -EINVAL;
+ if (ctx->flags & PRNG_FIXED_SIZE) {
+ if (nbytes < DEFAULT_BLK_SZ)
+ goto done;
+ byte_count = DEFAULT_BLK_SZ;
+ }
+
+ err = byte_count;
+
+ dbgprint(KERN_CRIT "getting %d random bytes for context %p\n",
+ byte_count, ctx);
+
+
+remainder:
+ if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
+ if (_get_more_prng_bytes(ctx) < 0) {
+ memset(buf, 0, nbytes);
+ err = -EINVAL;
+ goto done;
+ }
+ }
+
+ /*
+ * Copy up to the next whole block size
+ */
+ if (byte_count < DEFAULT_BLK_SZ) {
+ for (; ctx->rand_data_valid < DEFAULT_BLK_SZ;
+ ctx->rand_data_valid++) {
+ *ptr = ctx->rand_data[ctx->rand_data_valid];
+ ptr++;
+ byte_count--;
+ if (byte_count == 0)
+ goto done;
+ }
+ }
+
+ /*
+ * Now copy whole blocks
+ */
+ for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) {
+ if (_get_more_prng_bytes(ctx) < 0) {
+ memset(buf, 0, nbytes);
+ err = -EINVAL;
+ goto done;
+ }
+ memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
+ ctx->rand_data_valid += DEFAULT_BLK_SZ;
+ ptr += DEFAULT_BLK_SZ;
+ }
+
+ /*
+ * Now copy any extra partial data
+ */
+ if (byte_count)
+ goto remainder;
+
+done:
+ spin_unlock_irqrestore(&ctx->prng_lock, flags);
+ dbgprint(KERN_CRIT "returning %d from get_prng_bytes in context %p\n",
+ err, ctx);
+ return err;
+}
+
+static void free_prng_context(struct prng_context *ctx)
+{
+ crypto_free_cipher(ctx->tfm);
+}
+
+static int reset_prng_context(struct prng_context *ctx,
+ unsigned char *key, size_t klen,
+ unsigned char *V, unsigned char *DT)
+{
+ int ret;
+ int rc = -EINVAL;
+ unsigned char *prng_key;
+
+ spin_lock(&ctx->prng_lock);
+ ctx->flags |= PRNG_NEED_RESET;
+
+ prng_key = (key != NULL) ? key : (unsigned char *)DEFAULT_PRNG_KEY;
+
+ if (!key)
+ klen = DEFAULT_PRNG_KSZ;
+
+ if (V)
+ memcpy(ctx->V, V, DEFAULT_BLK_SZ);
+ else
+ memcpy(ctx->V, DEFAULT_V_SEED, DEFAULT_BLK_SZ);
+
+ if (DT)
+ memcpy(ctx->DT, DT, DEFAULT_BLK_SZ);
+ else
+ memset(ctx->DT, 0, DEFAULT_BLK_SZ);
+
+ memset(ctx->rand_data, 0, DEFAULT_BLK_SZ);
+ memset(ctx->last_rand_data, 0, DEFAULT_BLK_SZ);
+
+ if (ctx->tfm)
+ crypto_free_cipher(ctx->tfm);
+
+ ctx->tfm = crypto_alloc_cipher("aes", 0, 0);
+ if (IS_ERR(ctx->tfm)) {
+ dbgprint(KERN_CRIT "Failed to alloc tfm for context %p\n",
+ ctx);
+ ctx->tfm = NULL;
+ goto out;
+ }
+
+ ctx->rand_data_valid = DEFAULT_BLK_SZ;
+
+ ret = crypto_cipher_setkey(ctx->tfm, prng_key, klen);
+ if (ret) {
+ dbgprint(KERN_CRIT "PRNG: setkey() failed flags=%x\n",
+ crypto_cipher_get_flags(ctx->tfm));
+ crypto_free_cipher(ctx->tfm);
+ goto out;
+ }
+
+ rc = 0;
+ ctx->flags &= ~PRNG_NEED_RESET;
+out:
+ spin_unlock(&ctx->prng_lock);
+
+ return rc;
+
+}
+
+static int cprng_init(struct crypto_tfm *tfm)
+{
+ struct prng_context *ctx = crypto_tfm_ctx(tfm);
+
+ spin_lock_init(&ctx->prng_lock);
+
+ return reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL);
+}
+
+static void cprng_exit(struct crypto_tfm *tfm)
+{
+ free_prng_context(crypto_tfm_ctx(tfm));
+}
+
+static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
+ unsigned int dlen)
+{
+ struct prng_context *prng = crypto_rng_ctx(tfm);
+
+ return get_prng_bytes(rdata, dlen, prng);
+}
+
+static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
+{
+ struct prng_context *prng = crypto_rng_ctx(tfm);
+ u8 *key = seed + DEFAULT_PRNG_KSZ;
+
+ if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ)
+ return -EINVAL;
+
+ reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, NULL);
+
+ if (prng->flags & PRNG_NEED_RESET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct crypto_alg rng_alg = {
+ .cra_name = "stdrng",
+ .cra_driver_name = "ansi_cprng",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_TYPE_RNG,
+ .cra_ctxsize = sizeof(struct prng_context),
+ .cra_type = &crypto_rng_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(rng_alg.cra_list),
+ .cra_init = cprng_init,
+ .cra_exit = cprng_exit,
+ .cra_u = {
+ .rng = {
+ .rng_make_random = cprng_get_random,
+ .rng_reset = cprng_reset,
+