CTR DRBG zero result

I’m trying to perform CTR DRBG and here is my code.

    mbedtls_ctr_drbg_context ctr_ctx;
    unsigned char entropy[512];

    memset( entropy, 0x00, 512 );

    strncpy(entropy, entropyInput, entropyLength);
    strncpy(entropy + entropyLength, entropyInputPR1, entropyLength);
    strncpy(entropy + (2*entropyLength), entropyInputPR2, entropyLength);

    mbedtls_ctr_drbg_init(&ctr_ctx);

    mbedtls_ctr_drbg_seed( &ctr_ctx, mbedtls_entropy_func, entropy, pString, pStringLength );
    if ( predResist == 1 ) {
       mbedtls_ctr_drbg_set_prediction_resistance( &ctr_ctx, MBEDTLS_CTR_DRBG_PR_ON );
    }
    mbedtls_ctr_drbg_random_with_add( &ctr_ctx, result, sizeof(result), addInput1, addInputLength );
    if ( predResist == 0 ) {
       mbedtls_ctr_drbg_reseed( &ctr_ctx, NULL, 0 );
    }
    mbedtls_ctr_drbg_random_with_add( &ctr_ctx, result, sizeof(result), addInput2, addInputLength );

My input is the request file from NIST (shown below):

> [AES-128 no df]
> [PredictionResistance = True]
> [EntropyInputLen = 256]
> [NonceLen = 0]
> [PersonalizationStringLen = 0]
> [AdditionalInputLen = 0]
> [ReturnedBitsLen = 2048]
> 
> COUNT = 0
> EntropyInput = 64e1936f7fd634031b5d8944e28be0cc14b32f1e741d17a012ac84cd7890cc1e
> Nonce =
> PersonalizationString =
> AdditionalInput =
> EntropyInputPR = ecd7aaa7a1183e70fb19df20f70f56ce6870c56fe4af7f0b090125db7a04d68d
> AdditionalInput =
> EntropyInputPR = 636d9a4cff514bdfb557a44d3ec5fa3c16878e3f52c1a949924063637eacfc90

Few things I wanted to check:

  1. There are 3 entropy fields, so I’ve concatenated them. EntropyInput+EntropyInputPR+EntropyInputPR. Is that order correct?
  2. There is also a “nonce” field and I don’t see an example of how this can be used in the test suites. In my current example it is zero length but there are instances where this field has a value. Does this need to be concatenated with any of the other strings?
  3. Are any DRBG calls missing in my code or is something not in right order? Because with the current code, “result” just has the value 0.

THANKS in advance!

Hi @athorath
You shouldn’t concatenate the 3 entropy fields.
These do not indicate three entropy sources, but 3 occasions of entropy, in the test suite.
I would recomend you look at the Mbed TLS ctr drbg test suite for some hints on how to implement your tests
Regards,
Mbed TLS Team member
Ron

@roneld01: Thanks for the pointer to Mbed TLS ctr drbg test suite. That’s really helpful!
I did look at the ctr_drbg_validate_internal function. I’ve a few follow up questions (using ctr_drbg_validate_internal as reference) . Pasting my input below:

[AES-128 no df]
[PredictionResistance = True]
[EntropyInputLen = 256]
[NonceLen = 0]
[PersonalizationStringLen = 0]
[AdditionalInputLen = 0]
[ReturnedBitsLen = 2048]

