39c007f6bbbf16d70f489ce5fdbfe2cd30b06351
[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 = 0;
278         int r = 0;
279
280 #if 0
281         FIPSerr(FIPS_F_DRBG_RESEED, FIPS_R_NOT_INSTANTIATED);
282         FIPSerr(FIPS_F_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_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         }