Skip to content

CTR_DRBG: grab a nonce from the entropy source if needed #305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

gilles-peskine-arm
Copy link
Collaborator

@gilles-peskine-arm gilles-peskine-arm commented Oct 22, 2019

CTR_DRBG grabs as many bytes of entropy for the initial seeding as for reseeds. This is not compliant with NIST SP 800-90A when the entropy length is too small to cover both the entropy itself and a nonce, unless the caller passes a nonce in the custom parameter.

Fix this by making a second call to the entropy function to grab enough entropy for a random nonce (half the entropy length, per SP 800-90A).

To maximize backward compatibility, do not make a second call to the entropy function if the entropy length is sufficient to cover a nonce. Thus the only combination of boolean compile-time option that leads to making a second call to the entropy function is:

!MBEDTLS_CTR_DRBG_USE_AES_128 && (!MBEDTLS_SHA512_C || MBEDTLS_ENTROPY_FORCE_SHA256)

An alternative design choice would be to always grab a nonce whose length is half the entropy length, like the HMAC_DRBG module does. This would be a simpler design but I preferred this solution which only adds a few bytes of code and which has less risk of breaking someone's workflow.

Test run on a merge of this PR and #291 to get coverage of relevant compilation settings that are currently not covered: https://jenkins-internal.mbed.com/job/mbedtls-psa-release-new/592/ → passed except for some Mbed OS jobs which are also failing in this PR.

Some commits in this PR are minor bugs which I've fixed in the backports of #299 (Mbed-TLS/mbedtls#2894 and Mbed-TLS/mbedtls#2895):

The rest of the PR is a behavior change that should not be backported, and some very minor test improvements that do not need to be backported.

Internal ref: IOTCRYPT-180

Update a comment that referred to a now-removed function.
Remove a comment that documented a now-removed restriction.
Add a new function mbedtls_ctr_drbg_set_nonce_len() which configures
the DRBG instance to call f_entropy a second time during the initial
seeding to grab a nonce.

The default nonce length is 0, so there is no behavior change unless
the user calls the new function.
Add a new function mbedtls_ctr_drbg_set_nonce_len() which configures
the DRBG instance to call f_entropy a second time during the initial
seeding to grab a nonce.

The default nonce length is 0, so there is no behavior change unless
the user calls the new function.
Test mbedtls_ctr_drbg_set_nonce_len (good cases only, which is in
keeping with the coverage of other functions).
No behavior change. Prepare for a future version that will set the
entropy nonce length to a nonzero value by default.
Change the default entropy nonce length to be nonzero in some cases.
Specifically, the default nonce length is now set in such a way that
the entropy input during the initial seeding always contains enough
entropy to achieve the maximum possible security strength per
NIST SP 800-90A given the key size and entropy length.

If MBEDTLS_CTR_DRBG_ENTROPY_LEN is kept to its default value,
mbedtls_ctr_drbg_seed() now grabs extra entropy for a nonce if
MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is disabled and either
MBEDTLS_ENTROPY_FORCE_SHA256 is enabled or MBEDTLS_SHA512_C is
disabled. If MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is enabled, or if
the entropy module uses SHA-512, then the default value of
MBEDTLS_CTR_DRBG_ENTROPY_LEN does not require a second call to the
entropy function to achieve the maximum security strength.

This choice of default nonce size guarantees NIST compliance with the
maximum security strength while keeping backward compatibility and
performance high: in configurations that do not require grabbing more
entropy, the code will not grab more entropy than before.
The default entropy nonce length is either zero or nonzero depending
on the desired security strength and the entropy length.

The implementation calculates the actual entropy nonce length from the
actual entropy length, and therefore it doesn't need a constant that
indicates the default entropy nonce length. A portable application may
be interested in this constant, however. And our test code could
definitely use it.

Define a constant MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN and use it in
test code. Previously, test_suite_ctr_drbg had knowledge about the
default entropy nonce length built in and test_suite_psa_crypto_init
failed. Now both use MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN.

This change means that the test ctr_drbg_entropy_usage no longer
validates that the default entropy nonce length is sensible. So add a
new test that checks that the default entropy length and the default
entropy nonce length are sufficient to ensure the expected security
strength.
@gilles-peskine-arm gilles-peskine-arm force-pushed the ctr_drbg-grab_nonce_from_entropy-set_nonce_length branch from 79dde0f to 6997166 Compare October 23, 2019 17:48
@k-stachowiak k-stachowiak self-requested a review October 28, 2019 11:06
Copy link
Contributor

@k-stachowiak k-stachowiak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to ask for clarification of two issues I have pointed out in the comments. In general the PR looks good though.

@gilles-peskine-arm gilles-peskine-arm added the needs: work The pull request needs rework before it can be merged. label Oct 28, 2019
@gilles-peskine-arm
Copy link
Collaborator Author

