]> git.ipfire.org Git - thirdparty/openssl.git/blob - fips/rand/fips_drbg_lib.c
Use function name FIPS_drbg_health_check() for health check function.
[thirdparty/openssl.git] / fips / rand / fips_drbg_lib.c
1 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
2 * project.
3 */
4 /* ====================================================================
5 * Copyright (c) 2011 The OpenSSL Project. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. All advertising materials mentioning features or use of this
20 * software must display the following acknowledgment:
21 * "This product includes software developed by the OpenSSL Project
22 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
23 *
24 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
25 * endorse or promote products derived from this software without
26 * prior written permission. For written permission, please contact
27 * licensing@OpenSSL.org.
28 *
29 * 5. Products derived from this software may not be called "OpenSSL"
30 * nor may "OpenSSL" appear in their names without prior written
31 * permission of the OpenSSL Project.
32 *
33 * 6. Redistributions of any form whatsoever must retain the following
34 * acknowledgment:
35 * "This product includes software developed by the OpenSSL Project
36 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
39 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
41 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
42 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
47 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
49 * OF THE POSSIBILITY OF SUCH DAMAGE.
50 * ====================================================================
51 */
52
53 #define OPENSSL_FIPSAPI
54
55 #include <string.h>
56 #include <openssl/crypto.h>
57 #include <openssl/err.h>
58 #include <openssl/fips_rand.h>
59 #include "fips_rand_lcl.h"
60
61 /* Support framework for SP800-90 DRBGs */
62
63 int FIPS_drbg_init(DRBG_CTX *dctx, int type, unsigned int flags)
64 {
65 int rv;
66 memset(dctx, 0, sizeof(DRBG_CTX));
67 dctx->status = DRBG_STATUS_UNINITIALISED;
68 dctx->xflags = flags;
69 dctx->type = type;
70
71 dctx->iflags = 0;
72 dctx->entropy_blocklen = 0;
73 dctx->health_check_cnt = 0;
74 dctx->health_check_interval = DRBG_HEALTH_INTERVAL;
75
76 rv = fips_drbg_hash_init(dctx);
77
78 if (rv == -2)
79 rv = fips_drbg_ctr_init(dctx);
80 if (rv == -2)
81 rv = fips_drbg_hmac_init(dctx);
82 if (rv == -2)
83 rv = fips_drbg_ec_init(dctx);
84
85 if (rv <= 0)
86 {
87 if (rv == -2)
88 FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_UNSUPPORTED_DRBG_TYPE);
89 else
90 FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_ERROR_INITIALISING_DRBG);
91 }
92
93 /* If not in test mode run selftests on DRBG of the same type */
94
95 if (!(dctx->xflags & DRBG_FLAG_TEST))
96 {
97 if (!FIPS_drbg_health_check(dctx))
98 {
99 FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_SELFTEST_FAILURE);
100 return 0;
101 }
102 }
103
104 return rv;
105 }
106
107 DRBG_CTX *FIPS_drbg_new(int type, unsigned int flags)
108 {
109 DRBG_CTX *dctx;
110 dctx = OPENSSL_malloc(sizeof(DRBG_CTX));
111 if (!dctx)
112 {
113 FIPSerr(FIPS_F_FIPS_DRBG_NEW, ERR_R_MALLOC_FAILURE);
114 return NULL;
115 }
116
117 if (type == 0)
118 {
119 memset(dctx, 0, sizeof(DRBG_CTX));
120 dctx->type = 0;
121 dctx->status = DRBG_STATUS_UNINITIALISED;
122 return dctx;
123 }
124
125 if (FIPS_drbg_init(dctx, type, flags) <= 0)
126 {
127 OPENSSL_free(dctx);
128 return NULL;
129 }
130
131 return dctx;
132 }
133
134 void FIPS_drbg_free(DRBG_CTX *dctx)
135 {
136 if (dctx->uninstantiate)
137 dctx->uninstantiate(dctx);
138 OPENSSL_cleanse(&dctx->d, sizeof(dctx->d));
139 OPENSSL_free(dctx);
140 }
141
142 static size_t fips_get_entropy(DRBG_CTX *dctx, unsigned char **pout,
143 int entropy, size_t min_len, size_t max_len)
144 {
145 unsigned char *tout, *p;
146 size_t bl = dctx->entropy_blocklen, rv;
147 if (dctx->xflags & DRBG_FLAG_TEST || !bl)
148 return dctx->get_entropy(dctx, pout, entropy, min_len, max_len);
149 rv = dctx->get_entropy(dctx, &tout, entropy + bl,
150 min_len + bl, max_len + bl);
151 *pout = tout + bl;
152 if (rv < (min_len + bl) || (rv % bl))
153 return 0;
154 /* Compare consecutive blocks for continuous PRNG test */
155 for (p = tout; p < tout + rv - bl; p += bl)
156 {
157 if (!memcmp(p, p + bl, bl))
158 {
159 FIPSerr(FIPS_F_FIPS_GET_ENTROPY, FIPS_R_ENTROPY_SOURCE_STUCK);
160 return 0;
161 }
162 }
163 rv -= bl;
164 if (rv > max_len)
165 return max_len;
166 return rv;
167 }
168
169 static void fips_cleanup_entropy(DRBG_CTX *dctx,
170 unsigned char *out, size_t olen)
171 {
172 size_t bl;
173 if (dctx->xflags & DRBG_FLAG_TEST)
174 bl = 0;
175 else
176 bl = dctx->entropy_blocklen;
177 /* Call cleanup with original arguments */
178 dctx->cleanup_entropy(dctx, out - bl, olen + bl);
179 }
180
181
182 int FIPS_drbg_instantiate(DRBG_CTX *dctx,
183 const unsigned char *pers, size_t perslen)
184 {
185 size_t entlen = 0, noncelen = 0;
186 unsigned char *nonce = NULL, *entropy = NULL;
187
188 #if 0
189 /* Put here so error script picks them up */
190 FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE,
191 FIPS_R_PERSONALISATION_STRING_TOO_LONG);
192 FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_IN_ERROR_STATE);
193 FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_ALREADY_INSTANTIATED);
194 FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_ERROR_RETRIEVING_ENTROPY);
195 FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_ERROR_RETRIEVING_NONCE);
196 FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_INSTANTIATE_ERROR);
197 #endif
198
199 int r = 0;
200
201 if (perslen > dctx->max_pers)
202 {
203 r = FIPS_R_PERSONALISATION_STRING_TOO_LONG;
204 goto end;
205 }
206
207 if (dctx->status != DRBG_STATUS_UNINITIALISED)
208 {
209 if (dctx->status == DRBG_STATUS_ERROR)
210 r = FIPS_R_IN_ERROR_STATE;
211 else
212 r = FIPS_R_ALREADY_INSTANTIATED;
213 goto end;
214 }
215
216 dctx->status = DRBG_STATUS_ERROR;
217
218 entlen = fips_get_entropy(dctx, &entropy, dctx->strength,
219 dctx->min_entropy, dctx->max_entropy);
220
221 if (entlen < dctx->min_entropy || entlen > dctx->max_entropy)
222 {
223 r = FIPS_R_ERROR_RETRIEVING_ENTROPY;
224 goto end;
225 }
226
227 if (dctx->max_nonce > 0)
228 {
229 noncelen = dctx->get_nonce(dctx, &nonce,
230 dctx->strength / 2,
231 dctx->min_nonce, dctx->max_nonce);
232
233 if (noncelen < dctx->min_nonce || noncelen > dctx->max_nonce)
234 {
235 r = FIPS_R_ERROR_RETRIEVING_NONCE;
236 goto end;
237 }
238
239 }
240
241 if (!dctx->instantiate(dctx,
242 entropy, entlen,
243 nonce, noncelen,
244 pers, perslen))
245 {
246 r = FIPS_R_ERROR_INSTANTIATING_DRBG;
247 goto end;
248 }
249
250
251 dctx->status = DRBG_STATUS_READY;
252 if (!(dctx->iflags & DRBG_CUSTOM_RESEED))
253 dctx->reseed_counter = 1;
254
255 end:
256
257 if (entropy && dctx->cleanup_entropy)
258 fips_cleanup_entropy(dctx, entropy, entlen);
259
260 if (nonce && dctx->cleanup_nonce)
261 dctx->cleanup_nonce(dctx, nonce, noncelen);
262
263 if (dctx->status == DRBG_STATUS_READY)
264 return 1;
265
266 if (r && !(dctx->iflags & DRBG_FLAG_NOERR))
267 FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, r);
268
269 return 0;
270
271 }
272
273 static int drbg_reseed(DRBG_CTX *dctx,
274 const unsigned char *adin, size_t adinlen, int hcheck)
275 {
276 unsigned char *entropy = NULL;
277 size_t entlen;
278 int r = 0;
279
280 #if 0
281 FIPSerr(FIPS_F_FIPS_DRBG_RESEED, FIPS_R_NOT_INSTANTIATED);
282 FIPSerr(FIPS_F_FIPS_DRBG_RESEED, FIPS_R_ADDITIONAL_INPUT_TOO_LONG);
283 #endif
284 if (dctx->status != DRBG_STATUS_READY
285 && dctx->status != DRBG_STATUS_RESEED)
286 {
287 if (dctx->status == DRBG_STATUS_ERROR)
288 r = FIPS_R_IN_ERROR_STATE;
289 else if(dctx->status == DRBG_STATUS_UNINITIALISED)
290 r = FIPS_R_NOT_INSTANTIATED;
291 goto end;
292 }
293
294 if (!adin)
295 adinlen = 0;
296 else if (adinlen > dctx->max_adin)
297 {
298 r = FIPS_R_ADDITIONAL_INPUT_TOO_LONG;
299 goto end;
300 }
301
302 dctx->status = DRBG_STATUS_ERROR;
303 /* Peform health check on all reseed operations if not a prediction
304 * resistance request and not in test mode.
305 */
306 if (hcheck && !(dctx->xflags & DRBG_FLAG_TEST))
307 {
308 if (!FIPS_drbg_health_check(dctx))
309 {
310 r = FIPS_R_SELFTEST_FAILURE;
311 goto end;
312 }
313 }
314
315 entlen = fips_get_entropy(dctx, &entropy, dctx->strength,
316 dctx->min_entropy, dctx->max_entropy);
317
318 if (entlen < dctx->min_entropy || entlen > dctx->max_entropy)
319 {
320 r = FIPS_R_ERROR_RETRIEVING_ENTROPY;
321 goto end;
322 }
323
324 if (!dctx->reseed(dctx, entropy, entlen, adin, adinlen))
325 goto end;
326
327 dctx->status = DRBG_STATUS_READY;
328 if (!(dctx->iflags & DRBG_CUSTOM_RESEED))
329 dctx->reseed_counter = 1;
330 end:
331
332 if (entropy && dctx->cleanup_entropy)
333 fips_cleanup_entropy(dctx, entropy, entlen);
334
335 if (dctx->status == DRBG_STATUS_READY)
336 return 1;
337
338 if (r && !(dctx->iflags & DRBG_FLAG_NOERR))
339 FIPSerr(FIPS_F_FIPS_DRBG_RESEED, r);
340
341 return 0;
342 }
343
344 int FIPS_drbg_reseed(DRBG_CTX *dctx,
345 const unsigned char *adin, size_t adinlen)
346 {
347 return drbg_reseed(dctx, adin, adinlen, 1);
348 }
349
350 static int fips_drbg_check(DRBG_CTX *dctx)
351 {
352 if (dctx->xflags & DRBG_FLAG_TEST)
353 return 1;
354 dctx->health_check_cnt++;
355 if (dctx->health_check_cnt >= dctx->health_check_interval)
356 {
357 if (!FIPS_drbg_health_check(dctx))
358 {
359 FIPSerr(FIPS_F_FIPS_DRBG_CHECK, FIPS_R_SELFTEST_FAILURE);
360 return 0;
361 }
362 }
363 return 1;
364 }
365
366 int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen,
367 int prediction_resistance,
368 const unsigned char *adin, size_t adinlen)
369 {
370 int r = 0;
371
372 if (!fips_drbg_check(dctx))
373 return 0;
374
375 if (dctx->status != DRBG_STATUS_READY
376 && dctx->status != DRBG_STATUS_RESEED)
377 {
378 if (dctx->status == DRBG_STATUS_ERROR)
379 r = FIPS_R_IN_ERROR_STATE;
380 else if(dctx->status == DRBG_STATUS_UNINITIALISED)
381 r = FIPS_R_NOT_INSTANTIATED;
382 goto end;
383 }
384
385 if (outlen > dctx->max_request)
386 {
387 r = FIPS_R_REQUEST_TOO_LARGE_FOR_DRBG;
388 return 0;
389 }
390
391 if (adinlen > dctx->max_adin)
392 {
393 r = FIPS_R_ADDITIONAL_INPUT_TOO_LONG;
394 goto end;
395 }
396
397 if (dctx->iflags & DRBG_CUSTOM_RESEED)
398 dctx->generate(dctx, NULL, outlen, NULL, 0);
399 else if (dctx->reseed_counter >= dctx->reseed_interval)
400 dctx->status = DRBG_STATUS_RESEED;
401
402 if (dctx->status == DRBG_STATUS_RESEED || prediction_resistance)
403 {
404 /* If prediction resistance request don't do health check */
405 int hcheck = prediction_resistance ? 0 : 1;
406
407 if (!drbg_reseed(dctx, adin, adinlen, hcheck))
408 {
409 r = FIPS_R_RESEED_ERROR;
410 goto end;
411 }
412 adin = NULL;
413 adinlen = 0;
414 }
415
416 if (!dctx->generate(dctx, out, outlen, adin, adinlen))
417 {
418 r = FIPS_R_GENERATE_ERROR;
419 dctx->status = DRBG_STATUS_ERROR;
420 goto end;
421 }
422 if (!(dctx->iflags & DRBG_CUSTOM_RESEED))
423 {
424 if (dctx->reseed_counter >= dctx->reseed_interval)
425 dctx->status = DRBG_STATUS_RESEED;
426 else
427 dctx->reseed_counter++;
428 }
429
430 end:
431 if (r)
432 {
433 if (!(dctx->iflags & DRBG_FLAG_NOERR))
434 FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, r);
435 return 0;
436 }
437
438 return 1;
439 }
440
441 int FIPS_drbg_uninstantiate(DRBG_CTX *dctx)
442 {
443 int rv;
444 if (!dctx->uninstantiate)
445 rv = 1;
446 else
447 rv = dctx->uninstantiate(dctx);
448 /* Although we'd like to cleanse here we can't because we have to
449 * test the uninstantiate really zeroes the data.
450 */
451 memset(&dctx->d, 0, sizeof(dctx->d));
452 dctx->status = DRBG_STATUS_UNINITIALISED;
453 /* If method has problems uninstantiating, return error */
454 return rv;
455 }
456
457 int FIPS_drbg_set_callbacks(DRBG_CTX *dctx,
458 size_t (*get_entropy)(DRBG_CTX *ctx, unsigned char **pout,
459 int entropy, size_t min_len, size_t max_len),
460 void (*cleanup_entropy)(DRBG_CTX *ctx, unsigned char *out, size_t olen),
461 size_t entropy_blocklen,
462 size_t (*get_nonce)(DRBG_CTX *ctx, unsigned char **pout,
463 int entropy, size_t min_len, size_t max_len),
464 void (*cleanup_nonce)(DRBG_CTX *ctx, unsigned char *out, size_t olen))
465 {
466 if (dctx->status != DRBG_STATUS_UNINITIALISED)
467 return 0;
468 dctx->entropy_blocklen = entropy_blocklen;
469 dctx->get_entropy = get_entropy;
470 dctx->cleanup_entropy = cleanup_entropy;
471 dctx->get_nonce = get_nonce;
472 dctx->cleanup_nonce = cleanup_nonce;
473 return 1;
474 }
475
476 int FIPS_drbg_set_rand_callbacks(DRBG_CTX *dctx,
477 size_t (*get_adin)(DRBG_CTX *ctx, unsigned char **pout),
478 void (*cleanup_adin)(DRBG_CTX *ctx, unsigned char *out, size_t olen),
479 int (*rand_seed_cb)(DRBG_CTX *ctx, const void *buf, int num),
480 int (*rand_add_cb)(DRBG_CTX *ctx,
481 const void *buf, int num, double entropy))
482 {
483 if (dctx->status != DRBG_STATUS_UNINITIALISED)
484 return 0;
485 dctx->get_adin = get_adin;
486 dctx->cleanup_adin = cleanup_adin;
487 dctx->rand_seed_cb = rand_seed_cb;
488 dctx->rand_add_cb = rand_add_cb;
489 return 1;
490 }
491
492 void *FIPS_drbg_get_app_data(DRBG_CTX *dctx)
493 {
494 return dctx->app_data;
495 }
496
497 void FIPS_drbg_set_app_data(DRBG_CTX *dctx, void *app_data)
498 {
499 dctx->app_data = app_data;
500 }
501
502 size_t FIPS_drbg_get_blocklength(DRBG_CTX *dctx)
503 {
504 return dctx->blocklength;
505 }
506
507 int FIPS_drbg_get_strength(DRBG_CTX *dctx)
508 {
509 return dctx->strength;
510 }
511
512 void FIPS_drbg_set_check_interval(DRBG_CTX *dctx, int interval)
513 {
514 dctx->health_check_interval = interval;
515 }
516
517 void FIPS_drbg_set_reseed_interval(DRBG_CTX *dctx, int interval)
518 {
519 dctx->reseed_interval = interval;
520 }
521
522 static int drbg_stick = 0;
523
524 void FIPS_drbg_stick(void)
525 {
526 drbg_stick = 1;
527 }
528
529 /* Continuous DRBG utility function */
530 int fips_drbg_cprng_test(DRBG_CTX *dctx, const unsigned char *out)
531 {
532 /* No CPRNG in test mode */
533 if (dctx->xflags & DRBG_FLAG_TEST)
534 return 1;
535 /* Check block is valid: should never happen */
536 if (dctx->lb_valid == 0)
537 {
538 FIPSerr(FIPS_F_FIPS_DRBG_CPRNG_TEST, FIPS_R_INTERNAL_ERROR);
539 fips_set_selftest_fail();
540 return 0;
541 }
542 if (drbg_stick)
543 memcpy(dctx->lb, out, dctx->blocklength);
544 /* Check against last block: fail if match */
545 if (!memcmp(dctx->lb, out, dctx->blocklength))
546 {
547 FIPSerr(FIPS_F_FIPS_DRBG_CPRNG_TEST, FIPS_R_DRBG_STUCK);
548 fips_set_selftest_fail();
549 return 0;
550 }
551 /* Save last block for next comparison */
552 memcpy(dctx->lb, out, dctx->blocklength);
553 return 1;
554 }