Initial, provisional, subject to wholesale change, untested, probably
[openssl.git] / fips / rand / fips_drbg_hash.c
1 /* fips/rand/fips_drbg_hash.c */
2 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3  * project.
4  */
5 /* ====================================================================
6  * Copyright (c) 2011 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer. 
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  */
53
54 #define OPENSSL_FIPSAPI
55
56 #include <stdlib.h>
57 #include <string.h>
58 #include <openssl/crypto.h>
59 #include <openssl/evp.h>
60 #include <openssl/aes.h>
61 #include <openssl/fips.h>
62 #include <openssl/fips_rand.h>
63 #include "fips_rand_lcl.h"
64
65 /* This is Hash_df from SP 800-90 10.4.1 */
66
67 static int hash_df(DRBG_CTX *dctx, unsigned char *out,
68                         const unsigned char *in1, size_t in1len,
69                         const unsigned char *in2, size_t in2len,
70                         const unsigned char *in3, size_t in3len,
71                         const unsigned char *in4, size_t in4len)
72         {
73         EVP_MD_CTX *mctx = &dctx->d.hash.mctx;
74         unsigned char *vtmp = dctx->d.hash.vtmp;
75         unsigned char tmp[6];
76         /* Standard only ever needs seedlen bytes which is always less than
77          * maximum permitted so no need to check length.
78          */
79         size_t outlen = dctx->seedlen;
80         tmp[0] = 1;
81         tmp[1] = ((outlen * 8) >> 24) & 0xff;
82         tmp[2] = ((outlen * 8) >> 16) & 0xff;
83         tmp[3] = ((outlen * 8) >> 8) & 0xff;
84         tmp[4] = (outlen * 8) & 0xff;
85         if (!in1)
86                 {
87                 tmp[5] = (unsigned char)in1len;
88                 in1 = tmp + 5;
89                 in1len = 1;
90                 }
91         for (;;)
92                 {
93                 if (!FIPS_digestinit(mctx, dctx->d.hash.md))
94                         return 0;
95                 if (!FIPS_digestupdate(mctx, tmp, 5))
96                         return 0;
97                 if (in1 && !FIPS_digestupdate(mctx, in1, in1len))
98                         return 0;
99                 if (in2 && !FIPS_digestupdate(mctx, in2, in2len))
100                         return 0;
101                 if (in3 && !FIPS_digestupdate(mctx, in3, in3len))
102                         return 0;
103                 if (in4 && !FIPS_digestupdate(mctx, in4, in4len))
104                         return 0;
105                 if (outlen < dctx->blocklength)
106                         {
107                         if (!FIPS_digestfinal(mctx, vtmp, NULL))
108                                 return 0;
109                         memcpy(out, vtmp, outlen);
110                         OPENSSL_cleanse(vtmp, dctx->blocklength);
111                         return 1;
112                         }
113                 else if(!FIPS_digestfinal(mctx, out, NULL))
114                         return 0;
115
116                 outlen -= dctx->blocklength;
117                 if (outlen == 0)
118                         return 1;
119                 tmp[0]++;
120                 out += dctx->blocklength;
121                 }
122         }
123
124
125 /* Add an unsigned buffer to the buf value, storing the result in buf. For
126  * this algorithm the length of input never exceeds the seed length.
127  */
128
129 static void ctx_add_buf(DRBG_CTX *dctx, unsigned char *buf,
130                                 unsigned char *in, size_t inlen)
131         {
132         size_t i = inlen;
133         const unsigned char *q;
134         unsigned char c, *p;
135         p = buf + dctx->seedlen;
136         q = in + inlen;
137
138         OPENSSL_assert(i <= dctx->seedlen);
139
140         /* Special case: zero length, just increment buffer */
141         if (i)
142                 c = 0;
143         else 
144                 c = 1;
145
146         while (i)
147                 {
148                 int r;
149                 p--;
150                 q--;
151                 r = *p + *q + c;
152                 /* Carry */
153                 if (r > 0xff)
154                         c = 1;
155                 else
156                         c = 0;
157                 *p = r & 0xff;
158                 i--;
159                 }
160
161         i = dctx->seedlen - inlen;
162
163         /* If not adding whole buffer handle final carries */
164         if (c && i)
165                 {
166                 do
167                         {
168                         p--;
169                         c = *p;
170                         c++;
171                         *p = c;
172                         if(c)
173                                 return;
174                         } while(i--);
175                 }
176         }
177
178 /* Finalise and add hash to V */
179         
180 static int ctx_add_md(DRBG_CTX *dctx)
181         {
182         if (!FIPS_digestfinal(&dctx->d.hash.mctx, dctx->d.hash.vtmp, NULL))
183                         return 0;
184         ctx_add_buf(dctx, dctx->d.hash.V, dctx->d.hash.vtmp, dctx->blocklength);
185         return 1;
186         }
187
188 static int hash_gen(DRBG_CTX *dctx, unsigned char *out, size_t outlen)
189         {
190         DRBG_HASH_CTX *hctx = &dctx->d.hash;
191         if (outlen == 0)
192                 return 1;
193         memcpy(hctx->vtmp, hctx->V, dctx->seedlen);
194         for(;;)
195                 {
196                 FIPS_digestinit(&hctx->mctx, hctx->md);
197                 FIPS_digestupdate(&hctx->mctx, hctx->vtmp, dctx->seedlen);
198                 if (outlen < dctx->blocklength)
199                         {
200                         FIPS_digestfinal(&hctx->mctx, hctx->vtmp, NULL);
201                         memcpy(out, hctx->vtmp, outlen);
202                         return 1;
203                         }
204                 FIPS_digestfinal(&hctx->mctx, out, NULL);
205                 outlen -= dctx->blocklength;
206                 if (outlen == 0)
207                         return 1;
208                 out += dctx->blocklength;
209                 ctx_add_buf(dctx, hctx->vtmp, NULL, 0);
210                 }
211         }
212
213 static int drbg_hash_instantiate(DRBG_CTX *dctx,
214                                 const unsigned char *ent, size_t ent_len,
215                                 const unsigned char *nonce, size_t nonce_len,
216                                 const unsigned char *pstr, size_t pstr_len)
217         {
218         DRBG_HASH_CTX *hctx = &dctx->d.hash;
219         if (!hash_df(dctx, hctx->V, 
220                         ent, ent_len, nonce, nonce_len, pstr, pstr_len,
221                         NULL, 0))
222                 return 0;
223         if (!hash_df(dctx, hctx->C, 
224                         NULL, 0, hctx->V, dctx->seedlen,
225                         NULL, 0, NULL, 0))
226                 return 0;
227
228 #ifdef HASH_DRBG_TRACE
229         fprintf(stderr, "V+C after instantiate:\n");
230         hexprint(stderr, hctx->V, dctx->seedlen);
231         hexprint(stderr, hctx->C, dctx->seedlen);
232 #endif
233         return 1;
234         }
235
236         
237 static int drbg_hash_reseed(DRBG_CTX *dctx,
238                                 const unsigned char *ent, size_t ent_len,
239                                 const unsigned char *adin, size_t adin_len)
240         {
241         DRBG_HASH_CTX *hctx = &dctx->d.hash;
242         /* V about to be updated so use C as output instead */
243         if (!hash_df(dctx, hctx->C,
244                         NULL, 1, hctx->V, dctx->seedlen,
245                         ent, ent_len, adin, adin_len))
246                 return 0;
247         memcpy(hctx->V, hctx->C, dctx->seedlen);
248         if (!hash_df(dctx, hctx->C, NULL, 0,
249                         hctx->V, dctx->seedlen, NULL, 0, NULL, 0))
250                 return 0;
251 #ifdef HASH_DRBG_TRACE
252         fprintf(stderr, "V+C after reseed:\n");
253         hexprint(stderr, hctx->V, dctx->seedlen);
254         hexprint(stderr, hctx->C, dctx->seedlen);
255 #endif
256         return 1;
257         }
258
259 static int drbg_hash_generate(DRBG_CTX *dctx,
260                                 unsigned char *out, size_t outlen,
261                                 const unsigned char *adin, size_t adin_len)
262         {
263         DRBG_HASH_CTX *hctx = &dctx->d.hash;
264         EVP_MD_CTX *mctx = &hctx->mctx;
265         unsigned char tmp[4];
266         if (adin && adin_len)
267                 {
268                 tmp[0] = 2;
269                 if (!FIPS_digestinit(mctx, hctx->md))
270                         return 0;
271                 if (!EVP_DigestUpdate(mctx, tmp, 1))
272                         return 0;
273                 if (!EVP_DigestUpdate(mctx, hctx->V, dctx->seedlen))
274                         return 0;
275                 if (!EVP_DigestUpdate(mctx, adin, adin_len))
276                         return 0;
277                 if (!ctx_add_md(dctx))
278                         return 0;
279                 }
280         if (!hash_gen(dctx, out, outlen))
281                 return 0;
282
283         tmp[0] = 3;
284         if (!FIPS_digestinit(mctx, hctx->md))
285                 return 0;
286         if (!EVP_DigestUpdate(mctx, tmp, 1))
287                 return 0;
288         if (!EVP_DigestUpdate(mctx, hctx->V, dctx->seedlen))
289                 return 0;
290
291         if (!ctx_add_md(dctx))
292                 return 0;
293
294         ctx_add_buf(dctx, hctx->V, hctx->C, dctx->seedlen);
295
296         tmp[0] = (dctx->reseed_counter >> 24) & 0xff;
297         tmp[1] = (dctx->reseed_counter >> 16) & 0xff;
298         tmp[2] = (dctx->reseed_counter >> 8) & 0xff;
299         tmp[3] = dctx->reseed_counter & 0xff;
300         ctx_add_buf(dctx, hctx->V, tmp, 4);
301 #ifdef HASH_DRBG_TRACE
302         fprintf(stderr, "V+C after generate:\n");
303         hexprint(stderr, hctx->V, dctx->seedlen);
304         hexprint(stderr, hctx->C, dctx->seedlen);
305 #endif
306         return 1;
307         }
308
309 int fips_drbg_hash_init(DRBG_CTX *dctx)
310         {
311         const EVP_MD *md;
312         DRBG_HASH_CTX *hctx = &dctx->d.hash;
313         switch (dctx->type)
314                 {
315                 case NID_sha1:
316                 md = EVP_sha1();
317                 dctx->strength = 128;
318                 break;
319
320                 case NID_sha224:
321                 md = EVP_sha224();
322                 dctx->strength = 192;
323                 break;
324
325                 case NID_sha256:
326                 md = EVP_sha256();
327                 dctx->strength = 256;
328                 break;
329
330                 case NID_sha384:
331                 md = EVP_sha384();
332                 dctx->strength = 256;
333                 break;
334
335                 case NID_sha512:
336                 md = EVP_sha512();
337                 dctx->strength = 256;
338                 break;
339
340                 default:
341                 return -2;
342                 break;
343
344                 }
345
346         dctx->instantiate = drbg_hash_instantiate;
347         dctx->reseed = drbg_hash_reseed;
348         dctx->generate = drbg_hash_generate;
349
350         dctx->d.hash.md = md;
351         EVP_MD_CTX_init(&hctx->mctx);
352
353         /* These are taken from SP 800-90 10.1 table 2 */
354
355         dctx->blocklength = M_EVP_MD_size(md);
356         if (dctx->blocklength > 32)
357                 dctx->seedlen = 111;
358         else
359                 dctx->seedlen = 55;
360
361
362         dctx->min_entropy = dctx->strength / 8;
363         dctx->max_entropy = DRBG_MAX_ENTROPY;
364
365         dctx->min_nonce = dctx->min_entropy / 2;
366         dctx->max_nonce = DRBG_MAX_NONCE;
367
368         dctx->max_pers = DRBG_MAX_LENGTH;
369         dctx->max_adin = DRBG_MAX_LENGTH;
370
371         dctx->max_request = 1<<19;
372
373         return 1;
374         }