/* Support framework for SP800-90 DRBGs */
-static int fips_drbg_init(DRBG_CTX *dctx, int type, unsigned int flags)
+int FIPS_drbg_init(DRBG_CTX *dctx, int type, unsigned int flags)
{
int rv;
memset(dctx, 0, sizeof(DRBG_CTX));
if (rv == -2)
rv = fips_drbg_ctr_init(dctx);
+ if (rv <= 0)
+ {
+ if (rv == -2)
+ FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_UNSUPPORTED_DRBG_TYPE);
+ else
+ FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_ERROR_INITIALISING_DRBG);
+ }
+
return rv;
}
FIPSerr(FIPS_F_FIPS_DRBG_NEW, ERR_R_MALLOC_FAILURE);
return NULL;
}
- rv = fips_drbg_init(dctx, type, flags);
+ if (type == 0)
+ return dctx;
+ rv = FIPS_drbg_init(dctx, type, flags);
- if (rv <= 0)
+ if (FIPS_drbg_init(dctx, type, flags) <= 0)
{
- if (rv == -2)
- FIPSerr(FIPS_F_FIPS_DRBG_NEW, FIPS_R_UNSUPPORTED_DRBG_TYPE);
- else
- FIPSerr(FIPS_F_FIPS_DRBG_NEW, FIPS_R_ERROR_INITIALISING_DRBG);
-
OPENSSL_free(dctx);
return NULL;
}
+
return dctx;
}
void FIPS_drbg_free(DRBG_CTX *dctx)
{
- dctx->uninstantiate(dctx);
+ if (dctx->uninstantiate)
+ dctx->uninstantiate(dctx);
OPENSSL_cleanse(dctx, sizeof(DRBG_CTX));
OPENSSL_free(dctx);
}
int FIPS_drbg_instantiate(DRBG_CTX *dctx,
- int strength,
const unsigned char *pers, size_t perslen)
{
- size_t entlen, noncelen;
+ size_t entlen = 0, noncelen = 0;
+ unsigned char *nonce = NULL, *entropy = NULL;
#if 0
/* Put here so error script picks them up */
dctx->status = DRBG_STATUS_ERROR;
- entlen = dctx->get_entropy(dctx, dctx->entropy, dctx->strength,
+ entlen = dctx->get_entropy(dctx, &entropy, dctx->strength,
dctx->min_entropy, dctx->max_entropy);
if (entlen < dctx->min_entropy || entlen > dctx->max_entropy)
if (dctx->max_nonce > 0)
{
-
- noncelen = dctx->get_nonce(dctx, dctx->nonce,
+ noncelen = dctx->get_nonce(dctx, &nonce,
dctx->strength / 2,
dctx->min_nonce, dctx->max_nonce);
}
}
- else
- noncelen = 0;
if (!dctx->instantiate(dctx,
- dctx->entropy, entlen,
- dctx->nonce, noncelen,
+ entropy, entlen,
+ nonce, noncelen,
pers, perslen))
{
r = FIPS_R_ERROR_INSTANTIATING_DRBG;
dctx->status = DRBG_STATUS_READY;
dctx->reseed_counter = 1;
- /* Initial test value for reseed interval */
- dctx->reseed_interval = 1<<24;
end:
- OPENSSL_cleanse(dctx->entropy, sizeof(dctx->entropy));
- OPENSSL_cleanse(dctx->nonce, sizeof(dctx->nonce));
+ if (entropy && dctx->cleanup_entropy)
+ dctx->cleanup_entropy(dctx, entropy, entlen);
+
+ if (nonce && dctx->cleanup_nonce)
+ dctx->cleanup_nonce(dctx, nonce, noncelen);
if (dctx->status == DRBG_STATUS_READY)
return 1;
- if (r && !(dctx->flags & DRBG_FLAG_TEST))
+ if (r && !(dctx->flags & DRBG_FLAG_NOERR))
FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, r);
return 0;
int FIPS_drbg_reseed(DRBG_CTX *dctx,
const unsigned char *adin, size_t adinlen)
{
+ unsigned char *entropy = NULL;
size_t entlen;
int r = 0;
dctx->status = DRBG_STATUS_ERROR;
- entlen = dctx->get_entropy(dctx, dctx->entropy, dctx->strength,
+ entlen = dctx->get_entropy(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))
+ if (!dctx->reseed(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 (entropy && dctx->cleanup_entropy)
+ dctx->cleanup_entropy(dctx, entropy, entlen);
if (dctx->status == DRBG_STATUS_READY)
return 1;
- if (r && !(dctx->flags & DRBG_FLAG_TEST))
+ if (r && !(dctx->flags & DRBG_FLAG_NOERR))
FIPSerr(FIPS_F_FIPS_DRBG_RESEED, r);
return 0;
}
-int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen,
- int prediction_resistance,
+static int fips_drbg_generate_internal(DRBG_CTX *dctx,
+ unsigned char *out, size_t outlen,
+ int strength, int prediction_resistance,
const unsigned char *adin, size_t adinlen)
{
int r = 0;
r = FIPS_R_REQUEST_TOO_LARGE_FOR_DRBG;
return 0;
}
+
+ if (strength > dctx->strength)
+ {
+ r = FIPS_R_INSUFFICIENT_SECURITY_STRENGTH;
+ goto end;
+ }
+
if (dctx->status == DRBG_STATUS_RESEED || prediction_resistance)
{
if (!FIPS_drbg_reseed(dctx, adin, adinlen))
dctx->status = DRBG_STATUS_ERROR;
goto end;
}
- if (dctx->reseed_counter > dctx->reseed_interval)
+ if (dctx->reseed_counter >= dctx->reseed_interval)
dctx->status = DRBG_STATUS_RESEED;
else
dctx->reseed_counter++;
end:
if (r)
{
- if (!(dctx->flags & DRBG_FLAG_TEST))
- FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, r);
+ if (!(dctx->flags & DRBG_FLAG_NOERR))
+ FIPSerr(FIPS_F_FIPS_DRBG_GENERATE_INTERNAL, r);
return 0;
}
return 1;
}
+/* external generate function: incorporates continuous RNG test if not
+ * in test mode.
+ */
+
+int FIPS_drbg_generate(DRBG_CTX *dctx,
+ unsigned char *out, size_t outlen,
+ int strength, int prediction_resistance,
+ const unsigned char *adin, size_t adinlen)
+ {
+ unsigned char tmp[16], *pout;
+ size_t poutlen;
+ /* If test mode don't run continuous RNG test */
+ if (dctx->flags & DRBG_FLAG_TEST)
+ {
+ return fips_drbg_generate_internal(dctx, out, outlen,
+ strength,
+ prediction_resistance,
+ adin, adinlen);
+ }
+ /* If this is the first call generate block and save buffer */
+ if (!dctx->lb_valid)
+ {
+ if (!fips_drbg_generate_internal(dctx, dctx->lb, 16,
+ strength, prediction_resistance,
+ adin, adinlen))
+ return 0;
+ dctx->lb_valid = 1;
+ }
+
+ /* If request less that 16 bytes request 16 in temp buffer */
+
+ if (outlen < 16)
+ {
+ pout = tmp;
+ poutlen = 16;
+ }
+ else
+ {
+ pout = out;
+ poutlen = outlen;
+ }
+
+ /* Generate data */
+ if (!fips_drbg_generate_internal(dctx, pout, poutlen,
+ strength, prediction_resistance,
+ adin, adinlen))
+ return 0;
+ /* Compare to last block for continuous PRNG test */
+ if (!memcmp(pout, dctx->lb, 16))
+ {
+ FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, FIPS_R_DRBG_STUCK);
+ return 0;
+ }
+ /* Update last block */
+ memcpy(dctx->lb, pout, 16);
+ /* Copy to output buffer if needed */
+ if (outlen < 16)
+ memcpy(out, pout, outlen);
+
+ return 1;
+
+ }
+
int FIPS_drbg_uninstantiate(DRBG_CTX *dctx)
{
- int save_type, save_flags, rv;
- save_type = dctx->type;
- save_flags = dctx->flags;
- rv = dctx->uninstantiate(dctx);
- OPENSSL_cleanse(dctx, sizeof(DRBG_CTX));
+ int rv;
+ if (!dctx->uninstantiate)
+ rv = 1;
+ else
+ rv = dctx->uninstantiate(dctx);
+ /* Although we'd like to cleanse here we can't because we have to
+ * test the uninstantiate really zeroes the data.
+ */
+ memset(dctx, 0, sizeof(DRBG_CTX));
/* If method has problems uninstantiating, return error */
- if (rv <= 0)
- return rv;
- return fips_drbg_init(dctx, save_type, save_flags);
+ return rv;
}
-int FIPS_drbg_set_test_mode(DRBG_CTX *dctx,
- size_t (*get_entropy)(DRBG_CTX *ctx, unsigned char *out,
+int FIPS_drbg_set_callbacks(DRBG_CTX *dctx,
+ size_t (*get_entropy)(DRBG_CTX *ctx, unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len),
+ void (*cleanup_entropy)(DRBG_CTX *ctx, unsigned char *out, size_t olen),
+ size_t (*get_nonce)(DRBG_CTX *ctx, unsigned char **pout,
int entropy, size_t min_len, size_t max_len),
- size_t (*get_nonce)(DRBG_CTX *ctx, unsigned char *out,
- int entropy, size_t min_len, size_t max_len))
+ void (*cleanup_nonce)(DRBG_CTX *ctx, unsigned char *out, size_t olen))
{
if (dctx->status != DRBG_STATUS_UNINITIALISED)
return 0;
- dctx->flags |= DRBG_FLAG_TEST;
dctx->get_entropy = get_entropy;
+ dctx->cleanup_entropy = cleanup_entropy;
dctx->get_nonce = get_nonce;
+ dctx->cleanup_nonce = cleanup_nonce;
return 1;
}
{
return dctx->blocklength;
}
+
+int FIPS_drbg_get_strength(DRBG_CTX *dctx)
+ {
+ return dctx->strength;
+ }