Add extra LMS and LMOTS tests
NULL-message and LMOTS signature leak tests
Signed-off-by: Raef Coles <raef.coles@arm.com>
diff --git a/library/lmots.c b/library/lmots.c
index 8e56696..d733dc0 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 @@
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 56d23f8..993c01c 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 6b4cc35..0845a17 100644
--- a/tests/suites/test_suite_lmots.data
+++ b/tests/suites/test_suite_lmots.data
@@ -8,6 +8,11 @@
# 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 @@
# 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 2a9ea54..d7067df 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
@@ -34,6 +55,29 @@
/* 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 @@
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 c17d05a..d890dd4 100644
--- a/tests/suites/test_suite_lms.data
+++ b/tests/suites/test_suite_lms.data
@@ -3,6 +3,11 @@
# 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 148075d..9003923 100644
--- a/tests/suites/test_suite_lms.function
+++ b/tests/suites/test_suite_lms.function
@@ -45,6 +45,42 @@
/* 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 )
{