Looking at the test results, many Mbed OS jobs are failing. This isn't just known bugs or ordinary flakiness of some boards on the CI: all the benchmark jobs are failing on K64F with an error that's directly related to this PR (https://jenkins-internal.mbed.com/job/mbed-crypto-pr/job/PR-305-merge/4/execution/node/3923/log/), and all the CY8CKIT jobs are failing (not just the known issue with IAR) for a reason I don't understand.

You can't reuse a CTR_DRBG context without free()ing it and
re-init()ing. This generally happened to work, but was never
guaranteed. It could have failed with alternative implementations of
the AES module because mbedtls_ctr_drbg_seed() calls
mbedtls_aes_init() on a context which is already initialized if
mbedtls_ctr_drbg_seed() hasn't been called before, plausibly causing a
memory leak. Calling free() and seed() with no intervening init fails
when MBEDTLS_THREADING_C is enabled and all-bits-zero is not a valid
mutex representation. So add the missing free() and init().
You can't reuse a CTR_DRBG context without free()ing it and
re-init()ing it. This generally happened to work, but was never
guaranteed. It could have failed with alternative implementations of
the AES module because mbedtls_ctr_drbg_seed() calls
mbedtls_aes_init() on a context which is already initialized if
mbedtls_ctr_drbg_seed() hasn't been called before, plausibly causing a
memory leak. Since the addition of mbedtls_ctr_drbg_set_nonce_len(),
the second call to mbedtls_ctr_drbg_seed() uses a nonsensical value as
the entropy nonce length.

Calling free() and seed() with no intervening init fails when
MBEDTLS_THREADING_C is enabled and all-bits-zero is not a valid mutex
representation.
@gilles-peskine-arm gilles-peskine-arm force-pushed the ctr_drbg-grab_nonce_from_entropy-set_nonce_length branch from 07ebf57 to bd326f9 Compare October 28, 2019 20:06
Copy link
Contributor

@k-stachowiak k-stachowiak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing my comments! I have also reviewed the two new commits. Still looks good.

@gilles-peskine-arm
Copy link
Collaborator Author

gilles-peskine-arm commented Oct 29, 2019

CI failures:

@gilles-peskine-arm
Copy link
Collaborator Author

The non-reproducible CY8CKIT_062_WIFI_BT failures look like one board is non-responsive and other boards are working. There are enough passes to show that this PR is ok.

@gilles-peskine-arm gilles-peskine-arm removed the needs: work The pull request needs rework before it can be merged. label Oct 29, 2019
@Patater
Copy link
Contributor

Patater commented Oct 29, 2019

CI has at least one bad Cypress board in it causing tests to not complete successfully. I manually tested our mbed-os-example-mbed-crypto/getting-started example on CY8CKIT_062_WIFI_BT with GCC_ARM and the merge result of this PR with development. This run went well.

With ARMC6, the example run hung after printing the following, which is a known issue:

Import an AES key...	Imported a key
Sign a message...	

This is due to the version of ARMC6 (Component: ARM Compiler 6.10.1 Tool: armclang [5d143200]) having an optimizer bug. Using a custom build profile to set cflags to -O1, the example run completes successfully.

Copy link
Contributor

@dgreen-arm dgreen-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR looks good to me

@Patater Patater removed the needs: review The pull request is ready for review. This generally means that it has no known issues. label Nov 4, 2019
@gilles-peskine-arm gilles-peskine-arm merged commit 22589f0 into ARMmbed:development Nov 4, 2019
@yanesca
Copy link
Collaborator

yanesca commented Nov 7, 2019

@gilles-peskine-arm What is the meaning of the fixup! prefix in the commit messages? (I just find it confusing, because I usually mark commits like that if I later want to rebase and fixup some other commits with them.)

@gilles-peskine-arm
Copy link
Collaborator Author

@yanesca It's a git convention and feature indicating that the commit is an amendment to the previous commit with the same title minus the fixup! prefix. This convention is built into the --autosquash option of git rebase.

Normally a fixup is only in work in progress and shouldn't be pushed: you should rebase and squash the fixup before pushing. But here the defective commit was already present in the history, so the amendment can't be squashed.

gilles-peskine-arm added a commit to gilles-peskine-arm/mbed-crypto that referenced this pull request Nov 15, 2019
* ARMmbed#292: Make psa_close_key(0) and psa_destroy_key(0) succeed
* ARMmbed#299: Allow xxx_drbg_set_entropy_len before xxx_drbg_seed
* ARMmbed#259: Check `len` against buffers size upper bound in PSA tests
* ARMmbed#288: Add ECDSA tests with hash and key of different lengths
* ARMmbed#305: CTR_DRBG: grab a nonce from the entropy source if needed
* ARMmbed#316: Stop transactions from being reentrant
* ARMmbed#317: getting_started: Make it clear that keys are passed in
* ARMmbed#314: Fix pk_write with EC key to use a constant size for the private value
* ARMmbed#298: Test a build without any asymmetric cryptography
* ARMmbed#284: Fix some possibly-undefined variable warnings
* ARMmbed#315: Define MBEDTLS_PK_SIGNATURE_MAX_SIZE
* ARMmbed#318: Finish side-porting commits from mbedtls-restricted that missed the split
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants