ARMmbed

HMAC DRBG incorrect output

I’m trying to implement HMAC DRBG and I’m using CTR_DRBG as a reference.

My calls are as follows:

mbedtls_hmac_drbg_seed( &hmac_ctx, md_info, mbedtls_test_entropy_func, entropy_inputs, nonce_pers, strlen(nonce_pers) );
mbedtls_hmac_drbg_set_prediction_resistance(&hmac_ctx, MBEDTLS_CTR_DRBG_PR_ON );
mbedtls_hmac_drbg_random_with_add(&hmac_ctx, result, resultLen, addInput1, addInputLength);
mbedtls_hmac_drbg_random_with_add(&hmac_ctx, result, resultLen, addInput2, addInputLength);

I’ve printed the offset of index used for mbedtls_test_entropy_func() after every call. Here are the values:

**For SHA-1:**
md size = 20
Before mbedtls_hmac_drbg_seed() : 0
After mbedtls_hmac_drbg_seed() : 24
After mbedtls_hmac_drbg_random_with_add 1() : 40

========================================

**For SHA-256:**
md size = 32
Before mbedtls_hmac_drbg_seed() : 0
After mbedtls_hmac_drbg_seed() : 48
After mbedtls_hmac_drbg_random_with_add 1() : 80

Within 1 request file I’ve tests with SHA-1. SHA-256 and SHA-512. So I should be able to test any variant of SHA.
The #defines in config file are shown below:

#define MBEDTLS_SHA1_C
#define MBEDTLS_ENTROPY_FORCE_SHA256
#define MBEDTLS_SHA512_C

The offsets don’t seem right to me. I’m using the same mbedtls_test_entropy_func() as I used for CTR_DRBG ( I’ve replicated mbedtls_test_entropy_func () from ctr_drbg_validate_internal ()).

Hi @athorath

Thank you for your question!

Are you using the hmac_drbg as reference for your tests? If so, note that the entropy_context used should also change.

The entropy length in hmac_drbg is not identical as in ctr_drbg.

As you can see from the code, calling mbedtls_hmac_drbg_seed() will have the following definitions:

    /*
     * See SP800-57 5.6.1 (p. 65-66) for the security strength provided by
     * each hash function, then according to SP800-90A rev1 10.1 table 2,
     * min_entropy_len (in bits) is security_strength.
     *
     * (This also matches the sizes used in the NIST test vectors.)
     */
    ctx->entropy_len = md_size <= 20 ? 16 : /* 160-bits hash -> 128 bits */
                       md_size <= 28 ? 24 : /* 224-bits hash -> 192 bits */
                       32;  /* better (256+) -> 256 bits */

Which calls hmac_drbg_reseed_core() with use_nonce = 1 , so it calls f_entropy twice:

    if( ( ret = ctx->f_entropy( ctx->p_entropy,
                                seed, ctx->entropy_len ) ) != 0 )

