summaryrefslogtreecommitdiffstats
path: root/fips
diff options
context:
space:
mode:
authorDr. Stephen Henson <steve@openssl.org>2011-03-04 18:00:21 +0000
committerDr. Stephen Henson <steve@openssl.org>2011-03-04 18:00:21 +0000
commit591cbfae3cfcb26b45c523499847d3dbe53a44e4 (patch)
treea9d7f9ecb77975864e51264848939a5f9b611e58 /fips
parenta000759a5c5473ccd8e060f9f428424409e7c2df (diff)
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?
Diffstat (limited to 'fips')
-rw-r--r--fips/rand/Makefile8
-rw-r--r--fips/rand/fips_drbg_ctr.c419
-rw-r--r--fips/rand/fips_drbg_hash.c374
-rw-r--r--fips/rand/fips_drbg_lib.c226
-rw-r--r--fips/rand/fips_rand.h10
-rw-r--r--fips/rand/fips_rand_lcl.h176
6 files changed, 1210 insertions, 3 deletions
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 <stdlib.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/aes.h>
+#include <openssl/fips.h>
+#include <openssl/fips_rand.h>
+#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 <stdlib.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/aes.h>
+#include <openssl/fips.h>
+#include <openssl/fips_rand.h>
+#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 <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/aes.h>
+#include <openssl/fips_rand.h>
+#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);