Mbed-TLS / mbedtls

An open source, portable, easy to use, readable and flexible TLS library, and reference implementation of the PSA Cryptography API. Releases are on a varying cadence, typically around 3 - 6 months between releases.
https://www.trustedfirmware.org/projects/mbed-tls/
Other
5.2k stars 2.54k forks source link

No ECDSA Selftest Functions #7842

Open maxgerhardt opened 1 year ago

maxgerhardt commented 1 year ago

Suggested enhancement

mbedTLS should provide should provide a mbedtls_ecdsa_self_test() function, just like it does for mbedtls_ecp_self_test() for generate elliptic curve point tests.

While there is programs/pkey/ecdsa.c, it does not contain test vectors.

Justification

Mbed TLS needs this because in the case of alternative ECDSA impelmentations, developers can check the correctness of their implementation with the internal test bench function (as e.g. executed by selftest.c).

As I am for example activating MBEDTLS_ECDSA_VERIFY_ALT and MBEDTLS_ECDSA_SIGN_ALT and writing the necessary code to use the Public Key Accelerator (PKA) hardware of a STM32WLxxxx chip (example), I am getting exactly 0 feedback from the mbedtls testbench whether it works. Writing

int mbedtls_ecdsa_verify(mbedtls_ecp_group *grp,
                         const unsigned char *buf, size_t blen,
                         const mbedtls_ecp_point *Q,
                         const mbedtls_mpi *r,
                         const mbedtls_mpi *s) {
  return MBEDTLS_ERR_ECP_VERIFY_FAILED;
}

happily outputs

  ECP SW test #2 (constant op_count, other point): passed

  ENTROPY test: passed

  Executed 10 test suites

  [ All tests PASS ]
maxgerhardt commented 1 year ago

FYI, I came up with at least a starting point for the ECDSA verify that works:

#include <mbedtls/ecdsa.h>
#include <mbedtls/sha256.h>

int self_test_ecdsa() {
  /* Good vector from https://github.com/STMicroelectronics/STM32CubeWL/blob/main/Projects/NUCLEO-WL55JC/Examples/PKA/PKA_ECDSA_Verify/Src/SigVer.rsp
  [P-256,SHA-256]
  Msg = e1130af6a38ccb412a9c8d13e15dbfc9e69a16385af3c3f1e5da954fd5e7c45fd75e2b8c36699228e92840c0562fbf3772f07e17f1add56588dd45f7450e1217ad239922dd9c32695dc71ff2424ca0dec1321aa47064a044b7fe3c2b97d03ce470a592304c5ef21eed9f93da56bb232d1eeb0035f9bf0dfafdcc4606272b20a3
  Qx = e424dc61d4bb3cb7ef4344a7f8957a0c5134e16f7a67c074f82e6e12f49abf3c
  Qy = 970eed7aa2bc48651545949de1dddaf0127e5965ac85d1243d6f60e7dfaee927
  R = bf96b99aa49c705c910be33142017c642ff540c76349b9dab72f981fd9347f4f
  S = 17c55095819089c2e03b9cd415abdf12444e323075d98f31920b9e0f57ec871c
  Result = P (0 )
  */
  APP_PPRINTF("Starting ECDSA verify test!\n");
  const uint8_t msg[] = {
    0xe1, 0x13, 0x0a, 0xf6, 0xa3, 0x8c, 0xcb, 0x41, 0x2a, 0x9c, 0x8d, 0x13, 0xe1, 0x5d, 0xbf, 0xc9, 0xe6, 0x9a, 0x16, 0x38, 0x5a, 0xf3, 0xc3, 0xf1, 0xe5, 0xda, 0x95, 0x4f, 0xd5, 0xe7, 0xc4, 0x5f, 0xd7, 0x5e, 0x2b, 0x8c, 0x36, 0x69, 0x92, 0x28, 0xe9, 0x28, 0x40, 0xc0, 0x56, 0x2f, 0xbf, 0x37, 0x72, 0xf0, 0x7e, 0x17, 0xf1, 0xad, 0xd5, 0x65, 0x88, 0xdd, 0x45, 0xf7, 0x45, 0x0e, 0x12, 0x17, 0xad, 0x23, 0x99, 0x22, 0xdd, 0x9c, 0x32, 0x69, 0x5d, 0xc7, 0x1f, 0xf2, 0x42, 0x4c, 0xa0, 0xde, 0xc1, 0x32, 0x1a, 0xa4, 0x70, 0x64, 0xa0, 0x44, 0xb7, 0xfe, 0x3c, 0x2b, 0x97, 0xd0, 0x3c, 0xe4, 0x70, 0xa5, 0x92, 0x30, 0x4c, 0x5e, 0xf2, 0x1e, 0xed, 0x9f, 0x93, 0xda, 0x56, 0xbb, 0x23, 0x2d, 0x1e, 0xeb, 0x00, 0x35, 0xf9, 0xbf, 0x0d, 0xfa, 0xfd, 0xcc, 0x46, 0x06, 0x27, 0x2b, 0x20, 0xa3
  };
  const uint8_t pubpoint[] = {
    0x04 /* MBEDTLS_ECP_PF_UNCOMPRESSED */,
    //Qx
    0xe4, 0x24, 0xdc, 0x61, 0xd4, 0xbb, 0x3c, 0xb7, 0xef, 0x43, 0x44, 0xa7, 0xf8, 0x95, 0x7a, 0x0c, 0x51, 0x34, 0xe1, 0x6f, 0x7a, 0x67, 0xc0, 0x74, 0xf8, 0x2e, 0x6e, 0x12, 0xf4, 0x9a, 0xbf, 0x3c,
    // Qy
    0x97, 0x0e, 0xed, 0x7a, 0xa2, 0xbc, 0x48, 0x65, 0x15, 0x45, 0x94, 0x9d, 0xe1, 0xdd, 0xda, 0xf0, 0x12, 0x7e, 0x59, 0x65, 0xac, 0x85, 0xd1, 0x24, 0x3d, 0x6f, 0x60, 0xe7, 0xdf, 0xae, 0xe9, 0x27
  };
  const uint8_t Sig_R[] = {
    0xbf, 0x96, 0xb9, 0x9a, 0xa4, 0x9c, 0x70, 0x5c, 0x91, 0x0b, 0xe3, 0x31, 0x42, 0x01, 0x7c, 0x64, 0x2f, 0xf5, 0x40, 0xc7, 0x63, 0x49, 0xb9, 0xda, 0xb7, 0x2f, 0x98, 0x1f, 0xd9, 0x34, 0x7f, 0x4f
  };
  const uint8_t Sig_S[] = {
    0x17, 0xc5, 0x50, 0x95, 0x81, 0x90, 0x89, 0xc2, 0xe0, 0x3b, 0x9c, 0xd4, 0x15, 0xab, 0xdf, 0x12, 0x44, 0x4e, 0x32, 0x30, 0x75, 0xd9, 0x8f, 0x31, 0x92, 0x0b, 0x9e, 0x0f, 0x57, 0xec, 0x87, 0x1c
  };
  unsigned char hashOut[256 / 8]; //SHA256 output
  int ret = 0;
  if( (ret = mbedtls_sha256(msg, sizeof(msg), hashOut, 0 /* is SHA224 = no */)) != 0) {
    APP_PPRINTF("Hashing failed: %d\n", ret);
    return ret;
  }
  mbedtls_ecp_group ec_group;
  mbedtls_ecp_group_init(&ec_group);
  if( (ret = mbedtls_ecp_group_load(&ec_group, MBEDTLS_ECP_DP_SECP256R1)) != 0) {
    APP_PPRINTF("Loading group failed: %d\n", ret);
    return ret;
  }
  mbedtls_ecp_point Q; //public key point
  mbedtls_ecp_point_init(&Q);
  mbedtls_ecp_point_read_binary(&ec_group, &Q, pubpoint, sizeof(pubpoint));
  mbedtls_mpi r, s;
  mbedtls_mpi_init(&r);
  mbedtls_mpi_init(&s);
  mbedtls_mpi_read_binary(&r, Sig_R, sizeof(Sig_R));
  mbedtls_mpi_read_binary(&s, Sig_S, sizeof(Sig_S));
  APP_PPRINTF("Loaded all parameters, calling into ECDSA verify..\n");
  ret = mbedtls_ecdsa_verify(&ec_group, hashOut, sizeof(hashOut), &Q, &r, &s);
  if(ret != 0) {
    APP_PPRINTF("Signature verify failed, ret = %d\n", ret);
  } else {
    APP_PPRINTF("Signature verify OK!\n");
  }
  mbedtls_mpi_free(&r);
  mbedtls_mpi_free(&s);
  mbedtls_ecp_point_free(&Q);
  mbedtls_ecp_group_free(&ec_group);
  return ret;
}
gilles-peskine-arm commented 1 year ago

I agree that we should have a self-test, but that's mainly because some certifications require one. To test your driver, please run the unit tests (in particular test_suite_ecdsa and test_suite_psa_crypto). They're mainly written with a software implementation in mind, and might not catch issues that are specific to accelerators (such as not resetting the hardware state properly between calls), but they're a start.

The unit test framework may require some adaptation to run on embedded targets, since it assumes that some stdio is available. If that's an issue, please let us know what problems you run into: we do know that there's a potential gap there, but not what the exact requirements should be.

maxgerhardt commented 1 year ago

./scripts/generate_test_code.py -f suites/test_suite_ecdsa.function -d suites/test_suite_ecdsa.data -t suites/main_test.function -p suites/host_test.function -s suites --helpers-file suites/helpers.function -o .

Does indeed generate the test suite files, but this wants to read and parse its vectors from the .datax file, and I don't have a filesystem setup on the microcontroller. Unless I do a rewrite to have the ECC vectors directly in C, add in LittleFS or similiar filesystem or copy the .datax file content in C and hack around fread(), fopen(), etc., this doesn't work for me right now.

But I can at least cherry pick the test vectors and test functions and do my own stuff with it.

Do I see it correctly that.. mbedTLS does currenetly not have this testbed (tests/) running on.. embedded devices in their CI etc?

gilles-peskine-arm commented 1 year ago

Do I see it correctly that.. mbedTLS does currenetly not have this testbed (tests/) running on.. embedded devices in their CI etc?

Unfortunately, that is the case. We only test on Linux, FreeBSD and Windows at the moment. We used to also test under Mbed OS but we stopped a couple of years ago.

It's not that much of a problem because the code is largely OS-independent, apart from some small parts where we do test on the OSes for which we provide the platform integration. But it does make life harder if you want to test the integration on a platform. And for us it also means that we don't notice stack size increases (we don't monitor for that).

Unless I do a rewrite to have the ECC vectors directly in C, add in LittleFS or similiar filesystem or copy the .datax file content in C and hack around fread(), fopen(), etc., this doesn't work for me right now.

All right, thanks for the feedback. We plan to add a mechanism where you can either bundle the test data in the executable (at the cost of having large executables) or transmit the test data over e.g. a serial connection (at the cost of needing the machinery to transmit the data). But I can't give a timeline for that.