and

     * from the entropy source. See Sect 8.6.7 in SP800-90A. */
    if( use_nonce )
    {
        /* Note: We don't merge the two calls to f_entropy() in order
         *       to avoid requesting too much entropy from f_entropy()
         *       at once. Specifically, if the underlying digest is not
         *       SHA-1, 3 / 2 * entropy_len is at least 36 Bytes, which
         *       is larger than the maximum of 32 Bytes that our own
         *       entropy source implementation can emit in a single
         *       call in configurations disabling SHA-512. */
        if( ( ret = ctx->f_entropy( ctx->p_entropy,
                                    seed + seedlen,
                                    ctx->entropy_len / 2 ) ) != 0 )

so, for SHA1 hmac_drbg, your offset should be 16 + 8 = 24 , and for SHA256, it should be 32 + 16 = 48 , as the results you are seeing. The reasoning for these values are in the standards mentioned in the comments.
Regards,
Mbed TLS Support
Ron

@roneld01: Thanks for the wonderful explanation.
Now I’ve updated my code to look like hmac_drbg you mentioned in the comment above. I’m using hmac_drbg_pr() as my reference.

My mbedtls_test_entropy_func() now matches whatever is in that file. Apart from that, all my calls match hmac_drbg_pr() from the file you mentioned.

    mbedtls_hmac_drbg_init (&hmac_ctx);
    md_info = mbedtls_md_info_from_type( MBEDTLS_MD_SHA1 );
    p_entropy.p = entropy_inputs;
    p_entropy.len = strlen(entropy_inputs);

    rc = mbedtls_hmac_drbg_seed( &hmac_ctx, md_info, mbedtls_test_entropy_func, entropy_inputs, nonce_pers, strlen(nonce_pers) );
    
    mbedtls_hmac_drbg_set_prediction_resistance(&hmac_ctx, MBEDTLS_HMAC_DRBG_PR_ON );
    
    rc = mbedtls_hmac_drbg_random_with_add(&hmac_ctx, result, resultLen, addInput1, addInputLength);
    rc = mbedtls_hmac_drbg_random_with_add(&hmac_ctx, result, resultLen, addInput2, addInputLength);

It exactly matches hmac_drbg_pr(). However, I get an error in entropy function at line memcpy( buf, ctx->p, len );

I’ve printed the values:

(gdb) print len
$2 = 24
(gdb) print ctx->len
$3 = 11840012698478436789

ctx->len seems to be wrong. May be that’s what is causing the error?

entropy_ctx *ctx = (entropy_ctx *) data;
This line is not assigning anything to ctx->len is it?
In mbedtls_test_entropy_func() we just declare ctx of type entropy_ctx and assign data to it but nothing is assigned to ctx->len.

Also wanted to check one more thing.
Can you please confirm what’s the answer you should be getting while using the below inputs (listed what responses I get with OpenSSL and mbedTLS):

[SHA-1]
[PredictionResistance = True]
[EntropyInputLen = 128]
[NonceLen = 64]
[PersonalizationStringLen = 0]
[AdditionalInputLen = 0]
[ReturnedBitsLen = 640]

COUNT = 0
EntropyInput = 6878f9ea4ff71310b5e1eb74d12c50a4
Nonce = 9edc884c73de0637
PersonalizationString =
AdditionalInput =
EntropyInputPR = c924feaafcfacaaf76e558351963eddc
AdditionalInput =
EntropyInputPR = a9704c677723b480c10c878636167c0d
mbedtls_ReturnedBits = 459a3ea43f958d3000edf286e718238049740011fe061dc55ccde3cd575170c508ebc78178f79a926344d074fb4d5e19e89f4fc4c388f851ecd8e2a154f6ef3939dbd5e481095cce614c9aa2399987c3
openssl_ReturnedBits = f6a4a5e37fd88c6a444346d035d5a50b4e00522e28cc0aad9893cb1ca03fcb8474142bf3f40e5c391e81826240420f22415ef7bf9142add94925d53d93078927b00c1fe0b20a9387facd95fcf9ab541a

@roneld01: Hi, can you please confirm if this sounds right?
For SHA-1, after mbedtls_hmac_drbg_seed(), the entropy offset is 16+8 which is 24 (like you said in your previous comment) and that’s what I see.
And after first call to mbedtls_hmac_drbg_random_with_add() the offset is increased by 16, so the total is 40.
Does that sound right? That means for second mbedtls_hmac_drbg_random_with_add() there’s just 8 bytes left?

Consider this example:

EntropyInput = 6878f9ea4ff71310b5e1eb74d12c50a4
Nonce = 9edc884c73de0637
PersonalizationString =
AdditionalInput =
EntropyInputPR = c924feaafcfacaaf76e558351963eddc
AdditionalInput =
EntropyInputPR = a9704c677723b480c10c878636167c0d

So the entropy input would be [6878f9ea4ff71310b5e1eb74d12c50a4c924feaafcfacaaf76e558351963eddca9704c677723b480c10c878636167c0d].

So for the mbedtls_hmac_drbg_seed() 6878f9ea4ff71310b5e1eb74d12c50a4c924feaafcfacaaf is used (24 bytes).
For first mbedtls_hmac_drbg_random_with_add() 76e558351963eddca9704c677723b480 is used (16 bytes).
For second mbedtls_hmac_drbg_random_with_add() just c10c878636167c0d is left (8 bytes).

Hi @athorath
As for your comparison between OpenSSL and Mbed TLS results, you should be comparing with test vectors, as it is not an efficient way to know where and if there is an issue.

And after first call to mbedtls_hmac_drbg_random_with_add() the offset is increased by 16, so the total is 40. Does that sound right?

Yes, the initial seeding uses nonce, which adds additional 8 bytes to the entropy, while the mbedtls_hmac_drbg_random_with_add() calls mbedtls_hmac_drbg_reseed() in case PR is on. Please try callingmbedtls_hmac_drbg_set_prediction_resistance() with MBEDTLS_HMAC_DRBG_PR_OFF and compare the outputs wiht openSSL.

Regards,
Mbed TLS Support
Ron