diff --git a/library/lmots.c b/library/lmots.c index 8e56696e57..d733dc02e6 100644 --- a/library/lmots.c +++ b/library/lmots.c @@ -44,11 +44,6 @@ #include "psa/crypto.h" -#define MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET (MBEDTLS_LMOTS_SIG_TYPE_OFFSET + \ - MBEDTLS_LMOTS_TYPE_LEN) -#define MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(type) (MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET + \ - MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(type)) - #define MBEDTLS_LMOTS_PUBLIC_KEY_TYPE_OFFSET (0) #define MBEDTLS_LMOTS_PUBLIC_KEY_I_KEY_ID_OFFSET (MBEDTLS_LMOTS_PUBLIC_KEY_TYPE_OFFSET + \ MBEDTLS_LMOTS_TYPE_LEN) @@ -74,6 +69,10 @@ static const unsigned char D_PUBLIC_CONSTANT_BYTES[D_CONST_LEN] = {0x80, 0x80}; static const unsigned char D_MESSAGE_CONSTANT_BYTES[D_CONST_LEN] = {0x81, 0x81}; +#if defined(MBEDTLS_TEST_HOOKS) +int( *mbedtls_lmots_sign_private_key_invalidated_hook )( unsigned char * ) = NULL; +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + void unsigned_int_to_network_bytes( unsigned int val, size_t len, unsigned char *bytes ) { @@ -815,6 +814,18 @@ int mbedtls_lmots_sign( mbedtls_lmots_private_t *ctx, MBEDTLS_LMOTS_TYPE_LEN, sig + MBEDTLS_LMOTS_SIG_TYPE_OFFSET ); + /* Test hook to check if sig is being written to before we invalidate the + * private key. + */ +#if defined(MBEDTLS_TEST_HOOKS) + if( mbedtls_lmots_sign_private_key_invalidated_hook != NULL ) + { + ret = ( *mbedtls_lmots_sign_private_key_invalidated_hook )( sig ); + if( ret != 0 ) + return( ret ); + } +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + /* We've got a valid signature now, so it's time to make sure the private * key can't be reused. */ diff --git a/library/lmots.h b/library/lmots.h index 56d23f855a..993c01c10e 100644 --- a/library/lmots.h +++ b/library/lmots.h @@ -54,6 +54,10 @@ MBEDTLS_LMOTS_N_HASH_LEN(type)) #define MBEDTLS_LMOTS_SIG_TYPE_OFFSET (0) +#define MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET (MBEDTLS_LMOTS_SIG_TYPE_OFFSET + \ + MBEDTLS_LMOTS_TYPE_LEN) +#define MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(type) (MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET + \ + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(type)) #ifdef __cplusplus extern "C" { diff --git a/tests/suites/test_suite_lmots.data b/tests/suites/test_suite_lmots.data index 6b4cc35d98..0845a17d09 100644 --- a/tests/suites/test_suite_lmots.data +++ b/tests/suites/test_suite_lmots.data @@ -8,6 +8,11 @@ LMOTS sign-verify test #2 # message, and verifies the signature. lmots_sign_verify_test:"55a6647a581004306792b653a561d9f3":"00000000000000000000000000000000":12:"c3dbc3fea047dca8fb7a3cdf609a5b7f48599c193c90e958ce9388c84df0a906" +LMOTS NULL-message sign-verify test +# This test uses a NULL zero-length message, and then generates a private key, +# signs the message, and verifies the signature. +lmots_sign_verify_null_msg_test::"00000000000000000000000000000000":12:"be5fa89144f2d665c66ead8216bc02006e0eccd8b3697a0aea44f6c93afe7955" + LMOTS hash-sigs interop test #1 # This test uses data from https://github.com/cisco/hash-sigs due to the limited # amount of available test vectors for LMOTS. A public/private key was generated @@ -60,3 +65,10 @@ LMOTS key reuse test # message, and then attempts to sign the message again. The second signature # must fail as private key material must be deleted after a key is used to sign. lmots_reuse_test:"cfcd1e81193e310c9d931d1b00818d14":"00000000000000000000000000000000":12:"a7f53cc5a228ce63811ba4d7c1f74f7fce62afbf6813f3ca3ae43c11b138086f" + +LMOTS signature leak test +# This test uses a fixed message, and then generates a private key, signs the +# message, and then uses a test hook to check that the signature has not been +# modifier before the private key has been deleted (which could cause signature +# leakage during errors). +lmots_signature_leak_test:"cfcd1e81193e310c9d931d1b00818d14":"00000000000000000000000000000000":12:"a7f53cc5a228ce63811ba4d7c1f74f7fce62afbf6813f3ca3ae43c11b138086f" diff --git a/tests/suites/test_suite_lmots.function b/tests/suites/test_suite_lmots.function index 2a9ea541a4..d7067dfe00 100644 --- a/tests/suites/test_suite_lmots.function +++ b/tests/suites/test_suite_lmots.function @@ -2,6 +2,27 @@ #include "lmots.h" #include "mbedtls/lms.h" +#if defined(MBEDTLS_TEST_HOOKS) +extern int( *mbedtls_lmots_sign_private_key_invalidated_hook )( unsigned char * ); + +int check_lmots_private_key_for_leak(unsigned char * sig) +{ + size_t idx; + + for( idx = MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(MBEDTLS_LMOTS_SHA256_N32_W8); + idx < MBEDTLS_LMOTS_SIG_LEN(MBEDTLS_LMOTS_SHA256_N32_W8); + idx++ ) + { + if( sig[idx] != 0x7E ) { + while(1){} + return 1; + } + } + + return 0; +} +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -33,6 +54,29 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void lmots_sign_verify_null_msg_test ( data_t *key_id, int leaf_id, data_t *seed ) +{ + mbedtls_lmots_public_t pub_ctx; + mbedtls_lmots_private_t priv_ctx; + unsigned char sig[MBEDTLS_LMOTS_SIG_LEN(MBEDTLS_LMOTS_SHA256_N32_W8)]; + + mbedtls_lmots_init_public( &pub_ctx ); + mbedtls_lmots_init_private( &priv_ctx ); + + TEST_ASSERT( mbedtls_lmots_generate_private_key(&priv_ctx, MBEDTLS_LMOTS_SHA256_N32_W8, + key_id->x, leaf_id, seed->x, seed->len ) == 0 ); + TEST_ASSERT( mbedtls_lmots_calculate_public_key(&pub_ctx, &priv_ctx) == 0 ); + TEST_ASSERT( mbedtls_lmots_sign(&priv_ctx, &mbedtls_test_rnd_std_rand, NULL, + NULL, 0, sig, sizeof(sig), NULL ) == 0 ); + TEST_ASSERT( mbedtls_lmots_verify(&pub_ctx, NULL, 0, sig, sizeof(sig)) == 0 ); + +exit: + mbedtls_lmots_free_public( &pub_ctx ); + mbedtls_lmots_free_private( &priv_ctx ); +} +/* END_CASE */ + /* BEGIN_CASE */ void lmots_verify_test ( data_t *msg, data_t *sig, data_t *pub_key, int expected_rc ) @@ -94,3 +138,28 @@ exit: mbedtls_lmots_free_private( &ctx ); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS */ +void lmots_signature_leak_test ( data_t *msg, data_t *key_id, int leaf_id, + data_t *seed ) +{ + mbedtls_lmots_private_t ctx; + unsigned char sig[MBEDTLS_LMOTS_SIG_LEN(MBEDTLS_LMOTS_SHA256_N32_W8)]; + + mbedtls_lmots_sign_private_key_invalidated_hook = &check_lmots_private_key_for_leak; + + /* Fill with recognisable pattern */ + memset( sig, 0x7E, sizeof( sig ) ); + + mbedtls_lmots_init_private( &ctx ); + TEST_ASSERT( mbedtls_lmots_generate_private_key(&ctx, MBEDTLS_LMOTS_SHA256_N32_W8, + key_id->x, leaf_id, seed->x, + seed->len ) == 0 ); + TEST_ASSERT( mbedtls_lmots_sign(&ctx, mbedtls_test_rnd_std_rand, NULL, + msg->x, msg->len, sig, sizeof( sig ), NULL ) == 0 ); + +exit: + mbedtls_lmots_free_private( &ctx ); + mbedtls_lmots_sign_private_key_invalidated_hook = NULL; +} +/* END_CASE */ diff --git a/tests/suites/test_suite_lms.data b/tests/suites/test_suite_lms.data index c17d05ac57..d890dd4ddc 100644 --- a/tests/suites/test_suite_lms.data +++ b/tests/suites/test_suite_lms.data @@ -3,6 +3,11 @@ LMS sign-verify test # message, and verifies the signature. lms_sign_verify_test:"c41ba177a0ca1ec31dfb2e145237e65b":"626201f41afd7c9af793cf158da58e33" +LMS NULL-message sign-verify test +# This test uses a NULL zero-length message, and then generates a private key, +# signs the message, and verifies the signature. +lms_sign_verify_null_msg_test:"923a3c8e38c9b72e067996bfdaa36856" + LMS hash-sigs interop test #1 # This test uses data from https://github.com/cisco/hash-sigs due to the limited # amount of available test vectors for LMS. A public/private key was generated diff --git a/tests/suites/test_suite_lms.function b/tests/suites/test_suite_lms.function index 148075d1b8..900392334d 100644 --- a/tests/suites/test_suite_lms.function +++ b/tests/suites/test_suite_lms.function @@ -44,6 +44,42 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void lms_sign_verify_null_msg_test( data_t *seed ) +{ + mbedtls_lms_public_t pub_ctx; + mbedtls_lms_private_t priv_ctx; + unsigned char sig[MBEDTLS_LMS_SIG_LEN(MBEDTLS_LMS_SHA256_M32_H10, MBEDTLS_LMOTS_SHA256_N32_W8)]; + int rc; + + mbedtls_lms_init_public( &pub_ctx ); + mbedtls_lms_init_private( &priv_ctx ); + + /* Allocation failure isn't a test failure, since it likely just means + * there's not enough memory to run the test. + */ + rc = mbedtls_lms_generate_private_key( &priv_ctx, MBEDTLS_LMS_SHA256_M32_H10, + MBEDTLS_LMOTS_SHA256_N32_W8, + mbedtls_test_rnd_std_rand, NULL, + seed->x, seed->len ); + TEST_ASSUME( rc != MBEDTLS_ERR_LMS_ALLOC_FAILED ); + TEST_ASSERT( rc == 0 ); + + TEST_ASSERT( mbedtls_lms_calculate_public_key( &pub_ctx, &priv_ctx ) == 0 ); + + TEST_ASSERT( mbedtls_lms_sign( &priv_ctx, mbedtls_test_rnd_std_rand, NULL, + NULL, 0, sig, sizeof( sig ), + NULL ) == 0 ); + + TEST_ASSERT( mbedtls_lms_verify( &pub_ctx, NULL, 0, sig, + sizeof( sig ) ) == 0 ); + +exit: + mbedtls_lms_free_public( &pub_ctx ); + mbedtls_lms_free_private( &priv_ctx ); +} +/* END_CASE */ + /* BEGIN_CASE */ void lms_verify_test ( data_t * msg, data_t * sig, data_t * pub_key, int expected_rc )