COUNT = 0
EntropyInput = 64e1936f7fd634031b5d8944e28be0cc14b32f1e741d17a012ac84cd7890cc1e
Nonce =
PersonalizationString =
AdditionalInput =
EntropyInputPR = ecd7aaa7a1183e70fb19df20f70f56ce6870c56fe4af7f0b090125db7a04d68d
AdditionalInput =
EntropyInputPR = 636d9a4cff514bdfb557a44d3ec5fa3c16878e3f52c1a949924063637eacfc90
  1. While instantiating CTR DRBG using mbedtls_ctr_drbg_seed_entropy_len, nonce is a concatenation of nonce and personalization string is that correct? There’s a comment in the function saying nonce||perso = nonce[nonce->len]

  2. When I instantiate CTR DRBG using mbedtls_ctr_drbg_seed_entropy_len(), entropy->x is passed in which I believe maps to “EntropyInput” from the inputs I’ve shown above. And then I’ve 2 occurrences of “EntropyInputPR” in the list of inputs above. How would they be used because we are only passing 1 entropy value into ctr_drbg_validate_internal(). In OpenSSL usually when we call the generate for additional input 1, first “EntropyInputPR” is used and second one when generate is called second time with additional input 2.
    But if I want to apply same logic here, I’ve instantiated CTR DRBG using “EntropyInput”, so I need to instantiate context again and then call mbedtls_ctr_drbg_random_with_add() ? Or is entire approach totally wrong?
    I’ve a working code for CTR DRBG using OpenSSL and these are the steps followed:

    • Set call backs (for nonce and entropy)
    • Instantiate
    • If pred resistance is disabled, reseed using additional Input Reseed and entropy Reseed
    • Generate result using first entropyInputPR and additional input 1
    • Generate result using second entropyInputPR and additional input 2
    • Uninstantiate

Looking at the steps mentioned for OpenSSL I’m assuming additional inputs and entropyInputPR go together for mbedTLS as well?
Sorry if this sounds too vague.
Extremely thankful for your help on this!!

Hi @athorath

While instantiating CTR DRBG using mbedtls_ctr_drbg_seed_entropy_len, nonce is a concatenation of nonce and personalization string is that correct?

Yes. As you can see from the internal function’s documentation:

 * with inputs
 *   custom[:len] = nonce || personalization_string

Note that mbedtls_ctr_drbg_seed_entropy_len() as not a public function, and used for tests alone. You should be using mbedtls_ctr_drbg_seed() in your product.

How would they be used because we are only passing 1 entropy value into ctr_drbg_validate_internal ()

As you can see from the entropy test function used for the test alone, all the entropy data is in a single buffer, but every time a seed is called, this test function only returns the relevent entropy, with specific required size, in index test_offset_idx. So, every time you request to reseed, test_offset_idx is incremented by the length of entropy requsted, and the relevant entropyInputPR should be returned. You don’t need to instantiate the context again.
I hope my answer was clear enough.
Regards

@roneld01: Thank you, yes your pointers were very clear :slight_smile:
Will try them out.
Thanks again!

@roneld01: I’ve tried your suggestions. Previously mbedtls_ctr_drbg_random_with_add() calls all returned non-zero values but now all functions return 0 which is a huge step forward from where I was. However, I still don’t see the right result.
I’ve briefly shown my inputs, outputs and code snippet. Is there a step missing?

Input:

[AES-128 no df]
[PredictionResistance = True]
[EntropyInputLen = 256]
[NonceLen = 0]
[PersonalizationStringLen = 0]
[AdditionalInputLen = 0]
[ReturnedBitsLen = 2048]

COUNT = 0
EntropyInput = 64e1936f7fd634031b5d8944e28be0cc14b32f1e741d17a012ac84cd7890cc1e
Nonce =
PersonalizationString =
AdditionalInput =
EntropyInputPR = ecd7aaa7a1183e70fb19df20f70f56ce6870c56fe4af7f0b090125db7a04d68d
AdditionalInput =
EntropyInputPR = 636d9a4cff514bdfb557a44d3ec5fa3c16878e3f52c1a949924063637eacfc90

Some of the debug outputs:

Concatenated entropyInput : 64e1936f7fd634031b5d8944e28be0cc14b32f1e741d17a012ac84cd7890cc1eecd7aaa7a1183e70fb19df20f70f56ce6870c56fe4af7f0b090125db7a04d68d636d9a4cff514bdfb557a44d3ec5fa3c16878e3f52c1a949924063637eacfc90

