From 591cbfae3cfcb26b45c523499847d3dbe53a44e4 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Fri, 4 Mar 2011 18:00:21 +0000 Subject: [PATCH] Initial, provisional, subject to wholesale change, untested, probably not working, incomplete and unused SP800-90 DRBGs for CTR and Hash modes. Did I say this was untested? --- CHANGES | 4 + fips/rand/Makefile | 8 +- fips/rand/fips_drbg_ctr.c | 419 +++++++++++++++++++++++++++++++++++++ fips/rand/fips_drbg_hash.c | 374 +++++++++++++++++++++++++++++++++ fips/rand/fips_drbg_lib.c | 226 ++++++++++++++++++++ fips/rand/fips_rand.h | 10 + fips/rand/fips_rand_lcl.h | 176 ++++++++++++++++ 7 files changed, 1214 insertions(+), 3 deletions(-) create mode 100644 fips/rand/fips_drbg_ctr.c create mode 100644 fips/rand/fips_drbg_hash.c create mode 100644 fips/rand/fips_drbg_lib.c create mode 100644 fips/rand/fips_rand_lcl.h diff --git a/CHANGES b/CHANGES index 6b803975ea..d3c0c27f7a 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ Changes between 1.0.1 and 1.1.0 [xx XXX xxxx] + *) Initial implementation of SP800-90 DRBGs for Hash and CTR. Not used by + anything, incomplete, subject to change and largely untested at present. + [Steve Henson] + *) Modify fipscanisteronly build option to only build the necessary object files by filtering FIPS_EX_OBJ through a perl script in crypto/Makefile. [Steve Henson] diff --git a/fips/rand/Makefile b/fips/rand/Makefile index ce08396254..580d6df777 100644 --- a/fips/rand/Makefile +++ b/fips/rand/Makefile @@ -22,13 +22,15 @@ TEST= fips_randtest.c fips_rngvs.c APPS= LIB=$(TOP)/libcrypto.a -LIBSRC=fips_rand.c fips_rand_selftest.c -LIBOBJ=fips_rand.o fips_rand_selftest.o +LIBSRC= fips_rand.c fips_rand_selftest.c \ + fips_drbg_lib.c fips_drbg_hash.c fips_drbg_ctr.c +LIBOBJ= fips_rand.o fips_rand_selftest.o \ + fips_drbg_lib.o fips_drbg_hash.o fips_drbg_ctr.o SRC= $(LIBSRC) EXHEADER= fips_rand.h -HEADER= $(EXHEADER) +HEADER= $(EXHEADER) fips_rand_lcl.h ALL= $(GENERAL) $(SRC) $(HEADER) diff --git a/fips/rand/fips_drbg_ctr.c b/fips/rand/fips_drbg_ctr.c new file mode 100644 index 0000000000..212bcf8df6 --- /dev/null +++ b/fips/rand/fips_drbg_ctr.c @@ -0,0 +1,419 @@ +/* fips/rand/fips_drbg_ctr.c */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project. + */ +/* ==================================================================== + * Copyright (c) 2011 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include "fips_rand_lcl.h" + +static void inc_128(DRBG_CTR_CTX *cctx) + { + int i; + unsigned char c; + unsigned char *p = cctx->V + 15; + for (i = 0; i < 16; i++) + { + c = *p; + c++; + *p = c; + if (c) + return; + p--; + } + } + +static void ctr_XOR(DRBG_CTR_CTX *cctx, const unsigned char *in, size_t inlen) + { + size_t i, n; + /* Any zero padding will have no effect on the result as we + * are XORing. So just process however much input we have. + */ + + if (!in || !inlen) + return; + + if (inlen < cctx->keylen) + n = inlen; + else + n = cctx->keylen; + + for (i = 0; i < n; i++) + cctx->K[i] ^= in[i]; + if (inlen <= cctx->keylen) + return; + + n = inlen - cctx->keylen; + /* Should never happen */ + if (n > 16) + n = 16; + for (i = 0; i < 16; i++) + cctx->V[i] ^= in[i + cctx->keylen]; + } + +/* Process a complete block using BCC algorithm of SPP 800-90 10.4.3 */ + +static void ctr_BCC_block(DRBG_CTR_CTX *cctx, unsigned char *out, + const unsigned char *in) + { + int i; + for (i = 0; i < 16; i++) + out[i] ^= in[i]; + AES_encrypt(out, out, &cctx->df_ks); +#if 0 +fprintf(stderr, "BCC in+out\n"); +BIO_dump_fp(stderr, in, 16); +BIO_dump_fp(stderr, out, 16); +#endif + } + +/* Handle several BCC operations for as much data as we need for K and X */ +static void ctr_BCC_blocks(DRBG_CTR_CTX *cctx, const unsigned char *in) + { + ctr_BCC_block(cctx, cctx->KX, in); + ctr_BCC_block(cctx, cctx->KX + 16, in); + if (cctx->keylen != 16) + ctr_BCC_block(cctx, cctx->KX + 32, in); + } +/* Initialise BCC blocks: these have the value 0,1,2 in leftmost positions: + * see 10.4.2 stage 7. + */ +static void ctr_BCC_init(DRBG_CTR_CTX *cctx) + { + memset(cctx->KX, 0, 48); + memset(cctx->bltmp, 0, 16); + ctr_BCC_block(cctx, cctx->KX, cctx->bltmp); + cctx->bltmp[3] = 1; + ctr_BCC_block(cctx, cctx->KX + 16, cctx->bltmp); + if (cctx->keylen != 16) + { + cctx->bltmp[3] = 2; + ctr_BCC_block(cctx, cctx->KX + 32, cctx->bltmp); + } + } + +/* Process several blocks into BCC algorithm, some possibly partial */ +static void ctr_BCC_update(DRBG_CTR_CTX *cctx, + const unsigned char *in, size_t inlen) + { + if (!in || !inlen) + return; + /* If we have partial block handle it first */ + if (cctx->bltmp_pos) + { + size_t left = 16 - cctx->bltmp_pos; + /* If we now have a complete block process it */ + if (inlen >= left) + { + memcpy(cctx->bltmp + cctx->bltmp_pos, in, left); + ctr_BCC_blocks(cctx, cctx->bltmp); + cctx->bltmp_pos = 0; + inlen -= left; + in += left; + } + } + /* Process zero or more complete blocks */ + while (inlen >= 16) + { + ctr_BCC_blocks(cctx, in); + in += 16; + inlen -= 16; + } + /* Copy any remaining partial block to the temporary buffer */ + if (inlen > 0) + { + memcpy(cctx->bltmp + cctx->bltmp_pos, in, inlen); + cctx->bltmp_pos += inlen; + } + } + +static void ctr_BCC_final(DRBG_CTR_CTX *cctx) + { + if (cctx->bltmp_pos) + { + memset(cctx->bltmp + cctx->bltmp_pos, 0, 16 - cctx->bltmp_pos); + ctr_BCC_blocks(cctx, cctx->bltmp); + } + } + +static void ctr_df(DRBG_CTR_CTX *cctx, + const unsigned char *in1, size_t in1len, + const unsigned char *in2, size_t in2len, + const unsigned char *in3, size_t in3len) + { + size_t inlen; + unsigned char *p = cctx->bltmp; + static unsigned char c80 = 0x80; + + ctr_BCC_init(cctx); + if (!in1) + in1len = 0; + if (!in2) + in2len = 0; + if (!in3) + in3len = 0; + inlen = in1len + in2len + in3len; + /* Initialise L||N in temporary block */ + *p++ = (inlen >> 24) & 0xff; + *p++ = (inlen >> 16) & 0xff; + *p++ = (inlen >> 8) & 0xff; + *p++ = inlen & 0xff; + /* NB keylen is at most 32 bytes */ + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p = (unsigned char)((cctx->keylen + 16) & 0xff); + cctx->bltmp_pos = 8; + ctr_BCC_update(cctx, in1, in1len); + ctr_BCC_update(cctx, in2, in2len); + ctr_BCC_update(cctx, in3, in3len); + ctr_BCC_update(cctx, &c80, 1); + ctr_BCC_final(cctx); + /* Set up key K */ + AES_set_encrypt_key(cctx->KX, cctx->keylen * 8, &cctx->df_kxks); + /* X follows key K */ + AES_encrypt(cctx->KX + cctx->keylen, cctx->KX, &cctx->df_kxks); + AES_encrypt(cctx->KX, cctx->KX + 16, &cctx->df_kxks); + if (cctx->keylen != 16) + AES_encrypt(cctx->KX + 16, cctx->KX + 32, &cctx->df_kxks); +#if 0 +fprintf(stderr, "Output of ctr_df:\n"); +BIO_dump_fp(stderr, cctx->KX, cctx->keylen + 16); +#endif + } + +/* NB the no-df Update in SP800-90 specifies a constant input length + * of seedlen, however other uses of this algorithm pad the input with + * zeroes if necessary and have up to two parameters XORed together, + * handle both cases in this function instead. + */ + +static void ctr_Update(DRBG_CTX *dctx, + const unsigned char *in1, size_t in1len, + const unsigned char *in2, size_t in2len, + const unsigned char *nonce, size_t noncelen) + { + DRBG_CTR_CTX *cctx = &dctx->d.ctr; + /* ks is already setup for correct key */ + inc_128(cctx); + AES_encrypt(cctx->V, cctx->K, &cctx->ks); + /* If keylen longer than 128 bits need extra encrypt */ + if (cctx->keylen != 16) + { + inc_128(cctx); + AES_encrypt(cctx->V, cctx->K + 16, &cctx->ks); + } + inc_128(cctx); + AES_encrypt(cctx->V, cctx->V, &cctx->ks); + /* If 192 bit key part of V is on end of K */ + if (cctx->keylen == 24) + { + memcpy(cctx->V + 8, cctx->V, 8); + memcpy(cctx->V, cctx->K + 24, 8); + } + + if (dctx->flags & DRBG_FLAG_CTR_USE_DF) + { + /* If no input reuse existing derived value */ + if (in1 || nonce || in2) + ctr_df(cctx, in1, in1len, nonce, noncelen, in2, in2len); + /* If this a reuse input in1len != 0 */ + if (in1len) + ctr_XOR(cctx, cctx->KX, dctx->seedlen); + } + else + { + ctr_XOR(cctx, in1, in1len); + ctr_XOR(cctx, in2, in2len); + } + + AES_set_encrypt_key(cctx->K, dctx->strength, &cctx->ks); +#if 0 +fprintf(stderr, "K+V after update is:\n"); +BIO_dump_fp(stderr, cctx->K, cctx->keylen); +BIO_dump_fp(stderr, cctx->V, 16); +#endif + } + +static int drbg_ctr_instantiate(DRBG_CTX *dctx, + const unsigned char *ent, size_t entlen, + const unsigned char *nonce, size_t noncelen, + const unsigned char *pers, size_t perslen) + { + DRBG_CTR_CTX *cctx = &dctx->d.ctr; + memset(cctx->K, 0, sizeof(cctx->K)); + memset(cctx->V, 0, sizeof(cctx->V)); + AES_set_encrypt_key(cctx->K, dctx->strength, &cctx->ks); + ctr_Update(dctx, ent, entlen, pers, perslen, nonce, noncelen); + return 1; + } + +static int drbg_ctr_reseed(DRBG_CTX *dctx, + const unsigned char *ent, size_t entlen, + const unsigned char *adin, size_t adinlen) + { + ctr_Update(dctx, ent, entlen, adin, adinlen, NULL, 0); + return 1; + } + +static int drbg_ctr_generate(DRBG_CTX *dctx, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adinlen) + { + DRBG_CTR_CTX *cctx = &dctx->d.ctr; + if (adin && adinlen) + { + ctr_Update(dctx, adin, adinlen, NULL, 0, NULL, 0); + /* This means we reuse derived value */ + if (dctx->flags & DRBG_FLAG_CTR_USE_DF) + { + adin = NULL; + adinlen = 1; + } + } + else + adinlen = 0; + + for (;;) + { + inc_128(cctx); + if (outlen < 16) + { + /* Use K as temp space as it will be updated */ + AES_encrypt(cctx->V, cctx->K, &cctx->ks); + memcpy(out, cctx->K, outlen); + break; + } + AES_encrypt(cctx->V, out, &cctx->ks); + out += 16; + outlen -= 16; + if (outlen == 0) + break; + } + + ctr_Update(dctx, adin, adinlen, NULL, 0, NULL, 0); + + return 1; + + } + +int fips_drbg_ctr_init(DRBG_CTX *dctx) + { + DRBG_CTR_CTX *cctx = &dctx->d.ctr; + + size_t keylen; + + switch (dctx->type) + { + case NID_aes_128_ctr: + keylen = 16; + + case NID_aes_192_ctr: + keylen = 24; + + case NID_aes_256_ctr: + keylen = 32; + + default: + return -2; + } + + dctx->instantiate = drbg_ctr_instantiate; + dctx->reseed = drbg_ctr_reseed; + dctx->generate = drbg_ctr_generate; + + + cctx->keylen = keylen; + dctx->strength = keylen * 8; + dctx->blocklength = 16; + dctx->seedlen = keylen + 16; + + if (dctx->flags & DRBG_FLAG_CTR_USE_DF) + { + /* df initialisation */ + static unsigned char df_key[32] = + { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + }; + /* Set key schedule for df_key */ + AES_set_encrypt_key(df_key, dctx->strength, &cctx->df_ks); + + dctx->min_entropy = dctx->seedlen; + dctx->max_entropy = dctx->seedlen; + /* Nonce not used */ + dctx->min_nonce = 0; + dctx->max_nonce = 0; + dctx->max_pers = dctx->seedlen; + dctx->max_adin = dctx->seedlen; + } + else + { + dctx->min_entropy = cctx->keylen; + dctx->max_entropy = DRBG_MAX_ENTROPY; + dctx->min_nonce = dctx->min_entropy / 2; + dctx->max_nonce = DRBG_MAX_NONCE; + dctx->max_pers = DRBG_MAX_LENGTH; + dctx->max_adin = DRBG_MAX_LENGTH; + } + + dctx->max_request = 1<<19; + dctx->reseed_counter = DRBG_MAX_LENGTH; + + return 1; + } diff --git a/fips/rand/fips_drbg_hash.c b/fips/rand/fips_drbg_hash.c new file mode 100644 index 0000000000..4dbcdb6a7b --- /dev/null +++ b/fips/rand/fips_drbg_hash.c @@ -0,0 +1,374 @@ +/* fips/rand/fips_drbg_hash.c */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project. + */ +/* ==================================================================== + * Copyright (c) 2011 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#define OPENSSL_FIPSAPI + +#include +#include +#include +#include +#include +#include +#include +#include "fips_rand_lcl.h" + +/* This is Hash_df from SP 800-90 10.4.1 */ + +static int hash_df(DRBG_CTX *dctx, unsigned char *out, + const unsigned char *in1, size_t in1len, + const unsigned char *in2, size_t in2len, + const unsigned char *in3, size_t in3len, + const unsigned char *in4, size_t in4len) + { + EVP_MD_CTX *mctx = &dctx->d.hash.mctx; + unsigned char *vtmp = dctx->d.hash.vtmp; + unsigned char tmp[6]; + /* Standard only ever needs seedlen bytes which is always less than + * maximum permitted so no need to check length. + */ + size_t outlen = dctx->seedlen; + tmp[0] = 1; + tmp[1] = ((outlen * 8) >> 24) & 0xff; + tmp[2] = ((outlen * 8) >> 16) & 0xff; + tmp[3] = ((outlen * 8) >> 8) & 0xff; + tmp[4] = (outlen * 8) & 0xff; + if (!in1) + { + tmp[5] = (unsigned char)in1len; + in1 = tmp + 5; + in1len = 1; + } + for (;;) + { + if (!FIPS_digestinit(mctx, dctx->d.hash.md)) + return 0; + if (!FIPS_digestupdate(mctx, tmp, 5)) + return 0; + if (in1 && !FIPS_digestupdate(mctx, in1, in1len)) + return 0; + if (in2 && !FIPS_digestupdate(mctx, in2, in2len)) + return 0; + if (in3 && !FIPS_digestupdate(mctx, in3, in3len)) + return 0; + if (in4 && !FIPS_digestupdate(mctx, in4, in4len)) + return 0; + if (outlen < dctx->blocklength) + { + if (!FIPS_digestfinal(mctx, vtmp, NULL)) + return 0; + memcpy(out, vtmp, outlen); + OPENSSL_cleanse(vtmp, dctx->blocklength); + return 1; + } + else if(!FIPS_digestfinal(mctx, out, NULL)) + return 0; + + outlen -= dctx->blocklength; + if (outlen == 0) + return 1; + tmp[0]++; + out += dctx->blocklength; + } + } + + +/* Add an unsigned buffer to the buf value, storing the result in buf. For + * this algorithm the length of input never exceeds the seed length. + */ + +static void ctx_add_buf(DRBG_CTX *dctx, unsigned char *buf, + unsigned char *in, size_t inlen) + { + size_t i = inlen; + const unsigned char *q; + unsigned char c, *p; + p = buf + dctx->seedlen; + q = in + inlen; + + OPENSSL_assert(i <= dctx->seedlen); + + /* Special case: zero length, just increment buffer */ + if (i) + c = 0; + else + c = 1; + + while (i) + { + int r; + p--; + q--; + r = *p + *q + c; + /* Carry */ + if (r > 0xff) + c = 1; + else + c = 0; + *p = r & 0xff; + i--; + } + + i = dctx->seedlen - inlen; + + /* If not adding whole buffer handle final carries */ + if (c && i) + { + do + { + p--; + c = *p; + c++; + *p = c; + if(c) + return; + } while(i--); + } + } + +/* Finalise and add hash to V */ + +static int ctx_add_md(DRBG_CTX *dctx) + { + if (!FIPS_digestfinal(&dctx->d.hash.mctx, dctx->d.hash.vtmp, NULL)) + return 0; + ctx_add_buf(dctx, dctx->d.hash.V, dctx->d.hash.vtmp, dctx->blocklength); + return 1; + } + +static int hash_gen(DRBG_CTX *dctx, unsigned char *out, size_t outlen) + { + DRBG_HASH_CTX *hctx = &dctx->d.hash; + if (outlen == 0) + return 1; + memcpy(hctx->vtmp, hctx->V, dctx->seedlen); + for(;;) + { + FIPS_digestinit(&hctx->mctx, hctx->md); + FIPS_digestupdate(&hctx->mctx, hctx->vtmp, dctx->seedlen); + if (outlen < dctx->blocklength) + { + FIPS_digestfinal(&hctx->mctx, hctx->vtmp, NULL); + memcpy(out, hctx->vtmp, outlen); + return 1; + } + FIPS_digestfinal(&hctx->mctx, out, NULL); + outlen -= dctx->blocklength; + if (outlen == 0) + return 1; + out += dctx->blocklength; + ctx_add_buf(dctx, hctx->vtmp, NULL, 0); + } + } + +static int drbg_hash_instantiate(DRBG_CTX *dctx, + const unsigned char *ent, size_t ent_len, + const unsigned char *nonce, size_t nonce_len, + const unsigned char *pstr, size_t pstr_len) + { + DRBG_HASH_CTX *hctx = &dctx->d.hash; + if (!hash_df(dctx, hctx->V, + ent, ent_len, nonce, nonce_len, pstr, pstr_len, + NULL, 0)) + return 0; + if (!hash_df(dctx, hctx->C, + NULL, 0, hctx->V, dctx->seedlen, + NULL, 0, NULL, 0)) + return 0; + +#ifdef HASH_DRBG_TRACE + fprintf(stderr, "V+C after instantiate:\n"); + hexprint(stderr, hctx->V, dctx->seedlen); + hexprint(stderr, hctx->C, dctx->seedlen); +#endif + return 1; + } + + +static int drbg_hash_reseed(DRBG_CTX *dctx, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len) + { + DRBG_HASH_CTX *hctx = &dctx->d.hash; + /* V about to be updated so use C as output instead */ + if (!hash_df(dctx, hctx->C, + NULL, 1, hctx->V, dctx->seedlen, + ent, ent_len, adin, adin_len)) + return 0; + memcpy(hctx->V, hctx->C, dctx->seedlen); + if (!hash_df(dctx, hctx->C, NULL, 0, + hctx->V, dctx->seedlen, NULL, 0, NULL, 0)) + return 0; +#ifdef HASH_DRBG_TRACE + fprintf(stderr, "V+C after reseed:\n"); + hexprint(stderr, hctx->V, dctx->seedlen); + hexprint(stderr, hctx->C, dctx->seedlen); +#endif + return 1; + } + +static int drbg_hash_generate(DRBG_CTX *dctx, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len) + { + DRBG_HASH_CTX *hctx = &dctx->d.hash; + EVP_MD_CTX *mctx = &hctx->mctx; + unsigned char tmp[4]; + if (adin && adin_len) + { + tmp[0] = 2; + if (!FIPS_digestinit(mctx, hctx->md)) + return 0; + if (!EVP_DigestUpdate(mctx, tmp, 1)) + return 0; + if (!EVP_DigestUpdate(mctx, hctx->V, dctx->seedlen)) + return 0; + if (!EVP_DigestUpdate(mctx, adin, adin_len)) + return 0; + if (!ctx_add_md(dctx)) + return 0; + } + if (!hash_gen(dctx, out, outlen)) + return 0; + + tmp[0] = 3; + if (!FIPS_digestinit(mctx, hctx->md)) + return 0; + if (!EVP_DigestUpdate(mctx, tmp, 1)) + return 0; + if (!EVP_DigestUpdate(mctx, hctx->V, dctx->seedlen)) + return 0; + + if (!ctx_add_md(dctx)) + return 0; + + ctx_add_buf(dctx, hctx->V, hctx->C, dctx->seedlen); + + tmp[0] = (dctx->reseed_counter >> 24) & 0xff; + tmp[1] = (dctx->reseed_counter >> 16) & 0xff; + tmp[2] = (dctx->reseed_counter >> 8) & 0xff; + tmp[3] = dctx->reseed_counter & 0xff; + ctx_add_buf(dctx, hctx->V, tmp, 4); +#ifdef HASH_DRBG_TRACE + fprintf(stderr, "V+C after generate:\n"); + hexprint(stderr, hctx->V, dctx->seedlen); + hexprint(stderr, hctx->C, dctx->seedlen); +#endif + return 1; + } + +int fips_drbg_hash_init(DRBG_CTX *dctx) + { + const EVP_MD *md; + DRBG_HASH_CTX *hctx = &dctx->d.hash; + switch (dctx->type) + { + case NID_sha1: + md = EVP_sha1(); + dctx->strength = 128; + break; + + case NID_sha224: + md = EVP_sha224(); + dctx->strength = 192; + break; + + case NID_sha256: + md = EVP_sha256(); + dctx->strength = 256; + break; + + case NID_sha384: + md = EVP_sha384(); + dctx->strength = 256; + break; + + case NID_sha512: + md = EVP_sha512(); + dctx->strength = 256; + break; + + default: + return -2; + break; + + } + + dctx->instantiate = drbg_hash_instantiate; + dctx->reseed = drbg_hash_reseed; + dctx->generate = drbg_hash_generate; + + dctx->d.hash.md = md; + EVP_MD_CTX_init(&hctx->mctx); + + /* These are taken from SP 800-90 10.1 table 2 */ + + dctx->blocklength = M_EVP_MD_size(md); + if (dctx->blocklength > 32) + dctx->seedlen = 111; + else + dctx->seedlen = 55; + + + dctx->min_entropy = dctx->strength / 8; + dctx->max_entropy = DRBG_MAX_ENTROPY; + + dctx->min_nonce = dctx->min_entropy / 2; + dctx->max_nonce = DRBG_MAX_NONCE; + + dctx->max_pers = DRBG_MAX_LENGTH; + dctx->max_adin = DRBG_MAX_LENGTH; + + dctx->max_request = 1<<19; + + return 1; + } diff --git a/fips/rand/fips_drbg_lib.c b/fips/rand/fips_drbg_lib.c new file mode 100644 index 0000000000..da9fb37dd9 --- /dev/null +++ b/fips/rand/fips_drbg_lib.c @@ -0,0 +1,226 @@ +/* fips/rand/fips_drbg_lib.c */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project. + */ +/* ==================================================================== + * Copyright (c) 2011 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#define OPENSSL_FIPSAPI + +#include +#include +#include +#include +#include +#include "fips_rand_lcl.h" + +/* Support framework for SP800-90 DRBGs */ + +DRBG_CTX *FIPS_drbg_new(int type, unsigned int flags) + { + int rv; + DRBG_CTX *dctx; + dctx = OPENSSL_malloc(sizeof(DRBG_CTX)); + memset(dctx, 0, sizeof(DRBG_CTX)); + dctx->status = DRBG_STATUS_UNINITIALISED; + dctx->flags = flags; + dctx->type = type; + rv = fips_drbg_hash_init(dctx); + if (rv == -2) + rv = fips_drbg_ctr_init(dctx); + if (rv <= 0) + { + /* Fatal: cannot initialiase DRBG */ + goto err; + } + + return dctx; + + err: + if (dctx) + OPENSSL_free(dctx); + return NULL; + } + +int FIPS_drbg_instantiate(DRBG_CTX *dctx, + int strength, + const unsigned char *pers, size_t perslen) + { + size_t entlen, noncelen; + + if (perslen > dctx->max_pers) + return 0; + + if (dctx->status != DRBG_STATUS_UNINITIALISED) + { + /* error */ + return 0; + } + + dctx->status = DRBG_STATUS_ERROR; + + entlen = dctx->get_entropy(dctx, dctx->entropy, dctx->strength, + dctx->min_entropy, dctx->max_entropy); + + if (entlen < dctx->min_entropy || entlen > dctx->max_entropy) + goto end; + + if (dctx->max_nonce > 0) + { + + noncelen = dctx->get_nonce(dctx, dctx->nonce, + dctx->strength / 2, + dctx->min_nonce, dctx->max_nonce); + + if (noncelen < dctx->min_nonce || noncelen > dctx->max_nonce) + goto end; + + } + else + noncelen = 0; + + if (!dctx->instantiate(dctx, + dctx->entropy, entlen, + dctx->nonce, noncelen, + pers, perslen)) + goto end; + + + dctx->status = DRBG_STATUS_READY; + dctx->reseed_counter = 1; + + end: + + OPENSSL_cleanse(dctx->entropy, sizeof(dctx->entropy)); + OPENSSL_cleanse(dctx->nonce, sizeof(dctx->nonce)); + + if (dctx->status == DRBG_STATUS_READY) + return 1; + + return 0; + + } + +int FIPS_drbg_reseed(DRBG_CTX *dctx, + const unsigned char *adin, size_t adinlen) + { + size_t entlen; + if (dctx->status != DRBG_STATUS_READY + && dctx->status != DRBG_STATUS_RESEED) + { + /* error */ + return 0; + } + + if (!adin) + adinlen = 0; + else if (adinlen > dctx->max_adin); + { + /* error */ + return 0; + } + + dctx->status = DRBG_STATUS_ERROR; + + entlen = dctx->get_entropy(dctx, dctx->entropy, dctx->strength, + dctx->min_entropy, dctx->max_entropy); + + if (entlen < dctx->min_entropy || entlen > dctx->max_entropy) + goto end; + + if (!dctx->reseed(dctx, dctx->entropy, entlen, adin, adinlen)) + goto end; + + dctx->status = DRBG_STATUS_READY; + dctx->reseed_counter = 1; + end: + OPENSSL_cleanse(dctx->entropy, sizeof(dctx->entropy)); + if (dctx->status == DRBG_STATUS_READY) + return 1; + return 0; + } + + +int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, + int prediction_resistance, + const unsigned char *adin, size_t adinlen) + { + if (outlen > dctx->max_request) + { + /* Too large */ + return 0; + } + if (dctx->status == DRBG_STATUS_RESEED || prediction_resistance) + { + if (!FIPS_drbg_reseed(dctx, adin, adinlen)) + return 0; + adin = NULL; + adinlen = 0; + } + if (dctx->status != DRBG_STATUS_READY) + { + /* Bad error */ + return 0; + } + if (!dctx->generate(dctx, out, outlen, adin, adinlen)) + { + /* Bad error */ + dctx->status = DRBG_STATUS_ERROR; + return 0; + } + if (dctx->reseed_counter > dctx->reseed_interval) + dctx->status = DRBG_STATUS_RESEED; + else + dctx->reseed_counter++; + + return 1; + } + + + diff --git a/fips/rand/fips_rand.h b/fips/rand/fips_rand.h index 28d7610f7c..27eb8f7c2b 100644 --- a/fips/rand/fips_rand.h +++ b/fips/rand/fips_rand.h @@ -70,6 +70,16 @@ int FIPS_rand_status(void); const RAND_METHOD *FIPS_rand_method(void); +typedef struct drbg_ctx_st DRBG_CTX; + +DRBG_CTX *FIPS_drbg_new(int type, unsigned int flags); +int FIPS_drbg_instantiate(DRBG_CTX *dctx, int strength, + const unsigned char *pers, size_t perslen); +int FIPS_drbg_reseed(DRBG_CTX *dctx, const unsigned char *adin, size_t adinlen); +int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, + int prediction_resistance, + const unsigned char *adin, size_t adinlen); + #ifdef __cplusplus } #endif diff --git a/fips/rand/fips_rand_lcl.h b/fips/rand/fips_rand_lcl.h new file mode 100644 index 0000000000..72820bf784 --- /dev/null +++ b/fips/rand/fips_rand_lcl.h @@ -0,0 +1,176 @@ +/* fips/rand/fips_rand_lcl.h */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project. + */ +/* ==================================================================== + * Copyright (c) 2011 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +typedef struct drbg_hash_ctx_st DRBG_HASH_CTX; +typedef struct drbg_ctr_ctx_st DRBG_CTR_CTX; + +/* 888 bits from 10.1 table 2 */ +#define HASH_PRNG_MAX_SEEDLEN 111 + +struct drbg_hash_ctx_st + { + const EVP_MD *md; + EVP_MD_CTX mctx; + unsigned char V[HASH_PRNG_MAX_SEEDLEN]; + unsigned char C[HASH_PRNG_MAX_SEEDLEN]; + /* Temporary value storage: should always exceed max digest length */ + unsigned char vtmp[HASH_PRNG_MAX_SEEDLEN]; + }; + +struct drbg_ctr_ctx_st + { + AES_KEY ks; + size_t keylen; + unsigned char K[32]; + unsigned char V[16]; + /* Temp variables used by derivation function */ + AES_KEY df_ks; + AES_KEY df_kxks; + /* Temporary block storage used by ctr_df */ + unsigned char bltmp[16]; + size_t bltmp_pos; + unsigned char KX[48]; + }; + +/* DRBG flags */ + +/* Enable prediction resistance */ +#define DRBG_FLAG_PREDICTION_RESISTANCE 0x1 +/* CTR only: use derivation function */ +#define DRBG_FLAG_CTR_USE_DF 0x2 +/* PRNG is in test state */ +#define DRBG_FLAG_TEST 0x4 + +/* DRBG status values */ +/* not initialised */ +#define DRBG_STATUS_UNINITIALISED 0 +/* ok and ready to generate random bits */ +#define DRBG_STATUS_READY 1 +/* reseed required */ +#define DRBG_STATUS_RESEED 2 +/* fatal error condition */ +#define DRBG_STATUS_ERROR 3 + +/* Maximum values for temp entropy and nonce */ +#define DRBG_MAX_ENTROPY 1024 +#define DRBG_MAX_NONCE 1024 + +/* A default maximum length: larger than any reasonable value used in pratice */ + +#define DRBG_MAX_LENGTH 0x7fffffff + +/* DRBG context structure */ + +struct drbg_ctx_st + { + /* First types common to all implementations */ + /* DRBG type: a NID for the underlying algorithm */ + int type; + /* Various flags */ + unsigned int flags; + + /* The following parameters are setup by mechanism drbg_init() call */ + int strength; + size_t blocklength; + size_t max_request; + + size_t min_entropy, max_entropy; + size_t min_nonce, max_nonce; + size_t max_pers, max_adin; + unsigned int reseed_counter; + unsigned int reseed_interval; + size_t seedlen; + int status; + /* Application data: typically used by test get_entropy */ + void *app_data; + /* Implementation specific structures */ + union + { + DRBG_HASH_CTX hash; + DRBG_CTR_CTX ctr; + } d; + /* Initialiase PRNG and setup callbacks below */ + int (*init)(DRBG_CTX *ctx, int nid, int security, unsigned int flags); + /* Intantiate PRNG */ + int (*instantiate)(DRBG_CTX *ctx, + const unsigned char *ent, size_t entlen, + const unsigned char *nonce, size_t noncelen, + const unsigned char *pers, size_t perslen); + /* reseed */ + int (*reseed)(DRBG_CTX *ctx, + const unsigned char *ent, size_t entlen, + const unsigned char *adin, size_t adinlen); + /* generat output */ + int (*generate)(DRBG_CTX *ctx, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adinlen); + /* uninstantiate */ + int (*uninstantiate)(DRBG_CTX *ctx); + + unsigned char entropy[DRBG_MAX_ENTROPY]; + + /* entropy gathering function */ + size_t (*get_entropy)(DRBG_CTX *ctx, unsigned char *out, + int entropy, size_t min_len, size_t max_len); + + unsigned char nonce[DRBG_MAX_NONCE]; + + /* nonce gathering function */ + size_t (*get_nonce)(DRBG_CTX *ctx, unsigned char *out, + int entropy, size_t min_len, size_t max_len); + + }; + + +int fips_drbg_ctr_init(DRBG_CTX *dctx); +int fips_drbg_hash_init(DRBG_CTX *dctx); -- 2.25.1