Skip to content

Conversation

@gilles-peskine-arm
Copy link
Collaborator

@gilles-peskine-arm gilles-peskine-arm commented Nov 5, 2019

Define MBEDTLS_PK_SIGNATURE_MAX_SIZE, which is (larger than) the maximum size of a signature produced by mbedtls_pk_sign().

Fix the sample programs pk_sign and pk_verify to use this size, instead of MBEDTLS_MPI_MAX_SIZE which is typically too small in builds without RSA.

Add unit tests that would catch an incorrect value for MBEDTLS_PK_SIGNATURE_MAX_SIZE. Non-regression: one of the new test cases fails in config-suite-b.h with ASan enabled if MBEDTLS_PK_SIGNATURE_MAX_SIZE has the value MBEDTLS_MPI_MAX_SIZE which was previously used in programs/pkey/pk_sign.

Internal ref: IOTSSL-2857

This was fixed in a different way in Mbed TLS 2.7, 2.16 and development (released in 2.19) (private link: https://github.com/ARMmbed/mbedtls-restricted/pull/573). We lost the fix to development due to a mistake when splitting Mbed Crypto and Mbed TLS (internal ref: IOTCRYPT-969).

Goal this PR: fix IOTSSL-2857 on the crypto side in a way that's better for users and easier to test than the original fix. Note that on the TLS side, the fix made in X.509 via #r573 is sufficient to fix IOTSSL-2857.

Follow-ups:

  • In the Mbed TLS LTS branches, pk_sign was fixed but not pk_verify.
  • To make the TLS code more robust, especially when using PSA via pk, change it to use MBEDTLS_PK_SIGNATURE_MAX_SIZE.
  • A few bits of https://github.com/ARMmbed/mbedtls-restricted/pull/573 are not addressed here because they weren't needed to fix IOTSSL-2857. I'll make a separate PR for those.

Based on the buffer size used in the pk_sign sample program, this is
MBEDTLS_MPI_MAX_SIZE.
@gilles-peskine-arm gilles-peskine-arm added bug Something isn't working needs: review The pull request is ready for review. This generally means that it has no known issues. labels Nov 5, 2019
#if defined(MBEDTLS_RSA_C) && \
MBEDTLS_MPI_MAX_SIZE > MBEDTLS_PK_SIGNATURE_MAX_SIZE
#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE
#define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
Copy link
Contributor

Choose a reason for hiding this comment

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

These lines appear to unset MBEDTLS_PK_SIGNATURE_MAX_SIZE and then reset it back to MBEDTLS_MPI_MAX_SIZE. Am I missing something here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's a coincidence. The undef/define gymnastic is necessary to redefine the value. While you can express max(…) using C operators, it doesn't scale. It's still ok for max(x1,x2) (x1 > x2 ? x1 : x2) but it quickly grows unmanageable for max(x1, x2, x3, …).

I'll add a comment to explain what's going on.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, but no, the code isn't working as intended! It's always setting MBEDTLS_PK_SIGNATURE_MAX_SIZE to be at least MBEDTLS_MPI_MAX_SIZE, which I wanted to avoid in the fairly common case where MBEDTLS_MPI_MAX_SIZE is kept to a large value but RSA signature is disabled and MBEDTLS_PK_SIGNATURE_MAX_SIZE encompasses ECDSA only with a much smaller maximum size.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Uh, actually, this fallback was working as intended, sort of. It's not a fallback in the sense of “if nothing else is defined”, but something that can arise even if nothing is defined.

Except that the RSA_ALT case can't happen if RSA_ALT is not enabled, so that part was wrong.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.


TEST_ASSERT( mbedtls_pk_sign( &pk, MBEDTLS_MD_SHA256, hash, sizeof hash,
sig, &sig_len, rnd_std_rand, NULL ) == sign_ret );
TEST_ASSERT( sig_len <= MBEDTLS_PK_SIGNATURE_MAX_SIZE );
Copy link
Contributor

Choose a reason for hiding this comment

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

Is is even possible for this to be ever triggered? Won't it be a buffer overflow in such case? Can max(MBEDTLS_ECDSA_MAX_LEN, MBEDTLS_MPI_MAX_SIZE) be higher than MBEDTLS_PK_SIGNATURE_MAX_SIZE?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This can be triggered if mbedtls_pk_sign is buggy, which is the point of a test.

  • If mbedtls_pk_sign writes less than MAX_SIZE bytes to its output buffer but sets sig_len to this value, the test fails here. This is a possible failure mode, but not a likely one.
  • If mbedtls_pk_sign writes more than MAX_SIZE bytes to its output buffer, there's a buffer overflow. Since we can't be sure how much mbedtls_pk_sign will write, the potential buffer overflow if the code is buggy is unavoidable. I could add a safety margin and hope that the overflow doesn't go beyond that, but I don't think it's very useful: ASan detects it nicely (try it out with the commit that adds this test with config-suite-b.h).

max(MBEDTLS_ECDSA_MAX_LEN, MBEDTLS_MPI_MAX_SIZE) can be higher than MBEDTLS_PK_SIGNATURE_MAX_SIZE, but only if ECDSA or RSA is disabled, or if the definition of MBEDTLS_PK_SIGNATURE_MAX_SIZE is buggy.

AndrzejKurek
AndrzejKurek previously approved these changes Nov 8, 2019
@dgreen-arm
Copy link
Contributor

dgreen-arm commented Nov 8, 2019

CI looks pretty unhappy about something in the pk test suite
PSA wrapped sign .................................................. FAILED

@gilles-peskine-arm gilles-peskine-arm added needs: work The pull request needs rework before it can be merged. and removed needs: review The pull request is ready for review. This generally means that it has no known issues. labels Nov 8, 2019
@gilles-peskine-arm
Copy link
Collaborator Author

Turns out that checking the value of a variable before setting it doesn't work. And locally testing a bunch of “exotic” configuration, but not full, is not such a good predictor of all configurations working. Who'd'a thunk it.

@gilles-peskine-arm gilles-peskine-arm added needs: review The pull request is ready for review. This generally means that it has no known issues. and removed needs: work The pull request needs rework before it can be merged. labels Nov 8, 2019
@gilles-peskine-arm
Copy link
Collaborator Author

CI says it's still not right. I'll analyze another day.

@gilles-peskine-arm gilles-peskine-arm added needs: work The pull request needs rework before it can be merged. and removed needs: review The pull request is ready for review. This generally means that it has no known issues. labels Nov 8, 2019
In pk_sign_verify, if mbedtls_pk_sign() failed, sig_len was passed to
mbedtls_pk_verify_restartable() without having been initialized. This
worked only because in the only test case that expects signature to
fail, the verify implementation doesn't look at sig_len before failing
for the expected reason.

The value of sig_len if sign() fails is undefined, so set sig_len to
something sensible.
Add a check that the signature size from pk_sign is less than the
documented maximum size.

Reduce the stack consumption in pk_sign_verify.
This reveals that MBEDTLS_PK_SIGNATURE_MAX_SIZE is too small.
The original definition of MBEDTLS_PK_SIGNATURE_MAX_SIZE only took RSA
into account. An ECDSA signature may be larger than the maximum
possible RSA signature size, depending on build options; for example
this is the case with config-suite-b.h.
mbedtls_pk_sign does not take the size of its output buffer as a
parameter. We guarantee that MBEDTLS_PK_SIGNATURE_MAX_SIZE is enough.
For RSA and ECDSA signatures made in software, this is ensured by the
way MBEDTLS_PK_SIGNATURE_MAX_SIZE is defined at compile time. For
signatures made through RSA-alt and PSA, this is not guaranteed
robustly at compile time, but we can test it at runtime, so do that.
PSA_ASYMMETRIC_SIGNATURE_MAX_SIZE was taking the maximum ECDSA key
size as the ECDSA signature size. Fix it to use the actual maximum
size of an ECDSA signature.
@gilles-peskine-arm gilles-peskine-arm added needs: review The pull request is ready for review. This generally means that it has no known issues. and removed needs: work The pull request needs rework before it can be merged. labels Nov 12, 2019
@gilles-peskine-arm
Copy link
Collaborator Author

@AndrzejKurek @dgreen-arm CI passed! Please re-review. I tweaked some commits; the original branch is at https://github.com/gilles-peskine-arm/mbed-crypto/tree/pk_signature_max_size-1.

dgreen-arm
dgreen-arm previously approved these changes Nov 12, 2019
The initial value for the max calculation needs to be 0. The fallback
needs to come last. With the old code, the value was never smaller
than the fallback.

For RSA_ALT, use MPI_MAX_SIZE. Only use this if RSA_ALT is enabled.

For PSA, check PSA_ASYMMETRIC_SIGNATURE_MAX_SIZE, and separately check
the special case of ECDSA where PSA and mbedtls have different
representations for the signature.
Clarify the documentation regarding the signature size.

Also fix minor niggles about references to mbedtls_pk_sign().
@gilles-peskine-arm gilles-peskine-arm removed the needs: review The pull request is ready for review. This generally means that it has no known issues. label Nov 13, 2019
@gilles-peskine-arm gilles-peskine-arm merged commit f0d8700 into ARMmbed:development Nov 13, 2019
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.

3 participants