Nonce :

mbedtls_ctr_drbg_seed rc : 0

mbedtls_ctr_drbg_random_with_add add1 rc : 0

mbedtls_ctr_drbg_random_with_add add2 rc : 0

RESULT : [de5f9b23c26ae498f135e7072270b31b1032d11b93eaac16b30bf5e166f154a0f497593a16376839faa1bc0ff95d179fa09550908b942ea9fa08f1c73c31c18e72492ff714e2c809af5cf457bf66a2cfe862692e2efc966802cbad06aeaf096d6c8ba666488086df3944585cc0d78a7afac103594a0eb01b385a53f69ffa217c682dab45f9ca8e3e03e81e10de5864612e5312341ceb373c3aeba7ebc0e9fa0bb0a818c43075e03b0da9a7b2035b6a2a4e612d75a5a033babfb3e6f1d4db5e78c50fb7e0d23c297c8f474f1c919559635ec5947c7efd30bab2db7e6cd4c4a336137d490d8924941f0cb3ba2d41ed3b2df52eba8fb77b022abca3ee941bef3b2e]

And in this test nonce and personalization strings are 0 length.
Here the result is “de5f9b23…”. But the expected response for this input should be “27e689ecca2db…57d3b”.

Code snippet:

test_offset_idx = 0;
mbedtls_ctr_drbg_init(&ctr_ctx);
test_max_idx = 32 * 8;   // 32 in entropy length in bytes

rc = mbedtls_ctr_drbg_seed( &ctr_ctx, mbedtls_test_entropy_func, entropy_inputs, nonce_pers, strlen(nonce_pers));

if ( params->predResist == 0 ) {
    rc = mbedtls_ctr_drbg_reseed(&ctr_ctx, params->addInputReseed, params->addInputLength);
}
else {
     mbedtls_ctr_drbg_set_prediction_resistance(&ctr_ctx, MBEDTLS_CTR_DRBG_PR_ON );
}
rc = mbedtls_ctr_drbg_random_with_add(&ctr_ctx, result, resultLen, params->addInput1, params->addInputLength);
rc = mbedtls_ctr_drbg_random_with_add(&ctr_ctx, result, resultLen, params->addInput2, params->addInputLength);

I’ve replicated mbedtls_test_entropy_func() from ctr_drbg_validate_internal().

  1. If you see the “Concatenated entropyInput” in the debug, it shows that the 3 entropy inputs are concatenated correctly. All calls are returning zeroes so I’m not sure what could go wrong here!
  2. This is the next question, not related to my wrong output. If reseed values are given in test input, only then I’ll perform reseed operation (RESEED_FIRST). My doubt is, do I need to concatenate “concatenated entropyInput” with “entropyReseed” test inputs? If yes, in what order? (Right now the order of concatenation is entropyInput+entropyInputPR1+entropyInputPR2). Because the steps that I’ve in my code now, do nothing extra to use “entropyReseed” if reseed is enabled in my test.
  3. I don’t see AES related stuff anywhere. Like the size of AES being passed in. I could be using AES-128 or AES-192 or AES-256. I don’t see that being factored in anywhere. I see when mbedtls_ctr_drbg_seed() calls mbedtls_ctr_drbg_seed_entropy_len(), this uses MBEDTLS_CTR_DRBG_KEYSIZE. And this could be either 128 bits if MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is enabled else 256 bits. So I take it 192-bits is not supported. Also, how can I update settings so that both 128 and 256 bits are handled. Because the request files can have any AES key size.

Sorry for the non-stop questions!
And thanks for being so patient with me!

HI @athorath

I’ve replicated mbedtls_test_entropy_func () from ctr_drbg_validate_internal ().

What do you mean? Have you taken this to your code?
Could you confirm that test_offset_idx is increased every seed operation?

Does this mean that you are running an additional ressed if no prediction resistance?
Do you get error also when PredictionResistance is False?
Is MBEDTLS_CTR_DRBG_USE_128_BIT_KEY defined?
I am trying to understand your test case.
If prediction resistance is on, then every time you generate random, you reseed.

I can’t see anything suspicious in your code.
Have you checked rc of your calls to mbedtls_ctr_drbg_random_with_add()?
Why is test_max_idx set to 32 * 8? I believe it should be 32 * 3, however this shouldn’t be the cause for your failure.

Please note that the test suite for ctr_drbg is specifically use case in Mbed TLS tests, and should not be used in production.
You can implement your test however you wish, but you can look at the test suite for reference.

As for the test suite, you should always concatenate the entropy, according the test entropy function, to generate the expected entropy.

If yes, in what order?
The order they appear in the test. Every call for reseeding, should generate the expected entropy

I don’t see AES related stuff anywhere.

Correct. Mbed TLS ctr_drbg supports AES 256. MBEDTLS_CTR_DRBG_USE_128_BIT_KEY was added, in compile time, to add support for hardware accelarators that don’t have any AES other than 128 bit keys. Note that using AES 128 reduces the security strength of your random. You should only use AES 256 if possible!

Also, how can I update settings so that both 128 and 256 bits are handled.

As mentioned, only at build time. This is not dynamic.
Regards

  • The flow of my code is shown below (or what I want it to be):

    • Instantiate
    • If pred resistance is disabled, reseed using additional Input Reseed and entropy Reseed
    • Generate result using first entropyInputPR and additional input 1
    • Generate result using second entropyInputPR and additional input 2
    • Uninstantiate
  • Will use only AES-256

  • Will change 32 * 8 to 32 *3. The reason I did 32 * 8 was 32 is each entropy length in bytes and thought it needs to be in bits.

  • Increment of test_offset_idx I thought is done as part of mbedtls_test_entropy_func (). Will do this in my code.

  • And yes I’ve copied ONLY mbedtls_test_entropy_func () into my code for testing purposes.

  • You asked if I’m doing an additional reseed if prediction resistance is disabled. I’m looking into this.

THANKS A LOT!

Update: This is regarding your question if I’m doing additional reseed. I’ve explained what calls I used and for what purpose:

  1. Instantiate - to do this, I use mbedtls_ctr_drbg_seed() that uses “entropyInput”
  2. If pred resistance is disabled, reseed using additional Input Reseed and entropy Reseed - to do this, I use mbedtls_ctr_drbg_reseed() with “additional Input Reseed” and “entropy Reseed”
  3. Generate result using first entropyInputPR and additional input 1 - to do this, I use mbedtls_ctr_drbg_random_with_add()
  4. Generate result using second entropyInputPR and additional input 2 - to do this, I use mbedtls_ctr_drbg_random_with_add()
  5. Uninstantiate - to do this, I use mbedtls_ctr_drbg_free()

I’m guessing you asked about additional reseed because I’m calling mbedtls_ctr_drbg_seed() and then mbedtls_ctr_drbg_reseed(). If so, then I’m guessing the issue lies within steps 1 and 2. I’m probably using a wrong call or an extra call.
Do I use only the seed call to instantiate but pass in both entropy input and entropy reseed? And then just increment test_max_idx by 64 (because both are 32 bytes long) and call mbedtls_ctr_drbg_random_with_add() and remove the reseed call?

HI,

Increment of test_offset_idx I thought is done as part of mbedtls_test_entropy_func (). Will do this in my code.

It is incremented in the entropy function, but if it is static in one place, I suggest you check it’s value after every seeding call.

Have you run the ctr_drbg test suite provided in Mbed TLS, without your modifications, to see if there is some platform issues?

Do I use only the seed call to instantiate but pass in both entropy input and entropy reseed? And then just increment test_max_idx by 64 (because both are 32 bytes long) and call mbedtls_ctr_drbg_random_with_add() and remove the reseed call?

I don’t understand your question. You should implement according to the test case.

Please try the Mbed TLS test suite , once it works, and replace the data vector with the given test vector, in the relevant test ( no df, personelization=false, no additional, pr=true and no nonce)

Thanks for the response.
In your comment you suggested about checking the value of test_offset_idx. I’ve checked that and I see something suspicious.
“entropy_inputs” contains entropyInput+entropyInputPR+entropyInputPR. So everytime only 32 bytes should be read (that’s my entropy length).

// This should use 0-32 from entropy_inputs
mbedtls_ctr_drbg_seed( &ctr_ctx, mbedtls_test_entropy_func, entropy_inputs, nonce_pers, strlen(nonce_pers));
// This should use 33-64 from entropy_inputs
mbedtls_ctr_drbg_random_with_add(&ctr_ctx, result, resultLen, params->addInput1, params->addInputLength);
// This should use rest of it
mbedtls_ctr_drbg_random_with_add(&ctr_ctx, result, resultLen, params->addInput2, params->addInputLength);

Incrementing “test_offset_idx” is done in mbedtls_test_entropy_func.
I’ve outputted the value after and before each of the 3 calls mentioned above. It reads 0-48 and 48-96. How can the length that needs to be read be controlled?

mbedtls_test_entropy_func does take in “len” as a parameter but I don’t think when we use mbedtls_test_entropy_func in seed call, length can be specified.

Output:

Before ctr_drbg_seed() : 0

After ctr_drbg_seed() : 48

After mbedtls_ctr_drbg_random_with_add 1() : 96

How can this be resolved?

Thanks for your help…

I’ve looked into ctr_drbg.h and I see “MBEDTLS_CTR_DRBG_ENTROPY_LEN” is assigned 48 if MBEDTLS_SHA512_C is defined config.h else 32 is used. I’m guessing I’ve to disable “MBEDTLS_SHA512_C”?

I tried doing that and after that update I see correct length of entropy picked up (shown below):

Before ctr_drbg_seed() : 0

After ctr_drbg_seed() : 32

After mbedtls_ctr_drbg_random_with_add 1() : 64

After mbedtls_ctr_drbg_random_with_add 2() : 96

Hi @athorath
Yes, if you choose the public function mbedtls_ctr_drbg_seed(), then MBEDTLS_CTR_DRBG_ENTROPY_LEN will be used as entropy length, which is 48 if MBEDTLS_SHA512_C is enabled or mbedtls_ctr_drbg_seed_entropy_len is not enabled.
This is why in our tests we call mbedtls_ctr_drbg_seed_entropy_len(), however for your production, you shouldn’t call it.
I would suggest you define MBEDTLS_CTR_DRBG_ENTROPY_LEN instead of undefining MBEDTLS_SHA512_C as you may need SHA512 for other use cases.

Do you now get the expected result?
Regards

Thanks for the suggestion, will do it. I think I’m getting the right result now…

I’m sorry, it was a copy\paste error.
I meant you should define MBEDTLS_ENTROPY_FORCE_SHA256 instead of undefining MBEDTLS_SHA512_C

Oh I had already defined MBEDTLS_ENTROPY_FORCE_SHA256, but I defined MBEDTLS_CTR_DRBG_ENTROPY_LEN too based on previous comment. Do I need to have both in there? I’m guessing if I have MBEDTLS_ENTROPY_FORCE_SHA256, that should do it, no need of MBEDTLS_CTR_DRBG_ENTROPY_LEN.

I’m guessing if I have MBEDTLS_ENTROPY_FORCE_SHA256, that should do it, no need of MBEDTLS_CTR_DRBG_ENTROPY_LEN.

Correct. No need to define MBEDTLS_CTR_DRBG_ENTROPY_LEN