Blame view
crypto/ansi_cprng.c
10.7 KB
59e0b61cd treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
17f0f4a47 crypto: rng - RNG... |
2 3 4 5 6 7 |
/* * 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> |
17f0f4a47 crypto: rng - RNG... |
8 9 10 11 12 13 14 15 |
*/ #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> |
17f0f4a47 crypto: rng - RNG... |
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
#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 |
25985edce Fix common misspe... |
77 |
* returns 0 if generation succeeded, <0 if something went wrong |
17f0f4a47 crypto: rng - RNG... |
78 |
*/ |
667b6294b crypto: ansi_cprn... |
79 |
static int _get_more_prng_bytes(struct prng_context *ctx, int cont_test) |
17f0f4a47 crypto: rng - RNG... |
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
{ int i; unsigned char tmp[DEFAULT_BLK_SZ]; unsigned char *output = NULL; dbgprint(KERN_CRIT "Calling _get_more_prng_bytes for context %p ", 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)) { |
667b6294b crypto: ansi_cprn... |
127 |
if (cont_test) { |
c5b1e545a crypto: ansi_cprn... |
128 129 130 131 |
panic("cprng %p Failed repetition check! ", ctx); } |
17f0f4a47 crypto: rng - RNG... |
132 133 134 135 |
printk(KERN_ERR "ctx %p Failed repetition check! ", ctx); |
c5b1e545a crypto: ansi_cprn... |
136 |
|
17f0f4a47 crypto: rng - RNG... |
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
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 */ |
09fbf7c0f crypto: ansi_cprn... |
163 |
for (i = DEFAULT_BLK_SZ - 1; i >= 0; i--) { |
17f0f4a47 crypto: rng - RNG... |
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
ctx->DT[i] += 1; if (ctx->DT[i] != 0) break; } dbgprint("Returning new block for context %p ", 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 */ |
667b6294b crypto: ansi_cprn... |
182 183 |
static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx, int do_cont_test) |
17f0f4a47 crypto: rng - RNG... |
184 |
{ |
17f0f4a47 crypto: rng - RNG... |
185 186 187 |
unsigned char *ptr = buf; unsigned int byte_count = (unsigned int)nbytes; int err; |
ed9407005 crypto: ansi_prng... |
188 |
spin_lock_bh(&ctx->prng_lock); |
17f0f4a47 crypto: rng - RNG... |
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
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; } |
cde001e4c crypto: rng - RNG... |
204 205 206 207 208 |
/* * Return 0 in case of success as mandated by the kernel * crypto API interface definition. */ err = 0; |
17f0f4a47 crypto: rng - RNG... |
209 210 211 212 213 214 215 216 |
dbgprint(KERN_CRIT "getting %d random bytes for context %p ", byte_count, ctx); remainder: if (ctx->rand_data_valid == DEFAULT_BLK_SZ) { |
667b6294b crypto: ansi_cprn... |
217 |
if (_get_more_prng_bytes(ctx, do_cont_test) < 0) { |
17f0f4a47 crypto: rng - RNG... |
218 219 220 221 222 223 224 |
memset(buf, 0, nbytes); err = -EINVAL; goto done; } } /* |
aa1a85dbd crypto: ansi_cprn... |
225 |
* Copy any data less than an entire block |
17f0f4a47 crypto: rng - RNG... |
226 227 |
*/ if (byte_count < DEFAULT_BLK_SZ) { |
aa1a85dbd crypto: ansi_cprn... |
228 |
empty_rbuf: |
714b33d15 crypto: ansi_cprn... |
229 |
while (ctx->rand_data_valid < DEFAULT_BLK_SZ) { |
17f0f4a47 crypto: rng - RNG... |
230 231 232 |
*ptr = ctx->rand_data[ctx->rand_data_valid]; ptr++; byte_count--; |
714b33d15 crypto: ansi_cprn... |
233 |
ctx->rand_data_valid++; |
17f0f4a47 crypto: rng - RNG... |
234 235 236 237 238 239 240 241 242 |
if (byte_count == 0) goto done; } } /* * Now copy whole blocks */ for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) { |
aa1a85dbd crypto: ansi_cprn... |
243 |
if (ctx->rand_data_valid == DEFAULT_BLK_SZ) { |
667b6294b crypto: ansi_cprn... |
244 |
if (_get_more_prng_bytes(ctx, do_cont_test) < 0) { |
aa1a85dbd crypto: ansi_cprn... |
245 246 247 248 |
memset(buf, 0, nbytes); err = -EINVAL; goto done; } |
17f0f4a47 crypto: rng - RNG... |
249 |
} |
aa1a85dbd crypto: ansi_cprn... |
250 251 |
if (ctx->rand_data_valid > 0) goto empty_rbuf; |
17f0f4a47 crypto: rng - RNG... |
252 253 254 255 256 257 |
memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ); ctx->rand_data_valid += DEFAULT_BLK_SZ; ptr += DEFAULT_BLK_SZ; } /* |
aa1a85dbd crypto: ansi_cprn... |
258 |
* Now go back and get any remaining partial block |
17f0f4a47 crypto: rng - RNG... |
259 260 261 262 263 |
*/ if (byte_count) goto remainder; done: |
ed9407005 crypto: ansi_prng... |
264 |
spin_unlock_bh(&ctx->prng_lock); |
17f0f4a47 crypto: rng - RNG... |
265 266 267 268 269 270 271 272 273 274 275 276 |
dbgprint(KERN_CRIT "returning %d from get_prng_bytes in context %p ", 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, |
e7c2422a8 crypto: ansi_cprn... |
277 278 |
const unsigned char *key, size_t klen, const unsigned char *V, const unsigned char *DT) |
17f0f4a47 crypto: rng - RNG... |
279 280 |
{ int ret; |
e7c2422a8 crypto: ansi_cprn... |
281 |
const unsigned char *prng_key; |
17f0f4a47 crypto: rng - RNG... |
282 |
|
ed9407005 crypto: ansi_prng... |
283 |
spin_lock_bh(&ctx->prng_lock); |
17f0f4a47 crypto: rng - RNG... |
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
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); |
17f0f4a47 crypto: rng - RNG... |
303 304 305 306 307 308 309 |
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 ", crypto_cipher_get_flags(ctx->tfm)); |
17f0f4a47 crypto: rng - RNG... |
310 311 |
goto out; } |
fd09d7fac crypto: ansi_prng... |
312 |
ret = 0; |
17f0f4a47 crypto: rng - RNG... |
313 314 |
ctx->flags &= ~PRNG_NEED_RESET; out: |
ed9407005 crypto: ansi_prng... |
315 |
spin_unlock_bh(&ctx->prng_lock); |
fd09d7fac crypto: ansi_prng... |
316 |
return ret; |
17f0f4a47 crypto: rng - RNG... |
317 318 319 320 321 322 323 |
} static int cprng_init(struct crypto_tfm *tfm) { struct prng_context *ctx = crypto_tfm_ctx(tfm); spin_lock_init(&ctx->prng_lock); |
fd09d7fac crypto: ansi_prng... |
324 325 326 327 328 329 330 |
ctx->tfm = crypto_alloc_cipher("aes", 0, 0); if (IS_ERR(ctx->tfm)) { dbgprint(KERN_CRIT "Failed to alloc tfm for context %p ", ctx); return PTR_ERR(ctx->tfm); } |
17f0f4a47 crypto: rng - RNG... |
331 |
|
d7992f42c crypto: ansi_cprn... |
332 333 334 335 336 337 338 339 340 341 |
if (reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL) < 0) return -EINVAL; /* * after allocation, we should always force the user to reset * so they don't inadvertently use the insecure default values * without specifying them intentially */ ctx->flags |= PRNG_NEED_RESET; return 0; |
17f0f4a47 crypto: rng - RNG... |
342 343 344 345 346 347 |
} static void cprng_exit(struct crypto_tfm *tfm) { free_prng_context(crypto_tfm_ctx(tfm)); } |
e7c2422a8 crypto: ansi_cprn... |
348 349 350 |
static int cprng_get_random(struct crypto_rng *tfm, const u8 *src, unsigned int slen, u8 *rdata, unsigned int dlen) |
17f0f4a47 crypto: rng - RNG... |
351 352 |
{ struct prng_context *prng = crypto_rng_ctx(tfm); |
667b6294b crypto: ansi_cprn... |
353 354 |
return get_prng_bytes(rdata, dlen, prng, 0); } |
2566578a6 crypto: ansi_cprn... |
355 356 357 358 359 360 |
/* * This is the cprng_registered reset method the seed value is * interpreted as the tuple { V KEY DT} * V and KEY are required during reset, and DT is optional, detected * as being present by testing the length of the seed */ |
e7c2422a8 crypto: ansi_cprn... |
361 362 |
static int cprng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen) |
17f0f4a47 crypto: rng - RNG... |
363 364 |
{ struct prng_context *prng = crypto_rng_ctx(tfm); |
e7c2422a8 crypto: ansi_cprn... |
365 366 |
const u8 *key = seed + DEFAULT_BLK_SZ; const u8 *dt = NULL; |
17f0f4a47 crypto: rng - RNG... |
367 368 369 |
if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ) return -EINVAL; |
2566578a6 crypto: ansi_cprn... |
370 371 372 373 |
if (slen >= (2 * DEFAULT_BLK_SZ + DEFAULT_PRNG_KSZ)) dt = key + DEFAULT_PRNG_KSZ; reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, dt); |
17f0f4a47 crypto: rng - RNG... |
374 375 376 377 378 |
if (prng->flags & PRNG_NEED_RESET) return -EINVAL; return 0; } |
667b6294b crypto: ansi_cprn... |
379 |
#ifdef CONFIG_CRYPTO_FIPS |
e7c2422a8 crypto: ansi_cprn... |
380 381 382 |
static int fips_cprng_get_random(struct crypto_rng *tfm, const u8 *src, unsigned int slen, u8 *rdata, unsigned int dlen) |
2f32bfd83 crypto: ansi_cprn... |
383 384 385 386 387 |
{ struct prng_context *prng = crypto_rng_ctx(tfm); return get_prng_bytes(rdata, dlen, prng, 1); } |
e7c2422a8 crypto: ansi_cprn... |
388 389 |
static int fips_cprng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen) |
2f32bfd83 crypto: ansi_cprn... |
390 391 |
{ u8 rdata[DEFAULT_BLK_SZ]; |
e7c2422a8 crypto: ansi_cprn... |
392 |
const u8 *key = seed + DEFAULT_BLK_SZ; |
2f32bfd83 crypto: ansi_cprn... |
393 394 395 |
int rc; struct prng_context *prng = crypto_rng_ctx(tfm); |
505172e11 crypto: ansi_cprn... |
396 397 398 399 400 401 |
if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ) return -EINVAL; /* fips strictly requires seed != key */ if (!memcmp(seed, key, DEFAULT_PRNG_KSZ)) return -EINVAL; |
2f32bfd83 crypto: ansi_cprn... |
402 403 404 405 406 407 408 409 410 411 412 413 |
rc = cprng_reset(tfm, seed, slen); if (!rc) goto out; /* this primes our continuity test */ rc = get_prng_bytes(rdata, DEFAULT_BLK_SZ, prng, 0); prng->rand_data_valid = DEFAULT_BLK_SZ; out: return rc; } |
8fc229a51 crypto: ansi_cprn... |
414 |
#endif |
2f32bfd83 crypto: ansi_cprn... |
415 |
|
e7c2422a8 crypto: ansi_cprn... |
416 417 418 419 420 421 422 423 424 425 426 427 |
static struct rng_alg rng_algs[] = { { .generate = cprng_get_random, .seed = cprng_reset, .seedsize = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ, .base = { .cra_name = "stdrng", .cra_driver_name = "ansi_cprng", .cra_priority = 100, .cra_ctxsize = sizeof(struct prng_context), .cra_module = THIS_MODULE, .cra_init = cprng_init, .cra_exit = cprng_exit, |
8fc229a51 crypto: ansi_cprn... |
428 429 430 |
} #ifdef CONFIG_CRYPTO_FIPS }, { |
e7c2422a8 crypto: ansi_cprn... |
431 432 433 434 435 436 437 438 439 440 441 |
.generate = fips_cprng_get_random, .seed = fips_cprng_reset, .seedsize = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ, .base = { .cra_name = "fips(ansi_cprng)", .cra_driver_name = "fips_ansi_cprng", .cra_priority = 300, .cra_ctxsize = sizeof(struct prng_context), .cra_module = THIS_MODULE, .cra_init = cprng_init, .cra_exit = cprng_exit, |
667b6294b crypto: ansi_cprn... |
442 |
} |
667b6294b crypto: ansi_cprn... |
443 |
#endif |
8fc229a51 crypto: ansi_cprn... |
444 |
} }; |
17f0f4a47 crypto: rng - RNG... |
445 446 447 448 |
/* Module initalization */ static int __init prng_mod_init(void) { |
e7c2422a8 crypto: ansi_cprn... |
449 |
return crypto_register_rngs(rng_algs, ARRAY_SIZE(rng_algs)); |
17f0f4a47 crypto: rng - RNG... |
450 451 452 453 |
} static void __exit prng_mod_fini(void) { |
e7c2422a8 crypto: ansi_cprn... |
454 |
crypto_unregister_rngs(rng_algs, ARRAY_SIZE(rng_algs)); |
17f0f4a47 crypto: rng - RNG... |
455 456 457 458 459 460 461 |
} MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Software Pseudo Random Number Generator"); MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>"); module_param(dbg, int, 0); MODULE_PARM_DESC(dbg, "Boolean to enable debugging (0/1 == off/on)"); |
c4741b230 crypto: run initc... |
462 |
subsys_initcall(prng_mod_init); |
17f0f4a47 crypto: rng - RNG... |
463 |
module_exit(prng_mod_fini); |
5d26a105b crypto: prefix mo... |
464 |
MODULE_ALIAS_CRYPTO("stdrng"); |
3e14dcf7c crypto: add missi... |
465 |
MODULE_ALIAS_CRYPTO("ansi_cprng"); |