diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-10 14:20:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-10 14:20:42 -0400 |
commit | f6bccf695431da0e9bd773550ae91b8cb9ffb227 (patch) | |
tree | ff81689bd9bf849b4827da6fbd69fce471842057 /crypto/ansi_cprng.c | |
parent | 3af73d392c9c414ca527bab9c5d4c2a97698acbd (diff) | |
parent | a0f000ec9b61b99111757df138b11144236fc59b (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/ansi_cprng.c')
-rw-r--r-- | crypto/ansi_cprng.c | 417 |
1 files changed, 417 insertions, 0 deletions
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 @@ | |||
1 | /* | ||
2 | * PRNG: Pseudo Random Number Generator | ||
3 | * Based on NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4 using | ||
4 | * AES 128 cipher | ||
5 | * | ||
6 | * (C) Neil Horman <nhorman@tuxdriver.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * any later version. | ||
12 | * | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <crypto/internal/rng.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/string.h> | ||
22 | |||
23 | #include "internal.h" | ||
24 | |||
25 | #define DEFAULT_PRNG_KEY "0123456789abcdef" | ||
26 | #define DEFAULT_PRNG_KSZ 16 | ||
27 | #define DEFAULT_BLK_SZ 16 | ||
28 | #define DEFAULT_V_SEED "zaybxcwdveuftgsh" | ||
29 | |||
30 | /* | ||
31 | * Flags for the prng_context flags field | ||
32 | */ | ||
33 | |||
34 | #define PRNG_FIXED_SIZE 0x1 | ||
35 | #define PRNG_NEED_RESET 0x2 | ||
36 | |||
37 | /* | ||
38 | * Note: DT is our counter value | ||
39 | * I is our intermediate value | ||
40 | * V is our seed vector | ||
41 | * See http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf | ||
42 | * for implementation details | ||
43 | */ | ||
44 | |||
45 | |||
46 | struct prng_context { | ||
47 | spinlock_t prng_lock; | ||
48 | unsigned char rand_data[DEFAULT_BLK_SZ]; | ||
49 | unsigned char last_rand_data[DEFAULT_BLK_SZ]; | ||
50 | unsigned char DT[DEFAULT_BLK_SZ]; | ||
51 | unsigned char I[DEFAULT_BLK_SZ]; | ||
52 | unsigned char V[DEFAULT_BLK_SZ]; | ||
53 | u32 rand_data_valid; | ||
54 | struct crypto_cipher *tfm; | ||
55 | u32 flags; | ||
56 | }; | ||
57 | |||
58 | static int dbg; | ||
59 | |||
60 | static void hexdump(char *note, unsigned char *buf, unsigned int len) | ||
61 | { | ||
62 | if (dbg) { | ||
63 | printk(KERN_CRIT "%s", note); | ||
64 | print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, | ||
65 | 16, 1, | ||
66 | buf, len, false); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | #define dbgprint(format, args...) do {\ | ||
71 | if (dbg)\ | ||
72 | printk(format, ##args);\ | ||
73 | } while (0) | ||
74 | |||
75 | static void xor_vectors(unsigned char *in1, unsigned char *in2, | ||
76 | unsigned char *out, unsigned int size) | ||
77 | { | ||
78 | int i; | ||
79 | |||
80 | for (i = 0; i < size; i++) | ||
81 | out[i] = in1[i] ^ in2[i]; | ||
82 | |||
83 | } | ||
84 | /* | ||
85 | * Returns DEFAULT_BLK_SZ bytes of random data per call | ||
86 | * returns 0 if generation succeded, <0 if something went wrong | ||
87 | */ | ||
88 | static int _get_more_prng_bytes(struct prng_context *ctx) | ||
89 | { | ||
90 | int i; | ||
91 | unsigned char tmp[DEFAULT_BLK_SZ]; | ||
92 | unsigned char *output = NULL; | ||
93 | |||
94 | |||
95 | dbgprint(KERN_CRIT "Calling _get_more_prng_bytes for context %p\n", | ||
96 | ctx); | ||
97 | |||
98 | hexdump("Input DT: ", ctx->DT, DEFAULT_BLK_SZ); | ||
99 | hexdump("Input I: ", ctx->I, DEFAULT_BLK_SZ); | ||
100 | hexdump("Input V: ", ctx->V, DEFAULT_BLK_SZ); | ||
101 | |||
102 | /* | ||
103 | * This algorithm is a 3 stage state machine | ||
104 | */ | ||
105 | for (i = 0; i < 3; i++) { | ||
106 | |||
107 | switch (i) { | ||
108 | case 0: | ||
109 | /* | ||
110 | * Start by encrypting the counter value | ||
111 | * This gives us an intermediate value I | ||
112 | */ | ||
113 | memcpy(tmp, ctx->DT, DEFAULT_BLK_SZ); | ||
114 | output = ctx->I; | ||
115 | hexdump("tmp stage 0: ", tmp, DEFAULT_BLK_SZ); | ||
116 | break; | ||
117 | case 1: | ||
118 | |||
119 | /* | ||
120 | * Next xor I with our secret vector V | ||
121 | * encrypt that result to obtain our | ||
122 | * pseudo random data which we output | ||
123 | */ | ||
124 | xor_vectors(ctx->I, ctx->V, tmp, DEFAULT_BLK_SZ); | ||
125 | hexdump("tmp stage 1: ", tmp, DEFAULT_BLK_SZ); | ||
126 | output = ctx->rand_data; | ||
127 | break; | ||
128 | case 2: | ||
129 | /* | ||
130 | * First check that we didn't produce the same | ||
131 | * random data that we did last time around through this | ||
132 | */ | ||
133 | if (!memcmp(ctx->rand_data, ctx->last_rand_data, | ||
134 | DEFAULT_BLK_SZ)) { | ||
135 | printk(KERN_ERR | ||
136 | "ctx %p Failed repetition check!\n", | ||
137 | ctx); | ||
138 | ctx->flags |= PRNG_NEED_RESET; | ||
139 | return -EINVAL; | ||
140 | } | ||
141 | memcpy(ctx->last_rand_data, ctx->rand_data, | ||
142 | DEFAULT_BLK_SZ); | ||
143 | |||
144 | /* | ||
145 | * Lastly xor the random data with I | ||
146 | * and encrypt that to obtain a new secret vector V | ||
147 | */ | ||
148 | xor_vectors(ctx->rand_data, ctx->I, tmp, | ||
149 | DEFAULT_BLK_SZ); | ||
150 | output = ctx->V; | ||
151 | hexdump("tmp stage 2: ", tmp, DEFAULT_BLK_SZ); | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | |||
156 | /* do the encryption */ | ||
157 | crypto_cipher_encrypt_one(ctx->tfm, output, tmp); | ||
158 | |||
159 | } | ||
160 | |||
161 | /* | ||
162 | * Now update our DT value | ||
163 | */ | ||
164 | for (i = 0; i < DEFAULT_BLK_SZ; i++) { | ||
165 | ctx->DT[i] += 1; | ||
166 | if (ctx->DT[i] != 0) | ||
167 | break; | ||
168 | } | ||
169 | |||
170 | dbgprint("Returning new block for context %p\n", ctx); | ||
171 | ctx->rand_data_valid = 0; | ||
172 | |||
173 | hexdump("Output DT: ", ctx->DT, DEFAULT_BLK_SZ); | ||
174 | hexdump("Output I: ", ctx->I, DEFAULT_BLK_SZ); | ||
175 | hexdump("Output V: ", ctx->V, DEFAULT_BLK_SZ); | ||
176 | hexdump("New Random Data: ", ctx->rand_data, DEFAULT_BLK_SZ); | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | /* Our exported functions */ | ||
182 | static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | unsigned char *ptr = buf; | ||
186 | unsigned int byte_count = (unsigned int)nbytes; | ||
187 | int err; | ||
188 | |||
189 | |||
190 | if (nbytes < 0) | ||
191 | return -EINVAL; | ||
192 | |||
193 | spin_lock_irqsave(&ctx->prng_lock, flags); | ||
194 | |||
195 | err = -EINVAL; | ||
196 | if (ctx->flags & PRNG_NEED_RESET) | ||
197 | goto done; | ||
198 | |||
199 | /* | ||
200 | * If the FIXED_SIZE flag is on, only return whole blocks of | ||
201 | * pseudo random data | ||
202 | */ | ||
203 | err = -EINVAL; | ||
204 | if (ctx->flags & PRNG_FIXED_SIZE) { | ||
205 | if (nbytes < DEFAULT_BLK_SZ) | ||
206 | goto done; | ||
207 | byte_count = DEFAULT_BLK_SZ; | ||
208 | } | ||
209 | |||
210 | err = byte_count; | ||
211 | |||
212 | dbgprint(KERN_CRIT "getting %d random bytes for context %p\n", | ||
213 | byte_count, ctx); | ||
214 | |||
215 | |||
216 | remainder: | ||
217 | if (ctx->rand_data_valid == DEFAULT_BLK_SZ) { | ||
218 | if (_get_more_prng_bytes(ctx) < 0) { | ||
219 | memset(buf, 0, nbytes); | ||
220 | err = -EINVAL; | ||
221 | goto done; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * Copy up to the next whole block size | ||
227 | */ | ||
228 | if (byte_count < DEFAULT_BLK_SZ) { | ||
229 | for (; ctx->rand_data_valid < DEFAULT_BLK_SZ; | ||
230 | ctx->rand_data_valid++) { | ||
231 | *ptr = ctx->rand_data[ctx->rand_data_valid]; | ||
232 | ptr++; | ||
233 | byte_count--; | ||
234 | if (byte_count == 0) | ||
235 | goto done; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Now copy whole blocks | ||
241 | */ | ||
242 | for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) { | ||
243 | if (_get_more_prng_bytes(ctx) < 0) { | ||
244 | memset(buf, 0, nbytes); | ||
245 | err = -EINVAL; | ||
246 | goto done; | ||
247 | } | ||
248 | memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ); | ||
249 | ctx->rand_data_valid += DEFAULT_BLK_SZ; | ||
250 | ptr += DEFAULT_BLK_SZ; | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * Now copy any extra partial data | ||
255 | */ | ||
256 | if (byte_count) | ||
257 | goto remainder; | ||
258 | |||
259 | done: | ||
260 | spin_unlock_irqrestore(&ctx->prng_lock, flags); | ||
261 | dbgprint(KERN_CRIT "returning %d from get_prng_bytes in context %p\n", | ||
262 | err, ctx); | ||
263 | return err; | ||
264 | } | ||
265 | |||
266 | static void free_prng_context(struct prng_context *ctx) | ||
267 | { | ||
268 | crypto_free_cipher(ctx->tfm); | ||
269 | } | ||
270 | |||
271 | static int reset_prng_context(struct prng_context *ctx, | ||
272 | unsigned char *key, size_t klen, | ||
273 | unsigned char *V, unsigned char *DT) | ||
274 | { | ||
275 | int ret; | ||
276 | int rc = -EINVAL; | ||
277 | unsigned char *prng_key; | ||
278 | |||
279 | spin_lock(&ctx->prng_lock); | ||
280 | ctx->flags |= PRNG_NEED_RESET; | ||
281 | |||
282 | prng_key = (key != NULL) ? key : (unsigned char *)DEFAULT_PRNG_KEY; | ||
283 | |||
284 | if (!key) | ||
285 | klen = DEFAULT_PRNG_KSZ; | ||
286 | |||
287 | if (V) | ||
288 | memcpy(ctx->V, V, DEFAULT_BLK_SZ); | ||
289 | else | ||
290 | memcpy(ctx->V, DEFAULT_V_SEED, DEFAULT_BLK_SZ); | ||
291 | |||
292 | if (DT) | ||
293 | memcpy(ctx->DT, DT, DEFAULT_BLK_SZ); | ||
294 | else | ||
295 | memset(ctx->DT, 0, DEFAULT_BLK_SZ); | ||
296 | |||
297 | memset(ctx->rand_data, 0, DEFAULT_BLK_SZ); | ||
298 | memset(ctx->last_rand_data, 0, DEFAULT_BLK_SZ); | ||
299 | |||
300 | if (ctx->tfm) | ||
301 | crypto_free_cipher(ctx->tfm); | ||
302 | |||
303 | ctx->tfm = crypto_alloc_cipher("aes", 0, 0); | ||
304 | if (IS_ERR(ctx->tfm)) { | ||
305 | dbgprint(KERN_CRIT "Failed to alloc tfm for context %p\n", | ||
306 | ctx); | ||
307 | ctx->tfm = NULL; | ||
308 | goto out; | ||
309 | } | ||
310 | |||
311 | ctx->rand_data_valid = DEFAULT_BLK_SZ; | ||
312 | |||
313 | ret = crypto_cipher_setkey(ctx->tfm, prng_key, klen); | ||
314 | if (ret) { | ||
315 | dbgprint(KERN_CRIT "PRNG: setkey() failed flags=%x\n", | ||
316 | crypto_cipher_get_flags(ctx->tfm)); | ||
317 | crypto_free_cipher(ctx->tfm); | ||
318 | goto out; | ||
319 | } | ||
320 | |||
321 | rc = 0; | ||
322 | ctx->flags &= ~PRNG_NEED_RESET; | ||
323 | out: | ||
324 | spin_unlock(&ctx->prng_lock); | ||
325 | |||
326 | return rc; | ||
327 | |||
328 | } | ||
329 | |||
330 | static int cprng_init(struct crypto_tfm *tfm) | ||
331 | { | ||
332 | struct prng_context *ctx = crypto_tfm_ctx(tfm); | ||
333 | |||
334 | spin_lock_init(&ctx->prng_lock); | ||
335 | |||
336 | return reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL); | ||
337 | } | ||
338 | |||
339 | static void cprng_exit(struct crypto_tfm *tfm) | ||
340 | { | ||
341 | free_prng_context(crypto_tfm_ctx(tfm)); | ||
342 | } | ||
343 | |||
344 | static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata, | ||
345 | unsigned int dlen) | ||
346 | { | ||
347 | struct prng_context *prng = crypto_rng_ctx(tfm); | ||
348 | |||
349 | return get_prng_bytes(rdata, dlen, prng); | ||
350 | } | ||
351 | |||
352 | static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen) | ||
353 | { | ||
354 | struct prng_context *prng = crypto_rng_ctx(tfm); | ||
355 | u8 *key = seed + DEFAULT_PRNG_KSZ; | ||
356 | |||
357 | if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ) | ||
358 | return -EINVAL; | ||
359 | |||
360 | reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, NULL); | ||
361 | |||
362 | if (prng->flags & PRNG_NEED_RESET) | ||
363 | return -EINVAL; | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static struct crypto_alg rng_alg = { | ||
368 | .cra_name = "stdrng", | ||
369 | .cra_driver_name = "ansi_cprng", | ||
370 | .cra_priority = 100, | ||
371 | .cra_flags = CRYPTO_ALG_TYPE_RNG, | ||
372 | .cra_ctxsize = sizeof(struct prng_context), | ||
373 | .cra_type = &crypto_rng_type, | ||
374 | .cra_module = THIS_MODULE, | ||
375 | .cra_list = LIST_HEAD_INIT(rng_alg.cra_list), | ||
376 | .cra_init = cprng_init, | ||
377 | .cra_exit = cprng_exit, | ||
378 | .cra_u = { | ||
379 | .rng = { | ||
380 | .rng_make_random = cprng_get_random, | ||
381 | .rng_reset = cprng_reset, | ||
382 | .seedsize = DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ, | ||
383 | } | ||
384 | } | ||
385 | }; | ||
386 | |||
387 | |||
388 | /* Module initalization */ | ||
389 | static int __init prng_mod_init(void) | ||
390 | { | ||
391 | int ret = 0; | ||
392 | |||
393 | if (fips_enabled) | ||
394 | rng_alg.cra_priority += 200; | ||
395 | |||
396 | ret = crypto_register_alg(&rng_alg); | ||
397 | |||
398 | if (ret) | ||
399 | goto out; | ||
400 | out: | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static void __exit prng_mod_fini(void) | ||
405 | { | ||
406 | crypto_unregister_alg(&rng_alg); | ||
407 | return; | ||
408 | } | ||
409 | |||
410 | MODULE_LICENSE("GPL"); | ||
411 | MODULE_DESCRIPTION("Software Pseudo Random Number Generator"); | ||
412 | MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>"); | ||
413 | module_param(dbg, int, 0); | ||
414 | MODULE_PARM_DESC(dbg, "Boolean to enable debugging (0/1 == off/on)"); | ||
415 | module_init(prng_mod_init); | ||
416 | module_exit(prng_mod_fini); | ||
417 | MODULE_ALIAS("stdrng"); | ||