#include <openssl/fips_rand.h>
#include "fips_rand_lcl.h"
+static int drbg_hmac_update(DRBG_CTX *dctx,
+ const unsigned char *in1, size_t in1len,
+ const unsigned char *in2, size_t in2len,
+ const unsigned char *in3, size_t in3len
+ )
+ {
+ static unsigned char c0 = 0, c1 = 1;
+ DRBG_HMAC_CTX *hmac = &dctx->d.hmac;
+ HMAC_CTX *hctx = &hmac->hctx;
+
+ if (!HMAC_Init_ex(hctx, hmac->K, dctx->blocklength, hmac->md, NULL))
+ return 0;
+ if (!HMAC_Update(hctx, hmac->V, dctx->blocklength))
+ return 0;
+ if (!HMAC_Update(hctx, &c0, 1))
+ return 0;
+ if (in1len && !HMAC_Update(hctx, in1, in1len))
+ return 0;
+ if (in2len && !HMAC_Update(hctx, in2, in2len))
+ return 0;
+ if (in3len && !HMAC_Update(hctx, in3, in3len))
+ return 0;
+
+ if (!HMAC_Final(hctx, hmac->K, NULL))
+ return 0;
+
+ if (!HMAC_Init_ex(hctx, hmac->K, dctx->blocklength, hmac->md, NULL))
+ return 0;
+ if (!HMAC_Update(hctx, hmac->V, dctx->blocklength))
+ return 0;
+
+ if (!HMAC_Final(hctx, hmac->V, NULL))
+ return 0;
+
+ if (!in1len && !in2len && !in3len)
+ return 1;
+
+ if (!HMAC_Init_ex(hctx, hmac->K, dctx->blocklength, hmac->md, NULL))
+ return 0;
+ if (!HMAC_Update(hctx, hmac->V, dctx->blocklength))
+ return 0;
+ if (!HMAC_Update(hctx, &c1, 1))
+ return 0;
+ if (in1len && !HMAC_Update(hctx, in1, in1len))
+ return 0;
+ if (in2len && !HMAC_Update(hctx, in2, in2len))
+ return 0;
+ if (in3len && !HMAC_Update(hctx, in3, in3len))
+ return 0;
+
+ if (!HMAC_Final(hctx, hmac->K, NULL))
+ return 0;
+
+ if (!HMAC_Init_ex(hctx, hmac->K, dctx->blocklength, hmac->md, NULL))
+ return 0;
+ if (!HMAC_Update(hctx, hmac->V, dctx->blocklength))
+ return 0;
+
+ if (!HMAC_Final(hctx, hmac->V, NULL))
+ return 0;
+
+ return 1;
+
+ }
+
+static int drbg_hmac_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_HMAC_CTX *hmac = &dctx->d.hmac;
+ memset(hmac->K, 0, dctx->blocklength);
+ memset(hmac->V, 1, dctx->blocklength);
+ if (!drbg_hmac_update(dctx,
+ ent, ent_len, nonce, nonce_len, pstr, pstr_len))
+ return 0;
+
+#ifdef HMAC_DRBG_TRACE
+ fprintf(stderr, "K+V after instantiate:\n");
+ hexprint(stderr, hmac->K, hmac->blocklength);
+ hexprint(stderr, hmac->V, hmac->blocklength);
+#endif
+ return 1;
+ }
+
+static int drbg_hmac_reseed(DRBG_CTX *dctx,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len)
+ {
+ if (!drbg_hmac_update(dctx,
+ ent, ent_len, adin, adin_len, NULL, 0))
+ return 0;
+
+#ifdef HMAC_DRBG_TRACE
+ {
+ DRBG_HMAC_CTX *hmac = &dctx->d.hmac;
+ fprintf(stderr, "K+V after reseed:\n");
+ hexprint(stderr, hmac->K, hmac->blocklength);
+ hexprint(stderr, hmac->V, hmac->blocklength);
+ }
+#endif
+ return 1;
+ }
+
+static int drbg_hmac_generate(DRBG_CTX *dctx,
+ unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len)
+ {
+ DRBG_HMAC_CTX *hmac = &dctx->d.hmac;
+ HMAC_CTX *hctx = &hmac->hctx;
+ const unsigned char *Vtmp = hmac->V;
+ if (adin_len && !drbg_hmac_update(dctx, adin, adin_len,
+ NULL, 0, NULL, 0))
+ return 0;
+ for (;;)
+ {
+ if (!HMAC_Init_ex(hctx, hmac->K, dctx->blocklength,
+ hmac->md, NULL))
+ return 0;
+ if (!HMAC_Update(hctx, Vtmp, dctx->blocklength))
+ return 0;
+ if (!(dctx->xflags & DRBG_FLAG_TEST) && !dctx->lb_valid)
+ {
+ if (!HMAC_Final(hctx, dctx->lb, NULL))
+ return 0;
+ dctx->lb_valid = 1;
+ Vtmp = dctx->lb;
+ continue;
+ }
+ else if (outlen > dctx->blocklength)
+ {
+ if (!HMAC_Final(hctx, out, NULL))
+ return 0;
+ if (!fips_drbg_cprng_test(dctx, out))
+ return 0;
+ Vtmp = out;
+ }
+ else
+ {
+ if (!HMAC_Final(hctx, hmac->V, NULL))
+ return 0;
+ if (!fips_drbg_cprng_test(dctx, hmac->V))
+ return 0;
+ memcpy(out, hmac->V, outlen);
+ break;
+ }
+ out += dctx->blocklength;
+ outlen -= dctx->blocklength;
+ }
+ if (!drbg_hmac_update(dctx, adin, adin_len, NULL, 0, NULL, 0))
+ return 0;
+
+ return 1;
+ }
+
+static int drbg_hmac_uninstantiate(DRBG_CTX *dctx)
+ {
+ HMAC_CTX_cleanup(&dctx->d.hmac.hctx);
+ OPENSSL_cleanse(&dctx->d.hmac, sizeof(DRBG_HMAC_CTX));
+ return 1;
+ }
+
int fips_drbg_hmac_init(DRBG_CTX *dctx)
{
- return -2;
+ const EVP_MD *md = NULL;
+ DRBG_HMAC_CTX *hctx = &dctx->d.hmac;
+ dctx->strength = 256;
+ switch (dctx->type)
+ {
+ case NID_hmacWithSHA1:
+ md = EVP_sha1();
+ dctx->strength = 128;
+ break;
+
+ case NID_hmacWithSHA224:
+ md = EVP_sha224();
+ dctx->strength = 192;
+ break;
+
+ case NID_hmacWithSHA256:
+ md = EVP_sha256();
+ break;
+
+ case NID_hmacWithSHA384:
+ md = EVP_sha384();
+ break;
+
+ case NID_hmacWithSHA512:
+ md = EVP_sha512();
+ break;
+
+ default:
+ dctx->strength = 0;
+ return -2;
+ }
+ dctx->instantiate = drbg_hmac_instantiate;
+ dctx->reseed = drbg_hmac_reseed;
+ dctx->generate = drbg_hmac_generate;
+ dctx->uninstantiate = drbg_hmac_uninstantiate;
+ HMAC_CTX_init(&hctx->hctx);
+ hctx->md = md;
+ dctx->blocklength = M_EVP_MD_size(md);
+ dctx->seedlen = M_EVP_MD_size(md);
+
+ dctx->min_entropy = dctx->strength / 8;
+ dctx->max_entropy = DRBG_MAX_LENGTH;
+
+ dctx->min_nonce = dctx->min_entropy / 2;
+ dctx->max_nonce = DRBG_MAX_LENGTH;
+
+ dctx->max_pers = DRBG_MAX_LENGTH;
+ dctx->max_adin = DRBG_MAX_LENGTH;
+
+ dctx->max_request = 1<<16;
+ dctx->reseed_interval = 1<<24;
+
+ return 1;
}