diff --git a/ChangeLog.d/LMS.txt b/ChangeLog.d/LMS.txt new file mode 100644 index 000000000..6de374f86 --- /dev/null +++ b/ChangeLog.d/LMS.txt @@ -0,0 +1,11 @@ +Features + * Add the LMS post-quantum-safe stateful-hash asymmetric signature scheme. + Signature verification is production-ready, but generation is for testing + purposes only. This currently only supports one parameter set + (LMS_SHA256_M32_H10), meaning that each private key can be used to sign + 1024 messages. As such, it is not intended for use in TLS, but instead for + verification of assets transmitted over an insecure channel, particularly + firmware images. + * Add the LM-OTS post-quantum-safe one-time signature scheme, which is + required for LMS. This can be used independently, but each key can only be + used to sign one message so is impractical for most circumstances. diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h index 6b330a75b..0081ca3d6 100644 --- a/include/mbedtls/check_config.h +++ b/include/mbedtls/check_config.h @@ -353,6 +353,16 @@ #error "MBEDTLS_MD_C defined, but not all prerequisites" #endif +#if defined(MBEDTLS_LMS_C) && \ + ! ( defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_256) ) +#error "MBEDTLS_LMS_C requires MBEDTLS_PSA_CRYPTO_C and PSA_WANT_ALG_SHA_256" +#endif + +#if defined(MBEDTLS_LMS_PRIVATE) && \ + ( !defined(MBEDTLS_LMS_C) ) +#error "MBEDTLS_LMS_PRIVATE requires MBEDTLS_LMS_C" +#endif + #if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \ ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) ) #error "MBEDTLS_MEMORY_BUFFER_ALLOC_C defined, but not all prerequisites" diff --git a/include/mbedtls/error.h b/include/mbedtls/error.h index 8b2b9ea58..eb8391311 100644 --- a/include/mbedtls/error.h +++ b/include/mbedtls/error.h @@ -82,6 +82,7 @@ * POLY1305 3 0x0057-0x005B * CHACHAPOLY 2 0x0054-0x0056 * PLATFORM 2 0x0070-0x0072 + * LMS 5 0x0011-0x0019 * * High-level module nr (3 bits - 0x0...-0x7...) * Name ID Nr of Errors diff --git a/include/mbedtls/lms.h b/include/mbedtls/lms.h new file mode 100644 index 000000000..5e03d9b5f --- /dev/null +++ b/include/mbedtls/lms.h @@ -0,0 +1,450 @@ +/** + * \file lms.h + * + * \brief This file provides an API for the LMS post-quantum-safe stateful-hash + public-key signature scheme as defined in RFC8554 and NIST.SP.200-208. + * This implementation currently only supports a single parameter set + * MBEDTLS_LMS_SHA256_M32_H10 in order to reduce complexity. This is one + * of the signature schemes recommended by the IETF draft SUIT standard + * for IOT firmware upgrades (RFC9019). + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_LMS_H +#define MBEDTLS_LMS_H + +#include +#include + +#include "mbedtls/build_info.h" + +#define MBEDTLS_ERR_LMS_BAD_INPUT_DATA -0x0011 /**< Bad data has been input to an LMS function */ +#define MBEDTLS_ERR_LMS_OUT_OF_PRIVATE_KEYS -0x0013 /**< Specified LMS key has utilised all of its private keys */ +#define MBEDTLS_ERR_LMS_VERIFY_FAILED -0x0015 /**< LMS signature verification failed */ +#define MBEDTLS_ERR_LMS_ALLOC_FAILED -0x0017 /**< LMS failed to allocate space for a private key */ +#define MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL -0x0019 /**< Input/output buffer is too small to contain requited data */ + +/* Currently only defined for SHA256, 32 is the max hash output size */ +#define MBEDTLS_LMOTS_N_HASH_LEN_MAX (32u) +#define MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX (34u) +#define MBEDTLS_LMOTS_N_HASH_LEN(type) ((type) == MBEDTLS_LMOTS_SHA256_N32_W8 ? 32u : 0) +#define MBEDTLS_LMOTS_I_KEY_ID_LEN (16u) +#define MBEDTLS_LMOTS_Q_LEAF_ID_LEN (4u) +#define MBEDTLS_LMOTS_TYPE_LEN (4u) +#define MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(type) ((type) == MBEDTLS_LMOTS_SHA256_N32_W8 ? 34u : 0) +#define MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(type) (MBEDTLS_LMOTS_N_HASH_LEN(type)) + +#define MBEDTLS_LMOTS_SIG_LEN(type) (MBEDTLS_LMOTS_TYPE_LEN + \ + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(type) + \ + (MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(type) * \ + MBEDTLS_LMOTS_N_HASH_LEN(type))) + + +#define MBEDTLS_LMS_TYPE_LEN (4) +#define MBEDTLS_LMS_H_TREE_HEIGHT(type) ((type) == MBEDTLS_LMS_SHA256_M32_H10 ? 10u : 0) + +/* The length of a hash output, Currently only imlemented for SHA256. + * Max is 32 bytes. + */ +#define MBEDTLS_LMS_M_NODE_BYTES(type) ((type) == MBEDTLS_LMS_SHA256_M32_H10 ? 32 : 0) +#define MBEDTLS_LMS_M_NODE_BYTES_MAX 32 + +#define MBEDTLS_LMS_SIG_LEN(type, otstype) (MBEDTLS_LMOTS_Q_LEAF_ID_LEN + \ + MBEDTLS_LMOTS_SIG_LEN(otstype) + \ + MBEDTLS_LMS_TYPE_LEN + \ + (MBEDTLS_LMS_H_TREE_HEIGHT(type) * \ + MBEDTLS_LMS_M_NODE_BYTES(type))) + +#define MBEDTLS_LMS_PUBLIC_KEY_LEN(type) (MBEDTLS_LMS_TYPE_LEN + \ + MBEDTLS_LMOTS_TYPE_LEN + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN + \ + MBEDTLS_LMS_M_NODE_BYTES(type)) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** The Identifier of the LMS parameter set, as per + * https://www.iana.org/assignments/leighton-micali-signatures/leighton-micali-signatures.xhtml + * We are only implementing a subset of the types, particularly H10, for the sake of simplicty. + */ +typedef enum { + MBEDTLS_LMS_SHA256_M32_H10 = 0x6, +} mbedtls_lms_algorithm_type_t; + +/** The Identifier of the LMOTS parameter set, as per + * https://www.iana.org/assignments/leighton-micali-signatures/leighton-micali-signatures.xhtml. + * We are only implementing a subset of the types, particularly N32_W8, for the sake of simplicty. + */ +typedef enum { + MBEDTLS_LMOTS_SHA256_N32_W8 = 4 +} mbedtls_lmots_algorithm_type_t; + +/** LMOTS parameters structure. + * + * This contains the metadata associated with an LMOTS key, detailing the + * algorithm type, the key ID, and the leaf identifier should be key be part of + * a LMS key. + */ +typedef struct { + unsigned char MBEDTLS_PRIVATE(I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN]); /*!< The key + identifier. */ + unsigned char MBEDTLS_PRIVATE(q_leaf_identifier[MBEDTLS_LMOTS_Q_LEAF_ID_LEN]); /*!< Which + leaf of the LMS key this is. + 0 if the key is not part of an LMS key. */ + mbedtls_lmots_algorithm_type_t MBEDTLS_PRIVATE(type); /*!< The LM-OTS key type identifier as + per IANA. Only SHA256_N32_W8 is + currently supported. */ +} mbedtls_lmots_parameters_t; + +/** LMOTS public context structure. + * + * A LMOTS public key is a hash output, and the applicable parameter set. + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lmots_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PUBLIC_KEY -> INIT [label="free"]; + * INIT -> HAVE_PUBLIC_KEY [label="import_public_key"]; + * INIT -> HAVE_PUBLIC_KEY [label="calculate_public_key from private key"]; + * HAVE_PUBLIC_KEY -> HAVE_PUBLIC_KEY [label="export_public_key"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lmots_parameters_t MBEDTLS_PRIVATE(params); + unsigned char MBEDTLS_PRIVATE(public_key)[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char MBEDTLS_PRIVATE(have_public_key); /*!< Whether the context contains a public key. + Boolean values only. */ +} mbedtls_lmots_public_t; + +#if defined(MBEDTLS_LMS_PRIVATE) +/** LMOTS private context structure. + * + * A LMOTS private key is one hash output for each of digit of the digest + + * checksum, and the applicable parameter set. + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lmots_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PRIVATE_KEY -> INIT [label="free"]; + * INIT -> HAVE_PRIVATE_KEY [label="generate_private_key"]; + * HAVE_PRIVATE_KEY -> INIT [label="sign"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lmots_parameters_t MBEDTLS_PRIVATE(params); + unsigned char MBEDTLS_PRIVATE(private_key)[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char MBEDTLS_PRIVATE(have_private_key); /*!< Whether the context contains a private key. + Boolean values only. */ +} mbedtls_lmots_private_t; +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + + +/** LMS parameters structure. + * + * This contains the metadata associated with an LMS key, detailing the + * algorithm type, the type of the underlying OTS algorithm, and the key ID. + */ +typedef struct { + unsigned char MBEDTLS_PRIVATE(I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN]); /*!< The key + identifier. */ + mbedtls_lmots_algorithm_type_t MBEDTLS_PRIVATE(otstype); /*!< The LM-OTS key type identifier as + per IANA. Only SHA256_N32_W8 is + currently supported. */ + mbedtls_lms_algorithm_type_t MBEDTLS_PRIVATE(type); /*!< The LMS key type identifier as per + IANA. Only SHA256_M32_H10 is currently + supported. */ +} mbedtls_lms_parameters_t; + +/** LMS public context structure. + * + *A LMS public key is the hash output that is the root of the Merkle tree, and + * the applicable parameter set + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lms_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PUBLIC_KEY -> INIT [label="free"]; + * INIT -> HAVE_PUBLIC_KEY [label="import_public_key"]; + * INIT -> HAVE_PUBLIC_KEY [label="calculate_public_key from private key"]; + * HAVE_PUBLIC_KEY -> HAVE_PUBLIC_KEY [label="export_public_key"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lms_parameters_t MBEDTLS_PRIVATE(params); + unsigned char MBEDTLS_PRIVATE(T_1_pub_key)[MBEDTLS_LMS_M_NODE_BYTES_MAX]; /*!< The public key, in + the form of the Merkle tree root node. */ + unsigned char MBEDTLS_PRIVATE(have_public_key); /*!< Whether the context contains a public key. + Boolean values only. */ +} mbedtls_lms_public_t; + + +#if defined(MBEDTLS_LMS_PRIVATE) +/** LMS private context structure. + * + * A LMS private key is a set of LMOTS private keys, an index to the next usable + * key, and the applicable parameter set. + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lms_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PRIVATE_KEY -> INIT [label="free"]; + * INIT -> HAVE_PRIVATE_KEY [label="generate_private_key"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lms_parameters_t MBEDTLS_PRIVATE(params); + uint32_t MBEDTLS_PRIVATE(q_next_usable_key); /*!< The index of the next OTS key that has not + been used. */ + mbedtls_lmots_private_t *MBEDTLS_PRIVATE(ots_private_keys); /*!< The private key material. One OTS key + for each leaf node in the Merkle tree. NULL + when have_private_key is 0 and non-NULL otherwise. + is 2^MBEDTLS_LMS_H_TREE_HEIGHT(type) in length. */ + mbedtls_lmots_public_t *MBEDTLS_PRIVATE(ots_public_keys); /*!< The OTS key public keys, used to + build the Merkle tree. NULL + when have_private_key is 0 and + non-NULL otherwise. + Is 2^MBEDTLS_LMS_H_TREE_HEIGHT(type) + in length. */ + unsigned char MBEDTLS_PRIVATE(have_private_key); /*!< Whether the context contains a private key. + Boolean values only. */ +} mbedtls_lms_private_t; +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + +/** + * \brief This function initializes an LMS public context + * + * \param ctx The uninitialized LMS context that will then be + * initialized. + */ +void mbedtls_lms_public_init( mbedtls_lms_public_t *ctx ); + +/** + * \brief This function uninitializes an LMS public context + * + * \param ctx The initialized LMS context that will then be + * uninitialized. + */ +void mbedtls_lms_public_free( mbedtls_lms_public_t *ctx ); + +/** + * \brief This function imports an LMS public key into a + * public LMS context. + * + * \note Before this function is called, the context must + * have been initialized. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMS context store the key in. + * \param key The buffer from which the key will be read. + * #MBEDTLS_LMS_PUBLIC_KEY_LEN bytes will be read from + * this. + * \param key_size The size of the key being imported. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_import_public_key( mbedtls_lms_public_t *ctx, + const unsigned char *key, size_t key_size ); + +/** + * \brief This function exports an LMS public key from a + * LMS public context that already contains a public + * key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a public key. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMS public context that contains + * the public key. + * \param key The buffer into which the key will be output. Must + * be at least #MBEDTLS_LMS_PUBLIC_KEY_LEN in size. + * \param key_size The size of the key buffer. + * \param key_len If not NULL, will be written with the size of the + * key. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_export_public_key( const mbedtls_lms_public_t *ctx, + unsigned char *key, size_t key_size, + size_t *key_len ); + +/** + * \brief This function verifies a LMS signature, using a + * LMS context that contains a public key. + * + * \note Before this function is called, the context must + * have been initialized and must contain a public key + * (either by import or generation). + * + * \param ctx The initialized LMS public context from which the + * public key will be read. + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf from which the signature will be read. + * #MBEDTLS_LMS_SIG_LEN bytes will be read from + * this. + * \param sig_size The size of the signature to be verified. + * + * \return \c 0 on successful verification. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_verify( const mbedtls_lms_public_t *ctx, + const unsigned char *msg, size_t msg_size, + const unsigned char *sig, size_t sig_size ); + +#if defined(MBEDTLS_LMS_PRIVATE) +/** + * \brief This function initializes an LMS private context + * + * \param ctx The uninitialized LMS private context that will + * then be initialized. */ +void mbedtls_lms_private_init( mbedtls_lms_private_t *ctx ); + +/** + * \brief This function uninitializes an LMS private context + * + * \param ctx The initialized LMS private context that will then + * be uninitialized. + */ +void mbedtls_lms_private_free( mbedtls_lms_private_t *ctx ); + +/** + * \brief This function generates an LMS private key, and + * stores in into an LMS private context. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note The seed must have at least 256 bits of entropy. + * + * \param ctx The initialized LMOTS context to generate the key + * into. + * \param type The LMS parameter set identifier. + * \param otstype The LMOTS parameter set identifier. + * \param f_rng The RNG function to be used to generate the key ID. + * \param p_rng The RNG context to be passed to f_rng + * \param seed The seed used to deterministically generate the + * key. + * \param seed_size The length of the seed. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_generate_private_key( mbedtls_lms_private_t *ctx, + mbedtls_lms_algorithm_type_t type, + mbedtls_lmots_algorithm_type_t otstype, + int (*f_rng)(void *, unsigned char *, size_t), + void* p_rng, const unsigned char *seed, + size_t seed_size ); + +/** + * \brief This function calculates an LMS public key from a + * LMS context that already contains a private key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a private key. + * + * \param ctx The initialized LMS public context to calculate the key + * from and store it into. + * + * \param priv_ctx The LMS private context to read the private key + * from. This must have been initialized and contain a + * private key. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_calculate_public_key( mbedtls_lms_public_t *ctx, + const mbedtls_lms_private_t *priv_ctx ); + +/** + * \brief This function creates a LMS signature, using a + * LMS context that contains unused private keys. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note Before this function is called, the context must + * have been initialized and must contain a private + * key. + * + * \note Each of the LMOTS private keys inside a LMS private + * key can only be used once. If they are reused, then + * attackers may be able to forge signatures with that + * key. This is all handled transparently, but it is + * important to not perform copy operations on LMS + * contexts that contain private key material. + * + * \param ctx The initialized LMS private context from which the + * private key will be read. + * \param f_rng The RNG function to be used for signature + * generation. + * \param p_rng The RNG context to be passed to f_rng + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf into which the signature will be stored. + * Must be at least #MBEDTLS_LMS_SIG_LEN in size. + * \param sig_size The size of the buffer the signature will be + * written into. + * \param sig_len If not NULL, will be written with the size of the + * signature. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_sign( mbedtls_lms_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void* p_rng, const unsigned char *msg, + unsigned int msg_size, unsigned char *sig, size_t sig_size, + size_t *sig_len ); +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_LMS_H */ diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h index 23e601b32..4188afec9 100644 --- a/include/mbedtls/mbedtls_config.h +++ b/include/mbedtls/mbedtls_config.h @@ -2461,6 +2461,32 @@ */ #define MBEDTLS_HMAC_DRBG_C +/** + * \def MBEDTLS_LMS_C + * + * Enable the LMS stateful-hash asymmetric signature algorithm. + * + * Module: library/lms.c + * Caller: + * + * Requires: MBEDTLS_PSA_CRYPTO_C + * + * Uncomment to enable the LMS verification algorithm and public key operations. + */ +#define MBEDTLS_LMS_C + +/** + * \def MBEDTLS_LMS_PRIVATE + * + * Enable LMS private-key operations and signing code. Functions enabled by this + * option are experimental, and should not be used in production. + * + * Requires: MBEDTLS_LMS_C + * + * Uncomment to enable the LMS signature algorithm and private key operations. + */ +//#define MBEDTLS_LMS_PRIVATE + /** * \def MBEDTLS_NIST_KW_C * diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 951381475..7c325f7ba 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -44,6 +44,8 @@ set(src_crypto hash_info.c hkdf.c hmac_drbg.c + lmots.c + lms.c md.c md5.c memory_buffer_alloc.c diff --git a/library/Makefile b/library/Makefile index 9c3af3b9b..624773dc1 100644 --- a/library/Makefile +++ b/library/Makefile @@ -109,6 +109,8 @@ OBJS_CRYPTO= \ hash_info.o \ hkdf.o \ hmac_drbg.o \ + lmots.o \ + lms.o \ md.o \ md5.o \ memory_buffer_alloc.o \ diff --git a/library/lmots.c b/library/lmots.c new file mode 100644 index 000000000..788063c54 --- /dev/null +++ b/library/lmots.c @@ -0,0 +1,826 @@ +/* + * The LM-OTS one-time public-key signature scheme + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The following sources were referenced in the design of this implementation + * of the LM-OTS algorithm: + * + * [1] IETF RFC8554 + * D. McGrew, M. Curcio, S.Fluhrer + * https://datatracker.ietf.org/doc/html/rfc8554 + * + * [2] NIST Special Publication 800-208 + * David A. Cooper et. al. + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_LMS_C) + +#include + +#include "lmots.h" + +#include "mbedtls/lms.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include "psa/crypto.h" + +#define PUBLIC_KEY_TYPE_OFFSET (0) +#define PUBLIC_KEY_I_KEY_ID_OFFSET (PUBLIC_KEY_TYPE_OFFSET + \ + MBEDTLS_LMOTS_TYPE_LEN) +#define PUBLIC_KEY_Q_LEAF_ID_OFFSET (PUBLIC_KEY_I_KEY_ID_OFFSET + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN) +#define PUBLIC_KEY_KEY_HASH_OFFSET (PUBLIC_KEY_Q_LEAF_ID_OFFSET + \ + MBEDTLS_LMOTS_Q_LEAF_ID_LEN) + +/* We only support parameter sets that use 8-bit digits, as it does not require + * translation logic between digits and bytes */ +#define W_WINTERNITZ_PARAMETER (8u) +#define CHECKSUM_LEN (2) +#define I_DIGIT_IDX_LEN (2) +#define J_HASH_IDX_LEN (1) +#define D_CONST_LEN (2) + +#define DIGIT_MAX_VALUE ((1u << W_WINTERNITZ_PARAMETER) - 1u) + +#define D_CONST_LEN (2) +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 mbedtls_lms_unsigned_int_to_network_bytes( unsigned int val, size_t len, + unsigned char *bytes ) +{ + size_t idx; + + for ( idx = 0; idx < len; idx++ ) + { + bytes[idx] = ( val >> ( ( len - 1 - idx ) * 8 ) ) & 0xFF; + } +} + +unsigned int mbedtls_lms_network_bytes_to_unsigned_int( size_t len, + const unsigned char *bytes ) +{ + size_t idx; + unsigned int val = 0; + + for ( idx = 0; idx < len; idx++ ) + { + val |= ( ( unsigned int )bytes[idx] ) << (8 * ( len - 1 - idx ) ); + } + + return ( val ); +} + +/* Calculate the checksum digits that are appended to the end of the LMOTS digit + * string. See NIST SP800-208 section 3.1 or RFC8554 Algorithm 2 for details of + * the checksum algorithm. + * + * params The LMOTS parameter set, I and q values which + * describe the key being used. + * + * digest The digit string to create the digest from. As + * this does not contain a checksum, it is the same + * size as a hash output. + */ +static unsigned short lmots_checksum_calculate( const mbedtls_lmots_parameters_t *params, + const unsigned char* digest ) +{ + size_t idx; + unsigned sum = 0; + + for ( idx = 0; idx < MBEDTLS_LMOTS_N_HASH_LEN(params->type); idx++ ) + { + sum += DIGIT_MAX_VALUE - digest[idx]; + } + + return ( sum ); +} + +/* Create the string of digest digits (in the base determined by the Winternitz + * parameter with the checksum appended to the end (Q || cksm(Q)). See NIST + * SP800-208 section 3.1 or RFC8554 Algorithm 3 step 5 (also used in Algorithm + * 4b step 3) for details. + * + * params The LMOTS parameter set, I and q values which + * describe the key being used. + * + * msg The message that will be hashed to create the + * digest. + * + * msg_size The size of the message. + * + * C_random_value The random value that will be combined with the + * message digest. This is always the same size as a + * hash output for whichever hash algorithm is + * determined by the parameter set. + * + * output An output containing the digit string (+ + * checksum) of length P digits (in the case of + * MBEDTLS_LMOTS_SHA256_N32_W8, this means it is of + * size P bytes). + */ +static int create_digit_array_with_checksum( const mbedtls_lmots_parameters_t *params, + const unsigned char *msg, + size_t msg_len, + const unsigned char *C_random_value, + unsigned char *out ) +{ + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned short checksum; + + status = psa_hash_setup( &op, PSA_ALG_SHA_256 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, params->q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, D_MESSAGE_CONSTANT_BYTES, D_CONST_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, C_random_value, + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(params->type) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, msg, msg_len ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_finish( &op, out, + MBEDTLS_LMOTS_N_HASH_LEN(params->type), + &output_hash_len ); + if( status != PSA_SUCCESS ) + goto exit; + + checksum = lmots_checksum_calculate( params, out ); + mbedtls_lms_unsigned_int_to_network_bytes( checksum, CHECKSUM_LEN, + out + MBEDTLS_LMOTS_N_HASH_LEN(params->type) ); + +exit: + psa_hash_abort( &op ); + + return( mbedtls_lms_error_from_psa( status ) ); +} + +/* Hash each element of the string of digits (+ checksum), producing a hash + * output for each element. This is used in several places (by varying the + * hash_idx_min/max_values) in order to calculate a public key from a private + * key (RFC8554 Algorithm 1 step 4), in order to sign a message (RFC8554 + * Algorithm 3 step 5), and to calculate a public key candidate from a + * signature and message (RFC8554 Algorithm 4b step 3). + * + * params The LMOTS parameter set, I and q values which + * describe the key being used. + * + * x_digit_array The array of digits (of size P, 34 in the case of + * MBEDTLS_LMOTS_SHA256_N32_W8). + * + * hash_idx_min_values An array of the starting values of the j iterator + * for each of the members of the digit array. If + * this value in NULL, then all iterators will start + * at 0. + * + * hash_idx_max_values An array of the upper bound values of the j + * iterator for each of the members of the digit + * array. If this value in NULL, then iterator is + * bounded to be less than 2^w - 1 (255 in the case + * of MBEDTLS_LMOTS_SHA256_N32_W8) + * + * output An array containing a hash output for each member + * of the digit string P. In the case of + * MBEDTLS_LMOTS_SHA256_N32_W8, this is of size 32 * + * 34. + */ +static int hash_digit_array( const mbedtls_lmots_parameters_t *params, + const unsigned char *x_digit_array, + const unsigned char *hash_idx_min_values, + const unsigned char *hash_idx_max_values, + unsigned char *output ) +{ + unsigned int i_digit_idx; + unsigned char i_digit_idx_bytes[I_DIGIT_IDX_LEN]; + unsigned int j_hash_idx; + unsigned char j_hash_idx_bytes[J_HASH_IDX_LEN]; + unsigned int j_hash_idx_min; + unsigned int j_hash_idx_max; + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned char tmp_hash[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + + for ( i_digit_idx = 0; + i_digit_idx < MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(params->type); + i_digit_idx++ ) + { + + memcpy( tmp_hash, + &x_digit_array[i_digit_idx * MBEDTLS_LMOTS_N_HASH_LEN(params->type)], + MBEDTLS_LMOTS_N_HASH_LEN(params->type) ); + + j_hash_idx_min = hash_idx_min_values != NULL ? + hash_idx_min_values[i_digit_idx] : 0; + j_hash_idx_max = hash_idx_max_values != NULL ? + hash_idx_max_values[i_digit_idx] : DIGIT_MAX_VALUE; + + for ( j_hash_idx = j_hash_idx_min; + j_hash_idx < j_hash_idx_max; + j_hash_idx++ ) + { + status = psa_hash_setup( &op, PSA_ALG_SHA_256 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, + params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, + params->q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + mbedtls_lms_unsigned_int_to_network_bytes( i_digit_idx, + I_DIGIT_IDX_LEN, + i_digit_idx_bytes ); + status = psa_hash_update( &op, i_digit_idx_bytes, I_DIGIT_IDX_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + mbedtls_lms_unsigned_int_to_network_bytes( j_hash_idx, + J_HASH_IDX_LEN, + j_hash_idx_bytes ); + status = psa_hash_update( &op, j_hash_idx_bytes, J_HASH_IDX_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, tmp_hash, + MBEDTLS_LMOTS_N_HASH_LEN(params->type) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_finish( &op, tmp_hash, sizeof( tmp_hash ), + &output_hash_len ); + if( status != PSA_SUCCESS ) + goto exit; + + psa_hash_abort( &op ); + } + + memcpy( &output[i_digit_idx * MBEDTLS_LMOTS_N_HASH_LEN(params->type)], + tmp_hash, MBEDTLS_LMOTS_N_HASH_LEN(params->type) ); + } + +exit: + psa_hash_abort( &op ); + mbedtls_platform_zeroize( tmp_hash, sizeof( tmp_hash ) ); + + return( mbedtls_lms_error_from_psa( status ) ); +} + +/* Combine the hashes of the digit array into a public key. This is used in + * in order to calculate a public key from a private key (RFC8554 Algorithm 1 + * step 4), and to calculate a public key candidate from a signature and message + * (RFC8554 Algorithm 4b step 3). + * + * params The LMOTS parameter set, I and q values which describe + * the key being used. + * y_hashed_digits The array of hashes, one hash for each digit of the + * symbol array (which is of size P, 34 in the case of + * MBEDTLS_LMOTS_SHA256_N32_W8) + * + * pub_key The output public key (or candidate public key in + * case this is being run as part of signature + * verification), in the form of a hash output. + */ +static int public_key_from_hashed_digit_array( const mbedtls_lmots_parameters_t *params, + const unsigned char *y_hashed_digits, + unsigned char *pub_key ) +{ + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + + status = psa_hash_setup( &op, PSA_ALG_SHA_256 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, + params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, params->q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, D_PUBLIC_CONSTANT_BYTES, D_CONST_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, y_hashed_digits, + MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(params->type) * + MBEDTLS_LMOTS_N_HASH_LEN(params->type) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_finish( &op, pub_key, + MBEDTLS_LMOTS_N_HASH_LEN(params->type), + &output_hash_len ); + if( status != PSA_SUCCESS ) + +exit: + psa_hash_abort( &op ); + + return( mbedtls_lms_error_from_psa( status ) ); +} + +int mbedtls_lms_error_from_psa( psa_status_t status ) +{ + switch( status ) + { + case PSA_SUCCESS: + return( 0 ); + case PSA_ERROR_HARDWARE_FAILURE: + return( MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED ); + case PSA_ERROR_NOT_SUPPORTED: + return( MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED ); + case PSA_ERROR_BUFFER_TOO_SMALL: + return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL ); + case PSA_ERROR_INVALID_ARGUMENT: + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + default: + return( MBEDTLS_ERR_ERROR_GENERIC_ERROR ); + } +} + +void mbedtls_lmots_public_init( mbedtls_lmots_public_t *ctx ) +{ + memset( ctx, 0, sizeof( *ctx ) ) ; +} + +void mbedtls_lmots_public_free( mbedtls_lmots_public_t *ctx ) +{ + mbedtls_platform_zeroize( ctx, sizeof( *ctx ) ) ; +} + +int mbedtls_lmots_import_public_key( mbedtls_lmots_public_t *ctx, + const unsigned char *key, size_t key_len ) +{ + if( key_len < MBEDTLS_LMOTS_SIG_TYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + ctx->params.type = + mbedtls_lms_network_bytes_to_unsigned_int( MBEDTLS_LMOTS_TYPE_LEN, + key + MBEDTLS_LMOTS_SIG_TYPE_OFFSET ); + + if( key_len != MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type) ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + memcpy( ctx->params.I_key_identifier, + key + PUBLIC_KEY_I_KEY_ID_OFFSET, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + + memcpy( ctx->params.q_leaf_identifier, + key + PUBLIC_KEY_Q_LEAF_ID_OFFSET, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN ); + + memcpy( ctx->public_key, + key + PUBLIC_KEY_KEY_HASH_OFFSET, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type) ); + + ctx->have_public_key = 1; + + return( 0 ); +} + +int mbedtls_lmots_export_public_key( const mbedtls_lmots_public_t *ctx, + unsigned char *key, size_t key_size, + size_t *key_len ) +{ + if( key_size < MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type) ) + { + return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL ); + } + + if( ! ctx->have_public_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + mbedtls_lms_unsigned_int_to_network_bytes( ctx->params.type, + MBEDTLS_LMOTS_TYPE_LEN, + key + MBEDTLS_LMOTS_SIG_TYPE_OFFSET ); + + memcpy( key + PUBLIC_KEY_I_KEY_ID_OFFSET, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + + memcpy( key + PUBLIC_KEY_Q_LEAF_ID_OFFSET, + ctx->params.q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN ); + + memcpy( key + PUBLIC_KEY_KEY_HASH_OFFSET, ctx->public_key, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type) ); + + if( key_len != NULL ) + { + *key_len = MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type); + } + + return( 0 ); +} + +int mbedtls_lmots_calculate_public_key_candidate( const mbedtls_lmots_parameters_t *params, + const unsigned char *msg, + size_t msg_size, + const unsigned char *sig, + size_t sig_size, + unsigned char *out, + size_t out_size, + size_t *out_len ) +{ + unsigned char tmp_digit_array[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX]; + unsigned char y_hashed_digits[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( msg == NULL && msg_size != 0 ) + { + return ( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( sig_size != MBEDTLS_LMOTS_SIG_LEN(params->type) || + out_size < MBEDTLS_LMOTS_N_HASH_LEN(params->type) ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + ret = create_digit_array_with_checksum( params, msg, msg_size, + sig + MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET, + tmp_digit_array ); + if( ret ) + { + return ( ret ); + } + + ret = hash_digit_array( params, + sig + MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(params->type), + tmp_digit_array, NULL, ( unsigned char * )y_hashed_digits ); + if( ret ) + { + return ( ret ); + } + + ret = public_key_from_hashed_digit_array( params, + ( unsigned char * )y_hashed_digits, + out ); + if( ret ) + { + return ( ret ); + } + + if( out_len != NULL ) + { + *out_len = MBEDTLS_LMOTS_N_HASH_LEN(params->type); + } + + return( 0 ); +} + +int mbedtls_lmots_verify( const mbedtls_lmots_public_t *ctx, + const unsigned char *msg, size_t msg_size, + const unsigned char *sig, size_t sig_size ) +{ + unsigned char Kc_public_key_candidate[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( msg == NULL && msg_size != 0 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( !ctx->have_public_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( ctx->params.type != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( sig_size < MBEDTLS_LMOTS_SIG_TYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + if( mbedtls_lms_network_bytes_to_unsigned_int( MBEDTLS_LMOTS_TYPE_LEN, + sig + MBEDTLS_LMOTS_SIG_TYPE_OFFSET ) != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + ret = mbedtls_lmots_calculate_public_key_candidate( &ctx->params, + msg, msg_size, sig, sig_size, + Kc_public_key_candidate, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type), + NULL ); + if( ret ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + if( memcmp( &Kc_public_key_candidate, ctx->public_key, + sizeof( ctx->public_key ) ) ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + return( 0 ); +} + +#if defined(MBEDTLS_LMS_PRIVATE) + +void mbedtls_lmots_private_init( mbedtls_lmots_private_t *ctx ) +{ + memset( ctx, 0, sizeof( *ctx ) ) ; +} + +void mbedtls_lmots_private_free( mbedtls_lmots_private_t *ctx ) +{ + mbedtls_platform_zeroize( ctx, sizeof( *ctx ) ) ; +} + +int mbedtls_lmots_generate_private_key( mbedtls_lmots_private_t *ctx, + mbedtls_lmots_algorithm_type_t type, + const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN], + uint32_t q_leaf_identifier, + const unsigned char *seed, + size_t seed_size ) +{ + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned int i_digit_idx; + unsigned char i_digit_idx_bytes[2]; + unsigned char const_bytes[1]; + + if( ctx->have_private_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( type != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + ctx->params.type = type; + + memcpy( ctx->params.I_key_identifier, + I_key_identifier, + sizeof( ctx->params.I_key_identifier ) ); + + mbedtls_lms_unsigned_int_to_network_bytes( q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, + ctx->params.q_leaf_identifier ); + + mbedtls_lms_unsigned_int_to_network_bytes( 0xFF, sizeof( const_bytes ), + const_bytes ); + + for ( i_digit_idx = 0; + i_digit_idx < MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(ctx->params.type); + i_digit_idx++ ) + { + status = psa_hash_setup( &op, PSA_ALG_SHA_256 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, + ctx->params.I_key_identifier, + sizeof( ctx->params.I_key_identifier ) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, + ctx->params.q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + mbedtls_lms_unsigned_int_to_network_bytes( i_digit_idx, I_DIGIT_IDX_LEN, + i_digit_idx_bytes ); + status = psa_hash_update( &op, i_digit_idx_bytes, I_DIGIT_IDX_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, const_bytes, sizeof( const_bytes ) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, seed, seed_size ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_finish( &op, + ctx->private_key[i_digit_idx], + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type), + &output_hash_len ); + if( status != PSA_SUCCESS ) + goto exit; + + psa_hash_abort( &op ); + } + + ctx->have_private_key = 1; + +exit: + psa_hash_abort( &op ); + + return ( mbedtls_lms_error_from_psa( status ) ); +} + +int mbedtls_lmots_calculate_public_key( mbedtls_lmots_public_t *ctx, + const mbedtls_lmots_private_t *priv_ctx ) +{ + unsigned char y_hashed_digits[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Check that a private key is loaded */ + if( !priv_ctx->have_private_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + ret = hash_digit_array( &priv_ctx->params, + ( unsigned char * )priv_ctx->private_key, NULL, + NULL, ( unsigned char * )y_hashed_digits ); + if( ret ) + { + goto exit; + } + + ret = public_key_from_hashed_digit_array( &priv_ctx->params, + ( unsigned char * )y_hashed_digits, + ctx->public_key ); + if( ret ) + { + goto exit; + } + + memcpy( &ctx->params, &priv_ctx->params, + sizeof( ctx->params ) ); + + ctx->have_public_key = 1; + +exit: + mbedtls_platform_zeroize( y_hashed_digits, sizeof( y_hashed_digits ) ); + + return( ret ); +} + +int mbedtls_lmots_sign( mbedtls_lmots_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *msg, size_t msg_size, + unsigned char *sig, size_t sig_size, size_t* sig_len ) +{ + unsigned char tmp_digit_array[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX]; + /* Create a temporary buffer to prepare the signature in. This allows us to + * finish creating a signature (ensuring the process doesn't fail), and then + * erase the private key **before** writing any data into the sig parameter + * buffer. If data were directly written into the sig buffer, it might leak + * a partial signature on failure, which effectively compromises the private + * key. + */ + unsigned char tmp_sig[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char tmp_c_random[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( msg == NULL && msg_size != 0 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( sig_size < MBEDTLS_LMOTS_SIG_LEN(ctx->params.type) ) + { + return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL ); + } + + /* Check that a private key is loaded */ + if( !ctx->have_private_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + ret = f_rng( p_rng, tmp_c_random, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type) ); + if( ret ) + { + return( ret ); + } + + ret = create_digit_array_with_checksum( &ctx->params, + msg, msg_size, + tmp_c_random, + tmp_digit_array ); + if( ret ) + { + goto exit; + } + + ret = hash_digit_array( &ctx->params, ( unsigned char * )ctx->private_key, + NULL, tmp_digit_array, ( unsigned char * )tmp_sig ); + if( ret ) + { + goto exit; + } + + mbedtls_lms_unsigned_int_to_network_bytes( ctx->params.type, + 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. + */ + ctx->have_private_key = 0; + mbedtls_platform_zeroize( ctx->private_key, + sizeof( ctx->private_key ) ); + + memcpy( sig + MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET, tmp_c_random, + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(ctx->params.type) ); + + memcpy( sig + MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(ctx->params.type), tmp_sig, + MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(ctx->params.type) + * MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type) ); + + if( sig_len != NULL ) + { + *sig_len = MBEDTLS_LMOTS_SIG_LEN(ctx->params.type); + } + + ret = 0; + +exit: + mbedtls_platform_zeroize( tmp_digit_array, sizeof( tmp_digit_array ) ); + mbedtls_platform_zeroize( tmp_sig, sizeof( tmp_sig ) ); + + return ( ret ); +} + +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ +#endif /* defined(MBEDTLS_LMS_C) */ diff --git a/library/lmots.h b/library/lmots.h new file mode 100644 index 000000000..39e869925 --- /dev/null +++ b/library/lmots.h @@ -0,0 +1,322 @@ +/** + * \file lmots.h + * + * \brief This file provides an API for the LM-OTS post-quantum-safe one-time + * public-key signature scheme as defined in RFC8554 and NIST.SP.200-208. + * This implementation currently only supports a single parameter set + * MBEDTLS_LMOTS_SHA256_N32_W8 in order to reduce complexity. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_LMOTS_H +#define MBEDTLS_LMOTS_H + +#include "mbedtls/build_info.h" + +#include "psa/crypto.h" + +#include "mbedtls/lms.h" + +#include +#include + + +#define MBEDTLS_LMOTS_PUBLIC_KEY_LEN(type) (MBEDTLS_LMOTS_TYPE_LEN + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN + \ + MBEDTLS_LMOTS_Q_LEAF_ID_LEN + \ + 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" { +#endif + + +#if defined(MBEDTLS_TEST_HOOKS) +extern int( *mbedtls_lmots_sign_private_key_invalidated_hook )( unsigned char * ); +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + +/** + * \brief This function converts an unsigned int into a + * network-byte-order (big endian) string. + * + * \param val The unsigned integer value + * \param len The length of the string. + * \param bytes The string to output into. + */ +void mbedtls_lms_unsigned_int_to_network_bytes( unsigned int val, size_t len, + unsigned char *bytes ); + +/** + * \brief This function converts a network-byte-order + * (big endian) string into an unsigned integer. + * + * \param len The length of the string. + * \param bytes The string. + * + * \return The corresponding LMS error code. + */ +unsigned int mbedtls_lms_network_bytes_to_unsigned_int( size_t len, + const unsigned char *bytes ); + +/** + * \brief This function converts a \ref psa_status_t to a + * low-level LMS error code. + * + * \param status The psa_status_t to convert + * + * \return The corresponding LMS error code. + */ +int mbedtls_lms_error_from_psa( psa_status_t status ); + + +/** + * \brief This function initializes a public LMOTS context + * + * \param ctx The uninitialized LMOTS context that will then be + * initialized. + */ +void mbedtls_lmots_public_init( mbedtls_lmots_public_t *ctx ); + +/** + * \brief This function uninitializes a public LMOTS context + * + * \param ctx The initialized LMOTS context that will then be + * uninitialized. + */ +void mbedtls_lmots_public_free( mbedtls_lmots_public_t *ctx ); + +/** + * \brief This function imports an LMOTS public key into a + * LMOTS context. + * + * \note Before this function is called, the context must + * have been initialized. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMOTS context store the key in. + * \param key The buffer from which the key will be read. + * #MBEDTLS_LMOTS_PUBLIC_KEY_LEN bytes will be read + * from this. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_import_public_key( mbedtls_lmots_public_t *ctx, + const unsigned char *key, size_t key_size ); + +/** + * \brief This function exports an LMOTS public key from a + * LMOTS context that already contains a public key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a public key. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMOTS context that contains the + * publc key. + * \param key The buffer into which the key will be output. Must + * be at least #MBEDTLS_LMOTS_PUBLIC_KEY_LEN in size. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_export_public_key( const mbedtls_lmots_public_t *ctx, + unsigned char *key, size_t key_size, + size_t *key_len ); + +/** + * \brief This function creates a candidate public key from + * an LMOTS signature. This can then be compared to + * the real public key to determine the validity of + * the signature. + * + * \note This function is exposed publicly to be used in LMS + * signature verification, it is expected that + * mbedtls_lmots_verify will be used for LMOTS + * signature verification. + * + * \param params The LMOTS parameter set, q and I values as an + * mbedtls_lmots_parameters_t struct. + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buffer from which the signature will be read. + * #MBEDTLS_LMOTS_SIG_LEN bytes will be read from + * this. + * \param out The buffer where the candidate public key will be + * stored. Must be at least #MBEDTLS_LMOTS_N_HASH_LEN + * bytes in size. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_calculate_public_key_candidate( const mbedtls_lmots_parameters_t *params, + const unsigned char *msg, + size_t msg_size, + const unsigned char *sig, + size_t sig_size, + unsigned char *out, + size_t out_size, + size_t *out_len ); + +/** + * \brief This function verifies a LMOTS signature, using a + * LMOTS context that contains a public key. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note Before this function is called, the context must + * have been initialized and must contain a public key + * (either by import or calculation from a private + * key). + * + * \param ctx The initialized LMOTS context from which the public + * key will be read. + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf from which the signature will be read. + * #MBEDTLS_LMOTS_SIG_LEN bytes will be read from + * this. + * + * \return \c 0 on successful verification. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_verify( const mbedtls_lmots_public_t *ctx, + const unsigned char *msg, + size_t msg_size, const unsigned char *sig, + size_t sig_size ); + +#if defined(MBEDTLS_LMS_PRIVATE) + +/** + * \brief This function initializes a private LMOTS context + * + * \param ctx The uninitialized LMOTS context that will then be + * initialized. + */ +void mbedtls_lmots_private_init( mbedtls_lmots_private_t *ctx ); + +/** + * \brief This function uninitializes a private LMOTS context + * + * \param ctx The initialized LMOTS context that will then be + * uninitialized. + */ +void mbedtls_lmots_private_free( mbedtls_lmots_private_t *ctx ); + +/** + * \brief This function calculates an LMOTS private key, and + * stores in into an LMOTS context. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note The seed must have at least 256 bits of entropy. + * + * \param ctx The initialized LMOTS context to generate the key + * into. + * \param I_key_identifier The key identifier of the key, as a 16-byte string. + * \param q_leaf_identifier The leaf identifier of key. If this LMOTS key is + * not being used as part of an LMS key, this should + * be set to 0. + * \param seed The seed used to deterministically generate the + * key. + * \param seed_size The length of the seed. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_generate_private_key( mbedtls_lmots_private_t *ctx, + mbedtls_lmots_algorithm_type_t type, + const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN], + uint32_t q_leaf_identifier, + const unsigned char *seed, + size_t seed_size ); + +/** + * \brief This function generates an LMOTS public key from a + * LMOTS context that already contains a private key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a private key. + * + * \param ctx The initialized LMOTS context to generate the key + * from and store it into. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_calculate_public_key( mbedtls_lmots_public_t *ctx, + const mbedtls_lmots_private_t *priv_ctx ); + +/** + * \brief This function creates a LMOTS signature, using a + * LMOTS context that contains a private key. + * + * \note Before this function is called, the context must + * have been initialized and must contain a private + * key. + * + * \note LMOTS private keys can only be used once, otherwise + * attackers may be able to create forged signatures. + * If the signing operation is successful, the private + * key in the context will be erased, and no further + * signing will be possible until another private key + * is loaded + * + * \param ctx The initialized LMOTS context from which the + * private key will be read. + * \param f_rng The RNG function to be used for signature + * generation. + * \param p_rng The RNG context to be passed to f_rng + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf into which the signature will be stored. + * Must be at least #MBEDTLS_LMOTS_SIG_LEN in size. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_sign( mbedtls_lmots_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *msg, size_t msg_size, + unsigned char *sig, size_t sig_size, size_t* sig_len ); + +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_LMOTS_H */ diff --git a/library/lms.c b/library/lms.c new file mode 100644 index 000000000..46ea567f2 --- /dev/null +++ b/library/lms.c @@ -0,0 +1,789 @@ +/* + * The LMS stateful-hash public-key signature scheme + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The following sources were referenced in the design of this implementation + * of the LMS algorithm: + * + * [1] IETF RFC8554 + * D. McGrew, M. Curcio, S.Fluhrer + * https://datatracker.ietf.org/doc/html/rfc8554 + * + * [2] NIST Special Publication 800-208 + * David A. Cooper et. al. + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_LMS_C) + +#include + +#include "lmots.h" + +#include "psa/crypto.h" + +#include "mbedtls/lms.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" + +#include "mbedtls/platform.h" + +#define SIG_Q_LEAF_ID_OFFSET (0) +#define SIG_OTS_SIG_OFFSET (SIG_Q_LEAF_ID_OFFSET + \ + MBEDTLS_LMOTS_Q_LEAF_ID_LEN) +#define SIG_TYPE_OFFSET(otstype) (SIG_OTS_SIG_OFFSET + \ + MBEDTLS_LMOTS_SIG_LEN(otstype)) +#define SIG_PATH_OFFSET(otstype) (SIG_TYPE_OFFSET(otstype) + \ + MBEDTLS_LMS_TYPE_LEN) + +#define PUBLIC_KEY_TYPE_OFFSET (0) +#define PUBLIC_KEY_OTSTYPE_OFFSET (PUBLIC_KEY_TYPE_OFFSET + \ + MBEDTLS_LMS_TYPE_LEN) +#define PUBLIC_KEY_I_KEY_ID_OFFSET (PUBLIC_KEY_OTSTYPE_OFFSET + \ + MBEDTLS_LMOTS_TYPE_LEN) +#define PUBLIC_KEY_ROOT_NODE_OFFSET (PUBLIC_KEY_I_KEY_ID_OFFSET + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN) + + +/* Currently only support H=10 */ +#define H_TREE_HEIGHT_MAX 10 +#define MERKLE_TREE_NODE_AM_MAX (1u << (H_TREE_HEIGHT_MAX + 1u)) +#define MERKLE_TREE_NODE_AM(type) (1u << (MBEDTLS_LMS_H_TREE_HEIGHT(type) + 1u)) +#define MERKLE_TREE_LEAF_NODE_AM(type) (1u << MBEDTLS_LMS_H_TREE_HEIGHT(type)) +#define MERKLE_TREE_INTERNAL_NODE_AM(type) (1u << MBEDTLS_LMS_H_TREE_HEIGHT(type)) + +#define D_CONST_LEN (2) +static const unsigned char D_LEAF_CONSTANT_BYTES[D_CONST_LEN] = {0x82, 0x82}; +static const unsigned char D_INTR_CONSTANT_BYTES[D_CONST_LEN] = {0x83, 0x83}; + + +/* Calculate the value of a leaf node of the Merkle tree (which is a hash of a + * public key and some other parameters like the leaf index). This function + * implements RFC8554 section 5.3, in the case where r >= 2^h. + * + * params The LMS parameter set, the underlying LMOTS + * parameter set, and I value which describe the key + * being used. + * + * pub_key The public key of the private whose index + * corresponds to the index of this leaf node. This + * is a hash output. + * + * r_node_idx The index of this node in the Merkle tree. Note + * that the root node of the Merkle tree is + * 1-indexed. + * + * out The output node value, which is a hash output. + */ +static int create_merkle_leaf_value( const mbedtls_lms_parameters_t *params, + unsigned char *pub_key, + unsigned int r_node_idx, + unsigned char *out ) +{ + psa_hash_operation_t op; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned char r_node_idx_bytes[4]; + + op = psa_hash_operation_init( ); + status = psa_hash_setup( &op, PSA_ALG_SHA_256 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + mbedtls_lms_unsigned_int_to_network_bytes( r_node_idx, 4, r_node_idx_bytes ); + status = psa_hash_update( &op, r_node_idx_bytes, 4 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, D_LEAF_CONSTANT_BYTES, D_CONST_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, pub_key, + MBEDTLS_LMOTS_N_HASH_LEN(params->otstype) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_finish( &op, out, MBEDTLS_LMS_M_NODE_BYTES(params->type), + &output_hash_len ); + if( status != PSA_SUCCESS ) + goto exit; + +exit: + psa_hash_abort( &op ); + + return ( mbedtls_lms_error_from_psa( status ) ); +} + +/* Calculate the value of an internal node of the Merkle tree (which is a hash + * of a public key and some other parameters like the node index). This function + * implements RFC8554 section 5.3, in the case where r < 2^h. + * + * params The LMS parameter set, the underlying LMOTS + * parameter set, and I value which describe the key + * being used. + * + * left_node The value of the child of this node which is on + * the left-hand side. As with all nodes on the + * Merkle tree, this is a hash output. + * + * right_node The value of the child of this node which is on + * the right-hand side. As with all nodes on the + * Merkle tree, this is a hash output. + * + * r_node_idx The index of this node in the Merkle tree. Note + * that the root node of the Merkle tree is + * 1-indexed. + * + * out The output node value, which is a hash output. + */ +static int create_merkle_internal_value( const mbedtls_lms_parameters_t *params, + const unsigned char *left_node, + const unsigned char *right_node, + unsigned int r_node_idx, + unsigned char *out ) +{ + psa_hash_operation_t op; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned char r_node_idx_bytes[4]; + + op = psa_hash_operation_init( ); + status = psa_hash_setup( &op, PSA_ALG_SHA_256 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + mbedtls_lms_unsigned_int_to_network_bytes( r_node_idx, 4, r_node_idx_bytes ); + status = psa_hash_update( &op, r_node_idx_bytes, 4 ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, D_INTR_CONSTANT_BYTES, D_CONST_LEN ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, left_node, + MBEDTLS_LMS_M_NODE_BYTES(params->type) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &op, right_node, + MBEDTLS_LMS_M_NODE_BYTES(params->type) ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_finish( &op, out, MBEDTLS_LMS_M_NODE_BYTES(params->type), + &output_hash_len ); + if( status != PSA_SUCCESS ) + goto exit; + +exit: + psa_hash_abort( &op ); + + return( mbedtls_lms_error_from_psa( status ) ); +} + +void mbedtls_lms_public_init( mbedtls_lms_public_t *ctx ) +{ + memset( ctx, 0, sizeof( *ctx ) ) ; +} + +void mbedtls_lms_public_free( mbedtls_lms_public_t *ctx ) +{ + mbedtls_platform_zeroize( ctx, sizeof( *ctx ) ); +} + +int mbedtls_lms_import_public_key( mbedtls_lms_public_t *ctx, + const unsigned char *key, size_t key_size ) +{ + mbedtls_lms_algorithm_type_t type; + mbedtls_lmots_algorithm_type_t otstype; + + type = mbedtls_lms_network_bytes_to_unsigned_int( MBEDTLS_LMS_TYPE_LEN, + key + PUBLIC_KEY_TYPE_OFFSET ); + if( type != MBEDTLS_LMS_SHA256_M32_H10 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + ctx->params.type = type; + + if( key_size != MBEDTLS_LMS_PUBLIC_KEY_LEN(ctx->params.type) ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + otstype = mbedtls_lms_network_bytes_to_unsigned_int( MBEDTLS_LMOTS_TYPE_LEN, + key + PUBLIC_KEY_OTSTYPE_OFFSET ); + if( otstype != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + ctx->params.otstype = otstype; + + memcpy( ctx->params.I_key_identifier, + key + PUBLIC_KEY_I_KEY_ID_OFFSET, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + memcpy( ctx->T_1_pub_key, key + PUBLIC_KEY_ROOT_NODE_OFFSET, + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type) ); + + ctx->have_public_key = 1; + + return( 0 ); +} + +int mbedtls_lms_export_public_key( const mbedtls_lms_public_t *ctx, + unsigned char *key, + size_t key_size, size_t *key_len ) +{ + if( key_size < MBEDTLS_LMS_PUBLIC_KEY_LEN(ctx->params.type) ) + { + return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL ); + } + + if( ! ctx->have_public_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + mbedtls_lms_unsigned_int_to_network_bytes( + ctx->params.type, + MBEDTLS_LMS_TYPE_LEN, key + PUBLIC_KEY_TYPE_OFFSET ); + mbedtls_lms_unsigned_int_to_network_bytes( ctx->params.otstype, + MBEDTLS_LMOTS_TYPE_LEN, + key + PUBLIC_KEY_OTSTYPE_OFFSET ); + memcpy( key + PUBLIC_KEY_I_KEY_ID_OFFSET, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + memcpy( key +PUBLIC_KEY_ROOT_NODE_OFFSET, + ctx->T_1_pub_key, + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type) ); + + if( key_len != NULL ) + { + *key_len = MBEDTLS_LMS_PUBLIC_KEY_LEN(ctx->params.type); + } + + return( 0 ); +} + +int mbedtls_lms_verify( const mbedtls_lms_public_t *ctx, + const unsigned char *msg, size_t msg_size, + const unsigned char *sig, size_t sig_size ) +{ + unsigned int q_leaf_identifier; + unsigned char Kc_candidate_ots_pub_key[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char Tc_candidate_root_node[MBEDTLS_LMS_M_NODE_BYTES_MAX]; + unsigned int height; + unsigned int curr_node_id; + unsigned int parent_node_id; + const unsigned char* left_node; + const unsigned char* right_node; + mbedtls_lmots_parameters_t ots_params; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( ! ctx->have_public_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( ctx->params.type + != MBEDTLS_LMS_SHA256_M32_H10 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( ctx->params.otstype + != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( sig_size != MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype) ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + if( sig_size < SIG_OTS_SIG_OFFSET + MBEDTLS_LMOTS_TYPE_LEN ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + if( mbedtls_lms_network_bytes_to_unsigned_int( MBEDTLS_LMOTS_TYPE_LEN, + sig + SIG_OTS_SIG_OFFSET + MBEDTLS_LMOTS_SIG_TYPE_OFFSET ) + != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + if( sig_size < SIG_TYPE_OFFSET(ctx->params.otstype) + MBEDTLS_LMS_TYPE_LEN ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + if( mbedtls_lms_network_bytes_to_unsigned_int( MBEDTLS_LMS_TYPE_LEN, + sig + SIG_TYPE_OFFSET(ctx->params.otstype)) + != MBEDTLS_LMS_SHA256_M32_H10 ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + + q_leaf_identifier = mbedtls_lms_network_bytes_to_unsigned_int( + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, sig + SIG_Q_LEAF_ID_OFFSET ); + + if( q_leaf_identifier >= MERKLE_TREE_LEAF_NODE_AM(ctx->params.type) ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + memcpy( ots_params.I_key_identifier, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + mbedtls_lms_unsigned_int_to_network_bytes( q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, + ots_params.q_leaf_identifier ); + ots_params.type = ctx->params.otstype; + + ret = mbedtls_lmots_calculate_public_key_candidate( &ots_params, msg, + msg_size, sig + SIG_OTS_SIG_OFFSET, + MBEDTLS_LMOTS_SIG_LEN(ctx->params.otstype), Kc_candidate_ots_pub_key, + sizeof( Kc_candidate_ots_pub_key ), NULL ); + if( ret != 0 ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + create_merkle_leaf_value( + &ctx->params, + Kc_candidate_ots_pub_key, + MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + q_leaf_identifier, + Tc_candidate_root_node ); + + curr_node_id = MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + + q_leaf_identifier; + + for( height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT(ctx->params.type); + height++ ) + { + parent_node_id = curr_node_id / 2; + + /* Left/right node ordering matters for the hash */ + if( curr_node_id & 1 ) + { + left_node = sig + SIG_PATH_OFFSET(ctx->params.otstype) + + height * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type); + right_node = Tc_candidate_root_node; + } + else + { + left_node = Tc_candidate_root_node; + right_node = sig + SIG_PATH_OFFSET(ctx->params.otstype) + + height * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type); + } + + create_merkle_internal_value( &ctx->params, left_node, right_node, + parent_node_id, Tc_candidate_root_node); + + curr_node_id /= 2; + } + + if( memcmp( Tc_candidate_root_node, ctx->T_1_pub_key, + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)) ) + { + return( MBEDTLS_ERR_LMS_VERIFY_FAILED ); + } + + return( 0 ); +} + +#if defined(MBEDTLS_LMS_PRIVATE) + +/* Calculate a full Merkle tree based on a private key. This function + * implements RFC8554 section 5.3, and is used to generate a public key (as the + * public key is the root node of the Merkle tree). + * + * ctx The LMS private context, containing a parameter + * set and private key material consisting of both + * public and private OTS. + * + * tree The output tree, which is 2^(H + 1) hash outputs. + * In the case of H=10 we have 2048 tree nodes (of + * which 1024 of them are leaf nodes). Note that + * because the Merkle tree root is 1-indexed, the 0 + * index tree node is never used. + */ +static int calculate_merkle_tree( const mbedtls_lms_private_t *ctx, + unsigned char *tree ) +{ + unsigned int priv_key_idx; + unsigned int r_node_idx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* First create the leaf nodes, in ascending order */ + for( priv_key_idx = 0; + priv_key_idx < MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type); + priv_key_idx++ ) + { + r_node_idx = MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + priv_key_idx; + + ret = create_merkle_leaf_value( &ctx->params, + ctx->ots_public_keys[priv_key_idx].public_key, r_node_idx, + &tree[r_node_idx * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)] ); + if( ret != 0 ) + { + return( ret ); + } + } + + /* Then the internal nodes, in reverse order so that we can guarantee the + * parent has been created */ + for( r_node_idx = MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) - 1; + r_node_idx > 0; + r_node_idx-- ) + { + ret = create_merkle_internal_value( &ctx->params, + &tree[( r_node_idx * 2 ) * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)], + &tree[( r_node_idx * 2 + 1 ) * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)], + r_node_idx, + &tree[r_node_idx * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)] ); + if( ret != 0 ) + { + return( ret ); + } + } + + return( 0 ); +} + +/* Calculate a path from a leaf node of the Merkle tree to the root of the tree, + * and return the full path. This function implements RFC8554 section 5.4.1, as + * the Merkle path is the main component of an LMS signature. + * + * ctx The LMS private context, containing a parameter + * set and private key material consisting of both + * public and private OTS. + * + * leaf_node_id Which leaf node to calculate the path from. + * + * path The output path, which is H hash outputs. + */ +static int get_merkle_path( mbedtls_lms_private_t *ctx, + unsigned int leaf_node_id, + unsigned char *path ) +{ + unsigned char tree[MERKLE_TREE_NODE_AM_MAX][MBEDTLS_LMS_M_NODE_BYTES_MAX]; + unsigned int curr_node_id = leaf_node_id; + unsigned int adjacent_node_id; + unsigned int height; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = calculate_merkle_tree( ctx, ( unsigned char * )tree ); + if( ret != 0 ) + { + goto exit; + } + + for( height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT(ctx->params.type); + height++ ) + { + adjacent_node_id = curr_node_id ^ 1; + + memcpy( &path[height * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)], + &tree[adjacent_node_id], + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type) ); + + curr_node_id >>=1; + } + + ret = 0; + +exit: + mbedtls_platform_zeroize( tree, sizeof( tree ) ); + + return( ret ); +} + +void mbedtls_lms_private_init( mbedtls_lms_private_t *ctx ) +{ + memset( ctx, 0, sizeof( *ctx ) ) ; +} + +void mbedtls_lms_private_free( mbedtls_lms_private_t *ctx ) +{ + unsigned int idx; + + if( ctx->have_private_key ) + { + if( ctx->ots_private_keys != NULL ) + { + for( idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++ ) + { + mbedtls_lmots_private_free( &ctx->ots_private_keys[idx] ); + } + } + + if( ctx->ots_public_keys != NULL ) + { + for( idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++ ) + { + mbedtls_lmots_public_free( &ctx->ots_public_keys[idx] ); + } + } + + mbedtls_free( ctx->ots_private_keys ); + mbedtls_free( ctx->ots_public_keys ); + } + + mbedtls_platform_zeroize( ctx, sizeof( *ctx ) ); +} + + +int mbedtls_lms_generate_private_key( mbedtls_lms_private_t *ctx, + mbedtls_lms_algorithm_type_t type, + mbedtls_lmots_algorithm_type_t otstype, + int (*f_rng)(void *, unsigned char *, size_t), + void* p_rng, const unsigned char *seed, + size_t seed_size ) +{ + unsigned int idx = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( type != MBEDTLS_LMS_SHA256_M32_H10 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( otstype != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( ctx->have_private_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + ctx->params.type = type; + ctx->params.otstype = otstype; + ctx->have_private_key = 1; + + ret = f_rng( p_rng, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN ); + if( ret != 0 ) + { + goto exit; + } + + /* Requires a cast to size_t to avoid an implicit cast warning on certain + * platforms (particularly Windows) */ + ctx->ots_private_keys = mbedtls_calloc( ( size_t )MERKLE_TREE_LEAF_NODE_AM(ctx->params.type), + sizeof( *ctx->ots_private_keys ) ); + if( ctx->ots_private_keys == NULL ) + { + ret = MBEDTLS_ERR_LMS_ALLOC_FAILED; + goto exit; + } + + /* Requires a cast to size_t to avoid an implicit cast warning on certain + * platforms (particularly Windows) */ + ctx->ots_public_keys = mbedtls_calloc( ( size_t )MERKLE_TREE_LEAF_NODE_AM(ctx->params.type), + sizeof( *ctx->ots_public_keys ) ); + if( ctx->ots_public_keys == NULL ) + { + ret = MBEDTLS_ERR_LMS_ALLOC_FAILED; + goto exit; + } + + for( idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++ ) + { + mbedtls_lmots_private_init( &ctx->ots_private_keys[idx] ); + mbedtls_lmots_public_init( &ctx->ots_public_keys[idx] ); + } + + + for( idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++ ) + { + ret = mbedtls_lmots_generate_private_key( &ctx->ots_private_keys[idx], + otstype, + ctx->params.I_key_identifier, + idx, seed, seed_size ); + if( ret != 0 ) + goto exit; + + ret = mbedtls_lmots_calculate_public_key( &ctx->ots_public_keys[idx], + &ctx->ots_private_keys[idx] ); + if( ret != 0 ) + goto exit; + } + + ctx->q_next_usable_key = 0; + +exit: + if( ret != 0 ) + { + mbedtls_lms_private_free(ctx); + } + + return( ret ); +} + +int mbedtls_lms_calculate_public_key( mbedtls_lms_public_t *ctx, + const mbedtls_lms_private_t *priv_ctx ) +{ + unsigned char tree[MERKLE_TREE_NODE_AM_MAX][MBEDTLS_LMS_M_NODE_BYTES_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( ! priv_ctx->have_private_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( priv_ctx->params.type + != MBEDTLS_LMS_SHA256_M32_H10 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( priv_ctx->params.otstype + != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + memcpy( &ctx->params, &priv_ctx->params, + sizeof( mbedtls_lmots_parameters_t ) ); + + ret = calculate_merkle_tree( priv_ctx, ( unsigned char * )tree ); + if( ret != 0 ) + { + goto exit; + } + + /* Root node is always at position 1, due to 1-based indexing */ + memcpy( ctx->T_1_pub_key, &tree[1], + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type) ); + + ctx->have_public_key = 1; + + ret = 0; + +exit: + mbedtls_platform_zeroize( tree, sizeof( tree ) ); + + return( ret ); +} + + +int mbedtls_lms_sign( mbedtls_lms_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void* p_rng, const unsigned char *msg, + unsigned int msg_size, unsigned char *sig, size_t sig_size, + size_t *sig_len ) +{ + uint32_t q_leaf_identifier; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( ! ctx->have_private_key ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( sig_size < MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype) ) + { + return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL ); + } + + if( ctx->params.type != MBEDTLS_LMS_SHA256_M32_H10 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( ctx->params.otstype + != MBEDTLS_LMOTS_SHA256_N32_W8 ) + { + return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + if( ctx->q_next_usable_key >= MERKLE_TREE_LEAF_NODE_AM(ctx->params.type) ) + { + return( MBEDTLS_ERR_LMS_OUT_OF_PRIVATE_KEYS ); + } + + + q_leaf_identifier = ctx->q_next_usable_key; + /* This new value must _always_ be written back to the disk before the + * signature is returned. + */ + ctx->q_next_usable_key += 1; + + if ( MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype) + < SIG_OTS_SIG_OFFSET ) + { + return ( MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + } + + ret = mbedtls_lmots_sign( &ctx->ots_private_keys[q_leaf_identifier], + f_rng, p_rng, msg, msg_size, + sig + SIG_OTS_SIG_OFFSET, + MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype) - SIG_OTS_SIG_OFFSET, + NULL ); + if( ret != 0 ) + { + return( ret ); + } + + mbedtls_lms_unsigned_int_to_network_bytes( ctx->params.type, + MBEDTLS_LMS_TYPE_LEN, + sig + SIG_TYPE_OFFSET(ctx->params.otstype) ); + mbedtls_lms_unsigned_int_to_network_bytes( q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, + sig + SIG_Q_LEAF_ID_OFFSET ); + + ret = get_merkle_path( ctx, + MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + q_leaf_identifier, + sig + SIG_PATH_OFFSET(ctx->params.otstype) ); + if( ret != 0 ) + { + return( ret ); + } + + if( sig_len != NULL ) + { + *sig_len = MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype); + } + + + return( 0 ); +} + +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ +#endif /* defined(MBEDTLS_LMS_C) */ diff --git a/scripts/generate_errors.pl b/scripts/generate_errors.pl index 0a03f02e9..41b03377d 100755 --- a/scripts/generate_errors.pl +++ b/scripts/generate_errors.pl @@ -47,7 +47,7 @@ my $error_format_file = $data_dir.'/error.fmt'; my @low_level_modules = qw( AES ARIA ASN1 BASE64 BIGNUM CAMELLIA CCM CHACHA20 CHACHAPOLY CMAC CTR_DRBG DES - ENTROPY ERROR GCM HKDF HMAC_DRBG MD5 + ENTROPY ERROR GCM HKDF HMAC_DRBG LMS MD5 NET OID PADLOCK PBKDF2 PLATFORM POLY1305 RIPEMD160 SHA1 SHA256 SHA512 THREADING ); my @high_level_modules = qw( CIPHER DHM ECP MD diff --git a/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux new file mode 100644 index 000000000..967f8f5e0 Binary files /dev/null and b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux differ diff --git a/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv new file mode 100644 index 000000000..ab1b23f66 Binary files /dev/null and b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv differ diff --git a/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_pub b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_pub new file mode 100644 index 000000000..5397d60f0 Binary files /dev/null and b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_pub differ diff --git a/tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv b/tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv new file mode 100644 index 000000000..db85e01b7 Binary files /dev/null and b/tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv differ diff --git a/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv b/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv new file mode 100644 index 000000000..6e827ceb9 Binary files /dev/null and b/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv differ diff --git a/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_pub b/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_pub new file mode 100644 index 000000000..652c0897c Binary files /dev/null and b/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_pub differ diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index a4c6c86c1..f1b2f0e29 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -987,6 +987,8 @@ component_test_psa_crypto_client () { scripts/config.py unset MBEDTLS_PSA_CRYPTO_C scripts/config.py unset MBEDTLS_PSA_CRYPTO_STORAGE_C scripts/config.py set MBEDTLS_PSA_CRYPTO_CLIENT + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE make msg "test: default config - PSA_CRYPTO_C + PSA_CRYPTO_CLIENT, make" @@ -1246,6 +1248,8 @@ component_test_full_no_cipher () { scripts/config.py unset MBEDTLS_SSL_PROTO_TLS1_3 scripts/config.py unset MBEDTLS_SSL_SRV_C scripts/config.py unset MBEDTLS_USE_PSA_CRYPTO + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE make msg "test: full minus CIPHER" @@ -1268,6 +1272,8 @@ component_test_crypto_full_no_cipher () { scripts/config.py unset MBEDTLS_PSA_CRYPTO_SE_C scripts/config.py unset MBEDTLS_PSA_CRYPTO_STORAGE_C scripts/config.py unset MBEDTLS_USE_PSA_CRYPTO + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE make msg "test: crypto_full minus CIPHER" @@ -1829,6 +1835,8 @@ component_test_no_use_psa_crypto_full_cmake_asan() { scripts/config.py unset MBEDTLS_PSA_ITS_FILE_C scripts/config.py unset MBEDTLS_PSA_CRYPTO_SE_C scripts/config.py unset MBEDTLS_PSA_CRYPTO_STORAGE_C + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan . make @@ -2275,6 +2283,8 @@ component_build_psa_accel_alg_md5() { scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_384 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_512 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE # Need to define the correct symbol and include the test driver header path in order to build with the test driver make CC=gcc CFLAGS="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST -DMBEDTLS_PSA_ACCEL_ALG_MD5 -I../tests/include -O2" LDFLAGS="$ASAN_CFLAGS" } @@ -2295,6 +2305,8 @@ component_build_psa_accel_alg_ripemd160() { scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_384 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_512 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE # Need to define the correct symbol and include the test driver header path in order to build with the test driver make CC=gcc CFLAGS="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST -DMBEDTLS_PSA_ACCEL_ALG_RIPEMD160 -I../tests/include -O2" LDFLAGS="$ASAN_CFLAGS" } @@ -2315,6 +2327,8 @@ component_build_psa_accel_alg_sha1() { scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_384 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_512 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE # Need to define the correct symbol and include the test driver header path in order to build with the test driver make CC=gcc CFLAGS="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST -DMBEDTLS_PSA_ACCEL_ALG_SHA_1 -I../tests/include -O2" LDFLAGS="$ASAN_CFLAGS" } @@ -2372,6 +2386,8 @@ component_build_psa_accel_alg_sha384() { scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_224 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_256 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE # Need to define the correct symbol and include the test driver header path in order to build with the test driver make CC=gcc CFLAGS="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST -DMBEDTLS_PSA_ACCEL_ALG_SHA_384 -I../tests/include -O2" LDFLAGS="$ASAN_CFLAGS" } @@ -2392,6 +2408,8 @@ component_build_psa_accel_alg_sha512() { scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_256 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_SHA_384 scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS + scripts/config.py unset MBEDTLS_LMS_C + scripts/config.py unset MBEDTLS_LMS_PRIVATE # Need to define the correct symbol and include the test driver header path in order to build with the test driver make CC=gcc CFLAGS="$ASAN_CFLAGS -DPSA_CRYPTO_DRIVER_TEST -DMBEDTLS_PSA_ACCEL_ALG_SHA_512 -I../tests/include -O2" LDFLAGS="$ASAN_CFLAGS" } diff --git a/tests/scripts/depends-hashes.pl b/tests/scripts/depends-hashes.pl index 68297a6a2..db18a92ae 100755 --- a/tests/scripts/depends-hashes.pl +++ b/tests/scripts/depends-hashes.pl @@ -57,7 +57,7 @@ my @hash_configs = ( ['unset MBEDTLS_MD5_C'], ['unset MBEDTLS_SHA512_C', 'unset MBEDTLS_SHA384_C '], ['unset MBEDTLS_SHA384_C'], - ['unset MBEDTLS_SHA256_C', 'unset MBEDTLS_SHA224_C'], + ['unset MBEDTLS_SHA256_C', 'unset MBEDTLS_SHA224_C', 'unset MBEDTLS_LMS_C', 'unset MBEDTLS_LMS_PRIVATE'], ['unset MBEDTLS_SHA1_C'], ); diff --git a/tests/suites/test_suite_lmots.data b/tests/suites/test_suite_lmots.data new file mode 100644 index 000000000..2737272bd --- /dev/null +++ b/tests/suites/test_suite_lmots.data @@ -0,0 +1,151 @@ +LMOTS sign-verify test #1 +# This test uses a fixed message, and then generates a private key, signs the +# message, and verifies the signature. +lmots_sign_verify_test:"c41ba177a0ca1ec31dfb2e145237e65b":"00000000000000000000000000000000":12:"403cbcc9808bb4b5ad72476ea297b2854c928ff5336f0b98ac2237ec83225ae7" + +LMOTS sign-verify test #2 +# This test uses a fixed message, and then generates a private key, signs the +# 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 hsslms interop test #1 +# This test uses data from https://github.com/pmvr/python-hsslms due to the +# limited amount of available test vectors for LMOTS, and few implementations +# providing direct access to the underlying OTS signature scheme. The private +# key is stored in data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv. +# This test uses the same OTS key as the LMS hsslms interop test 1 (leaf 0 of +# the LMS key), and the same message. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# pip3 install --user hsslms==0.1.2 +# +# from hsslms import LMS_Priv, LM_OTS_Priv, LMS_ALGORITHM_TYPE, LMOTS_ALGORITHM_TYPE +# import pickle +# +# with open('tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv', 'rb') as private_key_file: +# private_key = pickle.load(private_key_file) +# +# ots_private_key = LM_OTS_Priv(private_key.otstypecode, private_key.I, 0, private_key.SEED) +# ots_public_key = ots_private_key.gen_pub() +# message = bytes.fromhex('60da1a17c88c59da8a730e6ca8effd37') +# sig = ots_private_key.sign(message) +# print('lmots_verify_test:"{}":"{}":"{}":0'.format(message.hex(), sig.hex(), ots_public_key.pubkey.hex())) +lmots_verify_test:"60da1a17c88c59da8a730e6ca8effd37":"000000040bb462a8f59a277c1706ab69b1a40b0d56a3ffe1ddf0dfa890096c7a9c48b360e1e8f7abe4dc1950c4a64545ce6c0fe2a34477ec40f56db4eec37c1a2168e3059d4338a4eb368a64be5f98b5452f2c2fad23dcac585f5fe308bfc3df0b5cbc6cf3545236ed6c5a863e677521b5b5cee0aa1e755c3bbf5fb7326fac1a88cb12dd7f8d68ebe8bad07195a12fa11299073731e67f2452009252c595fc7d9285b90aaa912eb6cf0b5debc0996ca55ad5186702b244a616c4b9e0ceeea229e1e821c1ab0db906ce87640d128f1d8c4742d9baf340a8030df726a99a9b97f139ec57d8d87efdfca235f12de64e0a993804b95227cdfd26220a84502e350faaf5f91f3f49610eda211f9409005679e32068def22a2dcce3d226d0f68c4abc727b90d9c01daa05db24d7c0c9e9e48202e3420992ba78c36bc21c45cdf218801dc7053e3cbf39c141784e7a861671588622d540187912234ce628ea9cbd1800d215641163c762d2fd9194fa54bd9b46c83754579476398a5c2fece4642f1ee286a4e9a310b5e23088c75a68b123044c1c365c8b53fe9f895fa5d76fe1277c7c0f2a39f5b233f7d2acd5358feec2255feadb1c2513c4351c9bd1afe22d159f2d392c83bf7ec26b59e78330cd346adb85ef62fee3da63150ab5e0d7ce5d0ef353895360017faf3f35aca2cf3b8eda65389e2ba86f78ebfbe73382dfe9002331f24e96e1a6e56e7cc99ee848b82ad1ade3229e9e28168acfa8c059ed03028e8c872e72ff4cf8a50b84ade908ecf229a26ff1007c476d1aa376323fc567c9471085336496b231b5245a43c6c86c6a71c1b1fb4bd87c2d0b026bff55de121620a089ed9ade51c3bd91c703844c180ef9ad0ab550b9560ba9f1452463ce20987a402213ca5c16c927a0a85091dd74fbee22cac6b1afbc7e7dec229325c25ea3b3cc5a1c48c80665f9903e482b143f7cd051bdb990355f79c62553453c72ccbcc578df77069a7b0cf6fdc6853ec2f96fb7cc100216ae1b17aa20782fb0cd0f261b76a48b5d6f7bb48fa5f78c02a11ee81a8c0c81183910af770f2e907ebd5b2dc3a2b83529f62da074ca73c434f8f30b68a5dfee740f78d2c13b53c904e46dddf723923bfbffa437a4130c8c9b6d79a57db1c408b9c023f80fb3d766cb915e722f3b3152625d77bce3ca0c01e77f3750d7d1bef1ddda8b9b4233b09c89abe5913db50847a7ea219c3f406aa4cf41b6310bafa99a7b14f94b8ccd4dc7edb1a1e963ce26a53f3be71b4151ce5fae10ca30055e754880680e38cf2f21251f229341f7af9536360a428c2593c43fd2ae471bc1fa2b45ad55742a2f12f31eed6cb67945a898650c13650c4cffeecba8655f87e49ce921ced7ab00cf54eedf0c70e5c6cfa7f006763f0ac14d80cfb1662321cdccca8b1adf426eb9ca16ef2b978bb9ac229131fa5c1266a4980449c324ebdd8bcc98916089ee0b6966da7dc25350bdc758431884359d02f5fa567b39f49a6e410da2d0363944a090926308ed0ce7d565e6c4585ea010bc38ef1c976ae16ec1fbe6fb9d4d50e49a7be8273d2d56bf4e72acbadd90d5f8dee0":"0000000447cc5b29dd0cecd01c382434a6d1686400000000761e8e577fb4d12058806fc7bdaaef0ba64e454dc59b0230a77b43bbd83dc8c6":0 + +LMOTS hsslms interop test #2 +# This test uses data from https://github.com/pmvr/python-hsslms due to the +# limited amount of available test vectors for LMOTS, and few implementations +# providing direct access to the underlying OTS signature scheme. The private +# key is stored in data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv. +# This test uses the same OTS key as the LMS hsslms interop test 2 (leaf 1 of +# the LMS key), and the same message. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# pip3 install --user hsslms==0.1.2 +# +# from hsslms import LMS_Priv, LM_OTS_Priv, LMS_ALGORITHM_TYPE, LMOTS_ALGORITHM_TYPE +# import pickle +# +# with open('tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv', 'rb') as private_key_file: +# private_key = pickle.load(private_key_file) +# +#ots_private_key = LM_OTS_Priv(private_key.otstypecode, private_key.I, 1, private_key.SEED) +#ots_public_key = ots_private_key.gen_pub() +#message = bytes.fromhex('92d036bde8c45b8bb5dea2a072560b1e29fc4bb7dc4549ce90bccee8a6e962a1') +#sig = ots_private_key.sign(message) +#print('lmots_verify_test:"{}":"{}":"{}":0'.format(message.hex(), sig.hex(), ots_public_key.pubkey.hex())) +lmots_verify_test:"92d036bde8c45b8bb5dea2a072560b1e29fc4bb7dc4549ce90bccee8a6e962a1":"00000004e29f47d2314ebaf22ebb821dec653f5bd105aced5d24829787a93da910baa495cd5a8576dad606cc7407c4d8a38a715ded879274c5347a200cc1c08a6fcc7288e280bb2e66b682c4b20c514f7a990ce01594001917be8d1cb5a23c22dc00c81b18b8047177cc109a1ff862f535319b703be8e4439062348b7bc73e85e69f7d3f033767146130991f78b497e2a0eee1059d2cd87e0a99c1aae47a6496664735cdb383a8f7a1d686199cf2e07a67e9ef409048efb76cf0c689c1c6c67a5b6966e4b4773710bdff3f72a4f85428187f912c9f13a8bd06533450ce04dbbd2c022eab44a5f6a822d78918f692fa5c6c90aab8941072d679b89388160556597acf17b95b3ffdf8c4c21df5327bd756772a45fdde182004d91cff5aba111fbb70b5970a7d7416220de31e6e76646372e4a1606fbbd5be215a32bbb84da99c63af271edbd42ee87de174cabec7734b6d924d329640bdb84059cbcea89caa703667f5e1b3c1c71b53213f1cd7d1da3e42da70edeb7c0b596bcb981c08eb0f02408ee028a57165cbdc36c9edafa559826c2e690e73da7c7fa1b0fa0e6041a692a2e8f27af80513c07ecd89caaf78ddf8e2edaa17bece335068153b253ceef38b491801c1ef7c648045ce7c517afab888603648b17d3a98a3b5622b469a829b023c5cb2ce42462c28d22bc3de91dd8b38bab539971b0c7596dcd0d8c0d84bfd7925d6e2f2d114ca4f91fca12178a451ac0dabc8c21396ad5be57ea0648bd1054de00aa7fd3d46453ebacf6b611e05842f5f019aeca3c798ae063631fd5e56ea1f7a21bbea5c30e6d60a724ce187e7c497d918d2a4d5094224dde94a02e851eae1626533992a599a641466e4d683e40b5a28695aababd2d7f7d2ccee72c289876c8d581babaeb3d738f1d1fc765e9fee3f70670913e07cd38fc7b37e2caba0a735352aa3f4b2467010bb1b725d4bbd86d8c98eece10e925d8bb5c0e993dfa45621f91596f5d1e1446b118c48bc1e403627fdd299ad4d3d5f3a2dfb239bf22e7ff25d83287ba3a96b24cda0252df1907af1cb74d31d720c5baca0f316769f7f98b409c17bb543c39628446183e326d0745b4424520a9d582fc817eac55b0efc2ca4659a60a95e1d3b77bf1454e5cd4d1d54d51159d3df70a78345d1d6a7e0746b3deb080883f6506e9e7d0fb4bddaa66aa7cf555df1bb9d3f848b7e604b690a403f4e40188110e0ef9af15dc4952a8ed100987e39e8184be8dc62441ac2a561c7cbe431c45b0ec03c41c4867e38925977fc240ed2a04d73d4319435de354dfe0184220c71bd59be4e7f6dc9a1a27f4eefc990d615b2c12e13f1821727a607afdab359d2bad5b1be689a36662e052cfade2c0f5cc842c090082068d324f0e338830030d255ee6e6d9303c0037c24985338dfa16b5980a99782af1b3aca9123b5063e0b9f1a31105e2c9eaae2353b2ed53dab5b4fb43b4697d05fcf4941be071edf3456ac8e35eba39800ad968155574c14b6ce109982177b00ea5fbb739dc7553e40c98824d4932185e61ccc380b07476ae210ce3657b24f4639261a5e7e0c52d6afdea97bb2fc":"0000000447cc5b29dd0cecd01c382434a6d1686400000001f337dde97685d008a4440b59550277390018d3f1d485fa4b8c91796032de494b":0 + +LMOTS hsslms interop NULL-message test +# This test uses data from https://github.com/pmvr/python-hsslms due to the +# limited amount of available test vectors for LMOTS, and few implementations +# providing direct access to the underlying OTS signature scheme. The private +# key is stored in data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# pip3 install --user hsslms==0.1.2 +# +# from hsslms import LMS_Priv, LM_OTS_Priv, LMS_ALGORITHM_TYPE, LMOTS_ALGORITHM_TYPE +# import pickle +# +# with open('tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv', 'rb') as private_key_file: +# private_key = pickle.load(private_key_file) +# +#ots_private_key = LM_OTS_Priv(private_key.otstypecode, private_key.I, 3, private_key.SEED) +#ots_public_key = ots_private_key.gen_pub() +#message = bytes() +#sig = ots_private_key.sign(message) +#print('lmots_verify_test:"{}":"{}":"{}":0'.format(message.hex(), sig.hex(), ots_public_key.pubkey.hex())) +lmots_verify_test:"":"00000004862327ead0b0eee8bde100614b3369e183f97812c13f0979f7d37482a2ae719a811ba3a5c65cc036270d4b31ed6caa900ba0a98e3e5d6f7e4286571e003fd7f8fd523c7707eb00d25ce6b0d2c92317b2531b8ebb184ed65f7bd4c20611154409acb5134389c8aca9cb98c380fc8de4f48078a1859126967275219ca0168c6f0cfec0c2f63f98fb2a741fc15a5d59b50a97efe2564bd8a4f05fe250d4ec316e6a833a2dafaea47efa359840fb887e3a5ae0b07c75ed1dda3cc253365c5b9320180e5273a2c5078cdc0d3aefeaa94d8888c3112c2b68f85fdfaa13b5088f4bdf570f5a2ae32114497d28a6b46abe602f142a9382651a4b5fe7aeda3e54deaf85d51d59bc945e3970d4f603cb1617137c182087dcecb7f97016e138ae4c7f8926a9fbf7d1154cd53971e3c86e230fe783efdc44f4459143eeddec73612a11f6c4796bb734b703b94b3ee02a136f676ff959bd9dcba3a6cdf8244310b4125a07ef7a364d47c2d0067370f9024bb02217ea19baafa6111dbe1daa6f4d3ae287f8b4675934a8cb124b64f3d2baac01504a66b5cb80d5fe88281c92eb2d9e6105368ce748c2269f28444d20f8fa06f96738942606fd2ee1ae17b45953af9cc8aa10089b80c951ae7d4c6496476e0f9d88050a09433a99b92f1bd2bc2cc4e712fbba650e8c61716a2396bd802679096b2ed113dcf9107196f41185c9baa295c1000879dae4e36344b7ca9a4f040ceec064ff4a654a561a21fbdd0c28a4d0245da9fdb37a7ce20875e323db04197b6ec9d0265a840687a4067b6670482e3a765895a57f26fb971e359f30fa3c65b6197fbfe6433364f0062cc20d8ee2ebed5c3b96dfcd46aa99956b5b1602d9ea16b05ed54f1a72557148ec3a43baaac2474f735ce82979c87df358d175f4686aebde24b768f0f8dfa3c20d9c33db8244f47793eae676afe7485b08163ebd5c4b02c227a38824bb58d034e0a00395ce19e34846b8f6ce3cd3ba877a6ee953738c0ebffdc6eee63bff648e1530f611e9b5de0e5c41bf2f50375347dbe3c332ec523d516aa9478fdf61952f44068447d1474bc3a33f0d973f7b36360ddefd21ba57916dc0ee7a21082ec9c024d78938616e8bbaff451c8da9675cef9d0610872e8cd2b7673a86148e3abba473d0e4e1579ca3faa891d475a6bab9dc3a90537c701a62f41198b0e86f101b506a8a5b102ddd6fdafca56e7f32f4217f8bb7c228066c53fcd78b8541c3ffeb88fe685c796711bbe2d8fee6e9adcc077c140216438c5db25e7b7b34164fce6343dd8de5aa8310d18c9cf91992a25e6f71eb39fb7c267dc3b87d1781b34a4f3c84e2ecc04f73104d50e00631e2e7b157a8374c2b08dbcb3210b2852738a16cc580fa6df62b93f27151bfa77eaeb726ab18137e14962676836a573a6ac62b1bb8d40b402d2da0b37bb5a29e2ef154a78f61b632c2e9279670ba9a7a2c2ceda3f931940a5766738ad8ee62761c87d94e50ec995c01484fe6c96d0fb2ae97394e6497a4a8087c366edd038d72b01f4eb351a2ac41d19df56db40491da464a6f0c646b859e7ea3b0584be618fd7fb48c":"0000000447cc5b29dd0cecd01c382434a6d16864000000033fa1330497e44e2773f08e4727eb4d745db9051d6a60779e58a922dc8a7d4ede":0 + +LMOTS hash-sigs interop negative test (altered random value) +# This test uses the valid signature from hsslms interop test 1, and then +# alters the random value (C) of the signature, and is expected to fail to +# verify. +lmots_verify_test:"60da1a17c88c59da8a730e6ca8effd37":"000000041bb462a8f59a277c1706ab69b1a40b0d56a3ffe1ddf0dfa890096c7a9c48b360e1e8f7abe4dc1950c4a64545ce6c0fe2a34477ec40f56db4eec37c1a2168e3059d4338a4eb368a64be5f98b5452f2c2fad23dcac585f5fe308bfc3df0b5cbc6cf3545236ed6c5a863e677521b5b5cee0aa1e755c3bbf5fb7326fac1a88cb12dd7f8d68ebe8bad07195a12fa11299073731e67f2452009252c595fc7d9285b90aaa912eb6cf0b5debc0996ca55ad5186702b244a616c4b9e0ceeea229e1e821c1ab0db906ce87640d128f1d8c4742d9baf340a8030df726a99a9b97f139ec57d8d87efdfca235f12de64e0a993804b95227cdfd26220a84502e350faaf5f91f3f49610eda211f9409005679e32068def22a2dcce3d226d0f68c4abc727b90d9c01daa05db24d7c0c9e9e48202e3420992ba78c36bc21c45cdf218801dc7053e3cbf39c141784e7a861671588622d540187912234ce628ea9cbd1800d215641163c762d2fd9194fa54bd9b46c83754579476398a5c2fece4642f1ee286a4e9a310b5e23088c75a68b123044c1c365c8b53fe9f895fa5d76fe1277c7c0f2a39f5b233f7d2acd5358feec2255feadb1c2513c4351c9bd1afe22d159f2d392c83bf7ec26b59e78330cd346adb85ef62fee3da63150ab5e0d7ce5d0ef353895360017faf3f35aca2cf3b8eda65389e2ba86f78ebfbe73382dfe9002331f24e96e1a6e56e7cc99ee848b82ad1ade3229e9e28168acfa8c059ed03028e8c872e72ff4cf8a50b84ade908ecf229a26ff1007c476d1aa376323fc567c9471085336496b231b5245a43c6c86c6a71c1b1fb4bd87c2d0b026bff55de121620a089ed9ade51c3bd91c703844c180ef9ad0ab550b9560ba9f1452463ce20987a402213ca5c16c927a0a85091dd74fbee22cac6b1afbc7e7dec229325c25ea3b3cc5a1c48c80665f9903e482b143f7cd051bdb990355f79c62553453c72ccbcc578df77069a7b0cf6fdc6853ec2f96fb7cc100216ae1b17aa20782fb0cd0f261b76a48b5d6f7bb48fa5f78c02a11ee81a8c0c81183910af770f2e907ebd5b2dc3a2b83529f62da074ca73c434f8f30b68a5dfee740f78d2c13b53c904e46dddf723923bfbffa437a4130c8c9b6d79a57db1c408b9c023f80fb3d766cb915e722f3b3152625d77bce3ca0c01e77f3750d7d1bef1ddda8b9b4233b09c89abe5913db50847a7ea219c3f406aa4cf41b6310bafa99a7b14f94b8ccd4dc7edb1a1e963ce26a53f3be71b4151ce5fae10ca30055e754880680e38cf2f21251f229341f7af9536360a428c2593c43fd2ae471bc1fa2b45ad55742a2f12f31eed6cb67945a898650c13650c4cffeecba8655f87e49ce921ced7ab00cf54eedf0c70e5c6cfa7f006763f0ac14d80cfb1662321cdccca8b1adf426eb9ca16ef2b978bb9ac229131fa5c1266a4980449c324ebdd8bcc98916089ee0b6966da7dc25350bdc758431884359d02f5fa567b39f49a6e410da2d0363944a090926308ed0ce7d565e6c4585ea010bc38ef1c976ae16ec1fbe6fb9d4d50e49a7be8273d2d56bf4e72acbadd90d5f8dee0":"0000000447cc5b29dd0cecd01c382434a6d1686400000000761e8e577fb4d12058806fc7bdaaef0ba64e454dc59b0230a77b43bbd83dc8c6":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMOTS negative test (invalid type) #1 +# This test uses the valid signature from hsslms interop test 1, and then +# sets an invalid LMOTS type (0x5), and is expected to fail to +# verify. +lmots_verify_test:"60da1a17c88c59da8a730e6ca8effd37":"000000050bb462a8f59a277c1706ab69b1a40b0d56a3ffe1ddf0dfa890096c7a9c48b360e1e8f7abe4dc1950c4a64545ce6c0fe2a34477ec40f56db4eec37c1a2168e3059d4338a4eb368a64be5f98b5452f2c2fad23dcac585f5fe308bfc3df0b5cbc6cf3545236ed6c5a863e677521b5b5cee0aa1e755c3bbf5fb7326fac1a88cb12dd7f8d68ebe8bad07195a12fa11299073731e67f2452009252c595fc7d9285b90aaa912eb6cf0b5debc0996ca55ad5186702b244a616c4b9e0ceeea229e1e821c1ab0db906ce87640d128f1d8c4742d9baf340a8030df726a99a9b97f139ec57d8d87efdfca235f12de64e0a993804b95227cdfd26220a84502e350faaf5f91f3f49610eda211f9409005679e32068def22a2dcce3d226d0f68c4abc727b90d9c01daa05db24d7c0c9e9e48202e3420992ba78c36bc21c45cdf218801dc7053e3cbf39c141784e7a861671588622d540187912234ce628ea9cbd1800d215641163c762d2fd9194fa54bd9b46c83754579476398a5c2fece4642f1ee286a4e9a310b5e23088c75a68b123044c1c365c8b53fe9f895fa5d76fe1277c7c0f2a39f5b233f7d2acd5358feec2255feadb1c2513c4351c9bd1afe22d159f2d392c83bf7ec26b59e78330cd346adb85ef62fee3da63150ab5e0d7ce5d0ef353895360017faf3f35aca2cf3b8eda65389e2ba86f78ebfbe73382dfe9002331f24e96e1a6e56e7cc99ee848b82ad1ade3229e9e28168acfa8c059ed03028e8c872e72ff4cf8a50b84ade908ecf229a26ff1007c476d1aa376323fc567c9471085336496b231b5245a43c6c86c6a71c1b1fb4bd87c2d0b026bff55de121620a089ed9ade51c3bd91c703844c180ef9ad0ab550b9560ba9f1452463ce20987a402213ca5c16c927a0a85091dd74fbee22cac6b1afbc7e7dec229325c25ea3b3cc5a1c48c80665f9903e482b143f7cd051bdb990355f79c62553453c72ccbcc578df77069a7b0cf6fdc6853ec2f96fb7cc100216ae1b17aa20782fb0cd0f261b76a48b5d6f7bb48fa5f78c02a11ee81a8c0c81183910af770f2e907ebd5b2dc3a2b83529f62da074ca73c434f8f30b68a5dfee740f78d2c13b53c904e46dddf723923bfbffa437a4130c8c9b6d79a57db1c408b9c023f80fb3d766cb915e722f3b3152625d77bce3ca0c01e77f3750d7d1bef1ddda8b9b4233b09c89abe5913db50847a7ea219c3f406aa4cf41b6310bafa99a7b14f94b8ccd4dc7edb1a1e963ce26a53f3be71b4151ce5fae10ca30055e754880680e38cf2f21251f229341f7af9536360a428c2593c43fd2ae471bc1fa2b45ad55742a2f12f31eed6cb67945a898650c13650c4cffeecba8655f87e49ce921ced7ab00cf54eedf0c70e5c6cfa7f006763f0ac14d80cfb1662321cdccca8b1adf426eb9ca16ef2b978bb9ac229131fa5c1266a4980449c324ebdd8bcc98916089ee0b6966da7dc25350bdc758431884359d02f5fa567b39f49a6e410da2d0363944a090926308ed0ce7d565e6c4585ea010bc38ef1c976ae16ec1fbe6fb9d4d50e49a7be8273d2d56bf4e72acbadd90d5f8dee0":"0000000447cc5b29dd0cecd01c382434a6d1686400000000761e8e577fb4d12058806fc7bdaaef0ba64e454dc59b0230a77b43bbd83dc8c6":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMOTS negative test (invalid type) #2 +# This test uses the valid signature from hsslms interop test 1, and then +# sets an invalid LMOTS type (0x3), and is expected to fail to +# verify. +lmots_verify_test:"60da1a17c88c59da8a730e6ca8effd37":"000000030bb462a8f59a277c1706ab69b1a40b0d56a3ffe1ddf0dfa890096c7a9c48b360e1e8f7abe4dc1950c4a64545ce6c0fe2a34477ec40f56db4eec37c1a2168e3059d4338a4eb368a64be5f98b5452f2c2fad23dcac585f5fe308bfc3df0b5cbc6cf3545236ed6c5a863e677521b5b5cee0aa1e755c3bbf5fb7326fac1a88cb12dd7f8d68ebe8bad07195a12fa11299073731e67f2452009252c595fc7d9285b90aaa912eb6cf0b5debc0996ca55ad5186702b244a616c4b9e0ceeea229e1e821c1ab0db906ce87640d128f1d8c4742d9baf340a8030df726a99a9b97f139ec57d8d87efdfca235f12de64e0a993804b95227cdfd26220a84502e350faaf5f91f3f49610eda211f9409005679e32068def22a2dcce3d226d0f68c4abc727b90d9c01daa05db24d7c0c9e9e48202e3420992ba78c36bc21c45cdf218801dc7053e3cbf39c141784e7a861671588622d540187912234ce628ea9cbd1800d215641163c762d2fd9194fa54bd9b46c83754579476398a5c2fece4642f1ee286a4e9a310b5e23088c75a68b123044c1c365c8b53fe9f895fa5d76fe1277c7c0f2a39f5b233f7d2acd5358feec2255feadb1c2513c4351c9bd1afe22d159f2d392c83bf7ec26b59e78330cd346adb85ef62fee3da63150ab5e0d7ce5d0ef353895360017faf3f35aca2cf3b8eda65389e2ba86f78ebfbe73382dfe9002331f24e96e1a6e56e7cc99ee848b82ad1ade3229e9e28168acfa8c059ed03028e8c872e72ff4cf8a50b84ade908ecf229a26ff1007c476d1aa376323fc567c9471085336496b231b5245a43c6c86c6a71c1b1fb4bd87c2d0b026bff55de121620a089ed9ade51c3bd91c703844c180ef9ad0ab550b9560ba9f1452463ce20987a402213ca5c16c927a0a85091dd74fbee22cac6b1afbc7e7dec229325c25ea3b3cc5a1c48c80665f9903e482b143f7cd051bdb990355f79c62553453c72ccbcc578df77069a7b0cf6fdc6853ec2f96fb7cc100216ae1b17aa20782fb0cd0f261b76a48b5d6f7bb48fa5f78c02a11ee81a8c0c81183910af770f2e907ebd5b2dc3a2b83529f62da074ca73c434f8f30b68a5dfee740f78d2c13b53c904e46dddf723923bfbffa437a4130c8c9b6d79a57db1c408b9c023f80fb3d766cb915e722f3b3152625d77bce3ca0c01e77f3750d7d1bef1ddda8b9b4233b09c89abe5913db50847a7ea219c3f406aa4cf41b6310bafa99a7b14f94b8ccd4dc7edb1a1e963ce26a53f3be71b4151ce5fae10ca30055e754880680e38cf2f21251f229341f7af9536360a428c2593c43fd2ae471bc1fa2b45ad55742a2f12f31eed6cb67945a898650c13650c4cffeecba8655f87e49ce921ced7ab00cf54eedf0c70e5c6cfa7f006763f0ac14d80cfb1662321cdccca8b1adf426eb9ca16ef2b978bb9ac229131fa5c1266a4980449c324ebdd8bcc98916089ee0b6966da7dc25350bdc758431884359d02f5fa567b39f49a6e410da2d0363944a090926308ed0ce7d565e6c4585ea010bc38ef1c976ae16ec1fbe6fb9d4d50e49a7be8273d2d56bf4e72acbadd90d5f8dee0":"0000000447cc5b29dd0cecd01c382434a6d1686400000000761e8e577fb4d12058806fc7bdaaef0ba64e454dc59b0230a77b43bbd83dc8c6":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMOTS key import / export test +# This test uses the valid public key for hsslms interop test 1, imports it, and +# then exports it. It also checks if the export correctly fails when the export +# buffer is too small. +lmots_import_export_test:"0000000447cc5b29dd0cecd01c382434a6d1686400000001f337dde97685d008a4440b59550277390018d3f1d485fa4b8c91796032de494b":0 + +LMOTS key import too large key test +# This test uses the valid public key for hsslms interop test 1, add an extra +# byte, and then imports it. This should fail. +lmots_import_export_test:"0000000447cc5b29dd0cecd01c382434a6d1686400000001f337dde97685d008a4440b59550277390018d3f1d485fa4b8c91796032de494b00":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMOTS key import too small key test +# This test uses the valid public key for hsslms interop test 1, removes a byte, +# and then imports it. This should fail. +lmots_import_export_test:"0000000447cc5b29dd0cecd01c382434a6d1686400000001f337dde97685d008a4440b59550277390018d3f1d485fa4b8c91796032de49":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMOTS key import no type test +# This test uses the valid public key for hsslms interop test 1, cuts it down so +# it's smaller than the LMOTS type offset, and imports it. This should fail, and +# not attempt to read invalidly outside the buffer. +lmots_import_export_test:"000000":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMOTS key import invalid type test #1 +# This test uses the valid public key for hsslms interop test 1, alters the +# LMOTS type to 0x3, and imports it. This should fail. +lmots_import_export_test:"0000000347cc5b29dd0cecd01c382434a6d1686400000001f337dde97685d008a4440b59550277390018d3f1d485fa4b8c91796032de494b":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMOTS key import invalid type test #2 +# This test uses the valid public key for hsslms interop test 1, alters the +# LMOTS type to 0x5, and imports it. This should fail, and not attempt to read +# invalidly outside the buffer. +lmots_import_export_test:"0000000547cc5b29dd0cecd01c382434a6d1686400000001f337dde97685d008a4440b59550277390018d3f1d485fa4b8c91796032de494b":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMOTS key reuse test +# This test uses a fixed message, and then generates a private key, signs the +# 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 new file mode 100644 index 000000000..53ab02ff6 --- /dev/null +++ b/tests/suites/test_suite_lmots.function @@ -0,0 +1,246 @@ +/* BEGIN_HEADER */ +#include "lmots.h" +#include "mbedtls/lms.h" + +#if defined(MBEDTLS_TEST_HOOKS) +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++ ) + { + TEST_EQUAL( sig[idx], 0x7E ); + } + + return( 0 ); + +exit: + return( -1 ); +} +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_LMS_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE depends_on:MBEDTLS_LMS_PRIVATE */ +void lmots_sign_verify_test ( data_t *msg, 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_public_init( &pub_ctx ); + mbedtls_lmots_private_init( &priv_ctx ); + + TEST_EQUAL( mbedtls_lmots_generate_private_key(&priv_ctx, MBEDTLS_LMOTS_SHA256_N32_W8, + key_id->x, leaf_id, seed->x, seed->len ), 0 ); + TEST_EQUAL( mbedtls_lmots_calculate_public_key(&pub_ctx, &priv_ctx), 0 ); + TEST_EQUAL( mbedtls_lmots_sign(&priv_ctx, &mbedtls_test_rnd_std_rand, NULL, + msg->x, msg->len, sig, sizeof(sig), NULL ), 0 ); + TEST_EQUAL( mbedtls_lmots_verify(&pub_ctx, msg->x, msg->len, sig, sizeof(sig)), 0 ); + +exit: + mbedtls_lmots_public_free( &pub_ctx ); + mbedtls_lmots_private_free( &priv_ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_LMS_PRIVATE */ +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_public_init( &pub_ctx ); + mbedtls_lmots_private_init( &priv_ctx ); + + TEST_EQUAL( mbedtls_lmots_generate_private_key(&priv_ctx, MBEDTLS_LMOTS_SHA256_N32_W8, + key_id->x, leaf_id, seed->x, seed->len ), 0 ); + TEST_EQUAL( mbedtls_lmots_calculate_public_key(&pub_ctx, &priv_ctx), 0 ); + TEST_EQUAL( mbedtls_lmots_sign(&priv_ctx, &mbedtls_test_rnd_std_rand, NULL, + NULL, 0, sig, sizeof(sig), NULL ), 0 ); + TEST_EQUAL( mbedtls_lmots_verify(&pub_ctx, NULL, 0, sig, sizeof(sig)), 0 ); + +exit: + mbedtls_lmots_public_free( &pub_ctx ); + mbedtls_lmots_private_free( &priv_ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void lmots_verify_test ( data_t *msg, data_t *sig, data_t *pub_key, + int expected_rc ) +{ + mbedtls_lmots_public_t ctx; + unsigned int size; + unsigned char *tmp_sig = NULL; + + mbedtls_lmots_public_init( &ctx ); + + TEST_EQUAL(mbedtls_lmots_import_public_key( &ctx, pub_key->x, pub_key->len ), 0); + + TEST_EQUAL(mbedtls_lmots_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), expected_rc); + + /* Test negative cases if the input data is valid */ + if( expected_rc == 0 ) + { + if( msg->len >= 1 ) + { + /* Altering first message byte must cause verification failure */ + msg->x[0] ^= 1; + TEST_EQUAL(mbedtls_lmots_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + msg->x[0] ^= 1; + + /* Altering last message byte must cause verification failure */ + msg->x[msg->len - 1] ^= 1; + TEST_EQUAL(mbedtls_lmots_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + msg->x[msg->len - 1] ^= 1; + } + + /* Altering first signature byte must cause verification failure */ + sig->x[0] ^= 1; + TEST_EQUAL(mbedtls_lmots_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + sig->x[0] ^= 1; + + /* Altering last signature byte must cause verification failure */ + sig->x[sig->len - 1] ^= 1; + TEST_EQUAL(mbedtls_lmots_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + sig->x[sig->len - 1] ^= 1; + + /* Signatures of all sizes must not verify, whether shorter or longer */ + for( size = 0; size < sig->len; size++ ) { + if( size == sig->len ) + continue; + + ASSERT_ALLOC( tmp_sig, size ); + if( tmp_sig != NULL ) + memcpy( tmp_sig, sig->x, MIN(size, sig->len) ); + + TEST_EQUAL(mbedtls_lmots_verify( &ctx, msg->x, msg->len, tmp_sig, size ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + mbedtls_free( tmp_sig ); + tmp_sig = NULL; + } + } + +exit: + mbedtls_free( tmp_sig ); + mbedtls_lmots_public_free( &ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void lmots_import_export_test ( data_t * pub_key, int expected_import_rc ) +{ + mbedtls_lmots_public_t ctx; + unsigned char *exported_pub_key = NULL; + size_t exported_pub_key_buf_size; + size_t exported_pub_key_size; + + mbedtls_lmots_public_init( &ctx ); + TEST_EQUAL( mbedtls_lmots_import_public_key( &ctx, pub_key->x, pub_key->len ), + expected_import_rc ); + + if( expected_import_rc == 0 ) + { + exported_pub_key_buf_size = MBEDTLS_LMOTS_PUBLIC_KEY_LEN(MBEDTLS_LMOTS_SHA256_N32_W8); + ASSERT_ALLOC( exported_pub_key, exported_pub_key_buf_size ); + + TEST_EQUAL( mbedtls_lmots_export_public_key( &ctx, exported_pub_key, + exported_pub_key_buf_size, + &exported_pub_key_size ), 0 ); + + TEST_EQUAL( exported_pub_key_size, + MBEDTLS_LMOTS_PUBLIC_KEY_LEN(MBEDTLS_LMOTS_SHA256_N32_W8) ); + ASSERT_COMPARE( pub_key->x, pub_key->len, + exported_pub_key, exported_pub_key_size ); + mbedtls_free(exported_pub_key); + exported_pub_key = NULL; + + /* Export into too-small buffer should fail */ + exported_pub_key_buf_size = MBEDTLS_LMOTS_PUBLIC_KEY_LEN(MBEDTLS_LMOTS_SHA256_N32_W8) - 1; + ASSERT_ALLOC( exported_pub_key, exported_pub_key_buf_size); + TEST_EQUAL( mbedtls_lmots_export_public_key( &ctx, exported_pub_key, + exported_pub_key_buf_size, NULL ), + MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL ); + mbedtls_free(exported_pub_key); + exported_pub_key = NULL; + + /* Export into too-large buffer should succeed */ + exported_pub_key_buf_size = MBEDTLS_LMOTS_PUBLIC_KEY_LEN(MBEDTLS_LMOTS_SHA256_N32_W8) + 1; + ASSERT_ALLOC( exported_pub_key, exported_pub_key_buf_size); + TEST_EQUAL( mbedtls_lmots_export_public_key( &ctx, exported_pub_key, + exported_pub_key_buf_size, + &exported_pub_key_size ), + 0 ); + ASSERT_COMPARE( pub_key->x, pub_key->len, + exported_pub_key, exported_pub_key_size ); + mbedtls_free(exported_pub_key); + exported_pub_key = NULL; + } + +exit: + mbedtls_lmots_public_free( &ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_LMS_PRIVATE */ +void lmots_reuse_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_private_init( &ctx ); + TEST_EQUAL( mbedtls_lmots_generate_private_key(&ctx, MBEDTLS_LMOTS_SHA256_N32_W8, + key_id->x, leaf_id, seed->x, + seed->len ), 0 ); + TEST_EQUAL( mbedtls_lmots_sign(&ctx, mbedtls_test_rnd_std_rand, NULL, + msg->x, msg->len, sig, sizeof( sig ), NULL ), 0 ); + + /* Running another sign operation should fail, since the key should now have + * been erased. + */ + TEST_EQUAL( mbedtls_lmots_sign(&ctx, mbedtls_test_rnd_std_rand, NULL, + msg->x, msg->len, sig, sizeof( sig ), NULL ), MBEDTLS_ERR_LMS_BAD_INPUT_DATA ); + +exit: + mbedtls_lmots_private_free( &ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS:MBEDTLS_LMS_PRIVATE */ +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_private_init( &ctx ); + TEST_EQUAL( mbedtls_lmots_generate_private_key(&ctx, MBEDTLS_LMOTS_SHA256_N32_W8, + key_id->x, leaf_id, seed->x, + seed->len ), 0 ); + TEST_EQUAL( mbedtls_lmots_sign(&ctx, mbedtls_test_rnd_std_rand, NULL, + msg->x, msg->len, sig, sizeof( sig ), NULL ), 0 ); + +exit: + mbedtls_lmots_private_free( &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 new file mode 100644 index 000000000..7802a70e6 --- /dev/null +++ b/tests/suites/test_suite_lms.data @@ -0,0 +1,263 @@ +LMS sign-verify test +# This test uses a fixed message, and then generates a private key, signs the +# 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 pyhsslms interop test #1 +# This test uses data from https://github.com/russhousley/pyhsslms due to the +# limited amount of available test vectors for LMS. The private key is stored in +# data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv. Note that this signature +# uses leaf key 0, so must be the first signature generated by the key if the +# signature is to be reproduced. Message data is random. Note that pyhsslms +# stores public keys and signatures in HSS form, which appends a 4-byte "levels" +# word at the start of the key/sig. We strip these 4 bytes from the signature +# and the public key before including them in a the test data. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# * pip3 install --user pyhsslms +# * cp data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv tmp/lms.prv +# * cp data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_pub tmp/lms.pub +# +# import pyhsslms +# +# private_key = pyhsslms.HssLmsPrivateKey('tmp/lms') +# public_key = private_key.hss_pub +# +# message1 = bytes.fromhex('60da1a17c88c59da8a730e6ca8effd37') +# sig1 = private_key.sign(message1)[4:] +# print('lms_verify_test:"{}":"{}":"{}":0'.format(message1.hex(), sig1.hex(), public_key.serialize()[4:].hex())) +lms_verify_test:"60da1a17c88c59da8a730e6ca8effd37":"000000000000000436c1e7d365851f12310f77341f4f994da12f39ad5d4cddf51563e80c98640f7edcc6ca027a76e48fe8f01f077f2733026c75e76fdb236b981e7bbe92e37527a5dc64d67449106387ab0ffffd5b5d4187165b4f03965dbdc5c652a4fc81ab83e951b24b61bf86d4d9a7e8d15206cac92c866b5bb358745306525955c56dfc925c48d0259865372043643c3b11daedd40d474c386daa36e3887bb65633cab290078eb2bc24c478a9ae18ac9fbd7c4a6e5338410b22adf02a27178c5a6e2d9ad403120d76c4dd27ec8974943b8226f86834364ac40984a96f1a1201e50eaf31c44e1c12b03a0cab40f6dcfc8acacfbd46333b48985e8b3a843c8f562a8007f69586444114adade8931adbdd636ee055423e33e4fddeff509a64b4589d25034adca9d55359c1489699cc6438c21da4b01d5403f53c2308fa28a9318235b788c15b37d359217301e9d0fa1b9a3b71ef95aca3657a976fd021ce20bbd4674d1a0cc551050b21ecd96f74a591bd84b5e9ae8b966592721a24bf0e16a44102c86999697ade9f7c937277fe8447b65573776507eda7725fbdf5ce27cdf6552d57b76e6f807a575dae1c9abaeb4667bcf0534ce78796f542b65a109bd9650b880d0ca638cf5de1ad97f6c52fa24951404cad923f649aabe664fabf318fc5910a8fecae45479b36c4961572a9d472b6de23cd601ae0d79ec98dcf9d0d5de6ebc9e71665d2b7066a8cdb93a5f65f48978fee68ed8c94a43af8759a2603321af84d22a4a37d7dfe6811f3d9b3c1bd9940214678f784658bf224a6e7efe22e30b962c7cbd18bb92df3d5e86b81493db30d761fb4775dab56a6c446f2b34d906944a72cf71f4f637f0668069f24ebb55e1c50b52c2f35b568b66fa648f5ebf10f74ff48246c3ead6cd6a5901c35f3411584760574c2db86ee5d23a094bffc16369f9845fe2570b1357315f401f1bc201ba165ee16a9afd811e4f9f34b8414134346598cd5fe76c883c5215d75106eceff18135c65473186ed1bcc45246d30aa7b1e561c46d0d1cca3da2e19cca1cfe4e89ca61de070d3cad2f96270962cd770c9154ce7bb5844171293e1a2722d3e340602895ae3c6848c83e264709af8677ff1d49580348d6084e41146410e537e6fdf91881fb8b858aaa04f064671971d082e1f7681eb9ac11da7b4776bedb0bdff6dcdab8facec17df48928e3be3603262cf39d0828ceca9230ccb610e8a6c7ea8e9a3a1d4e43d2f9c204d9327d6a2e8b4dc7b9a13838e1b08b414d9ef3495aee4f4fc05d71a5e8bd828f155a8a3b7ca6e22be59901fe627408a2e8ca8dc28458a4eda726b9e8f511c27495ea3bd3a50997d17a0de3394ccd51329e386ff39708e851cec61335e6b2bc6ad5aac9851a5467eba51cfc59804d674ca23232f8da4ad28c22f7dd54461e366e247e2ef28df07f6b3e4bc2c2e0b0233aee191c2efae467b2bd511c7cfd61dc96148b69b967b9d5eb0efe41a8b0197f8cdef88060d80ce1a2f3f649ab552b52bb1123eec2848c9dceff7ce5a1768d87e67105eda66493a017771170e3462566a08366aa01dfb2b0ca838c8018f0545000000068b991bed50319a6cb9ff040b92f1563889b3787c37145fc9737d4643f66ade33ebd85a2c29b8c64a581cff01b89d59807d6fade2d2c88872f77d0ed83d97c4b5438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":0 + +LMS pyhsslms interop test #2 +# This test case continues from "LMS pyhsslms interop test #1". +# The signature uses leaf key 1, so must be the second signature generated by +# the key if the signature is to be reproduced. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization), after generating the +# first signature: +# +# message2 = bytes.fromhex('92d036bde8c45b8bb5dea2a072560b1e29fc4bb7dc4549ce90bccee8a6e962a1') +# sig2 = private_key.sign(message2)[4:] +# print('lms_verify_test:"{}":"{}":"{}":0'.format(message2.hex(), sig2.hex(), public_key.serialize()[4:].hex())) +lms_verify_test:"92d036bde8c45b8bb5dea2a072560b1e29fc4bb7dc4549ce90bccee8a6e962a1":"00000001000000042fd4410a9c1947c00419216c64bc236a1620bde03ca9221e67f933bd2664f065e0cfc910c6a4317de4bda8c7fc1244ee1c102e8acc281d96c6a25d1925e087623fcb4faa00219e1f04a2c191ceceee98f2acd0fb1395fd984892f893a3ad862ff6def851e81915b9111288f84fb131e14979f1df6eecc774db45054041bfe74ec0446a0e6a6e01f9b402f41e784a2fcdc0cdccf0b89c2c8a9d2ab28e95e133b33dfb631619e75ec80a9c5d8f634f1d60feec2a5d9a5d6316fa7968734c26c3f60e53613096330a6fc1f779fe501db94b2a932ddc05740a872a8ec34c6d79c6689cd2cd6620d92ea89b39a62053c0bdd7596b360ff45de04bef0cd9b985f00681875e9f3465a71e5055e202dc51bf9ab29d227e8d2b09c6089f82cd356eb1622ada2233209f096cb35086fa2415434ef3ecd660bddf328d70e204d9a8be18319df1bd5c64072b30b72ec792c0a200b29429e262435b03f7fbb6dfcd76b9a84621c91e0ef646bd7367eead3582028a8ed9b40b1fe1562863ea43af350cdf0c965dc8329131df3f00b4b8a33548d7f7f1b03e952064e0f4cd9662af5a0d25ec8dc126d9621bf4707fbd525023ce91cbe05517bf2491e6455f2273b354c9d2f4a4364c3caf44c98ad23601cf1fc9cb490d2a9b9cdb1d60f0328e40734201e9e03f7af30fe6f0d6c7437fdd13573b012cd060a1a325b35d1a3d77e94a666d3873e267223a4e5bcf0752395570ad51d1ac7480cba32fcc8bf93439b8feafd0fb3520ab76d9ae2fba3b4600afce5fd96ff07d0e62579c16f993715f363982409ba38a46d09e6b448738f2bdb4277c65c933ea4a991fdc8021e3b9bbe5d00078b94ed1a78c61ee9d1dec014f99d23905a8fff335a9cca0228e7244a2a8b461970655b8a7f0f684c3f271c5f76924d851850b74754e24abaa9828d353976509dc4c4a241a0c314b80e400aceefe234fbdfb9af60d7c65752a4a396c4cdea1fec3478c263fb5883aa009f1845c4cb3f128c5eb9b290639c7c82fe33b17bd5ddb460a68d54be472769f77c73f7b4bfead2af4a9af6322f5bec9159d234e94a7d496cb6349a4b36fc7ca4e2c42168034bff62e687089fdd27a78484c788556edb58d7c911199752ca609a7906355f226756cd7c6c167b2a2929e8913fb2ec7c46c5caf73252f06cd51c5ca979d0b552831beeb5bcc25fba8ac83c8857633e3873adab0d23f1bb326a6c960e8bb1119e2f917c3892e9ad83f8af74abe0a0beee1734fdf5fe04024a6a644c2bbf88c6019d7115b0742898e90cc2d001efbc6f8e38eeedd5e9e9c777d1ecc6a2a9cf6d67a68781d99db1bbecdfe2e40dbe9074e7a69f0fa9037aecc31c9305c67129e0dbc8a66c8de6c18ed41746d794809bb3a5cc68c17db3052fe31e390ca862be3163660a1f70c5d2f026ed7649437600e38ee08e33f05aac9bcd8b7db309f2f41c34ba44304115ef8bbdba63629607daf67e2e642a726e021f6599032a0f8f3edef2ef5b007d3618856d48aec7894e9a4b802caf9c3f0022c6e61d34c38ba2ddef3c1b0797e7dc74faacb44ac72b5ea078f1a21c2cffc46ba000000063b71be980cffb4e8a8e310341d3b711ab19545ae90c3ac6adcbeb764419411a6ebd85a2c29b8c64a581cff01b89d59807d6fade2d2c88872f77d0ed83d97c4b5438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":0 + +LMS pyhsslms interop NULL-message test +# This test uses data from https://github.com/russhousley/pyhsslms due to the limited +# amount of available test vectors for LMS. The private key is stored in +# data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv. Note that this signature +# uses leaf key 2, so must be the third signature generated by the key if the +# signature is to be reproduced. Message data is random. Note that hash-sigs +# stores public keys and signatures in HSS form, which appends a 4-byte +# "levels" word at the start of the key/sig. We strip these 4 bytes from the +# signature and the public key before including them in a the test data. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# * pip3 install --user pyhsslms +# * cp data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv tmp/lms.prv +# * touch message.bin (create empty message file) +# * hsslms sign tmp/lms.prv message.bin (incorrect signature using leaf node 0) +# * rm message.bin.sig +# * hsslms sign tmp/lms.prv message.bin (incorrect signature using leaf node 1) +# * rm message.bin.sig +# * hsslms sign tmp/lms.prv message.bin (correct signature using leaf node 2) +# * cat message.bin.sig | xxd +# +# To validate the signature: +# * +# * touch message.bin (create empty message file) +# * echo -n -e "\0\0\0\0" > message.bin.sig; cat sig.bin >> message.bin.sig (restore the +# HSS levels) +# * cp data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8 tmp/lms.pub +# * hsslms verify tmp/lms message.bin +lms_verify_test:"":"0000000200000004b219a0053b6bfe1988ade7b0a438c106262366cb6338eb6ccd161326b29076d3493e88ab5df10ab456ddbac63f9cc7bc626a832664861a61125963f6e4b1fc202b0d6421cb1307451614d4d0e9e4509bc3991ede829f3805531912af12028c33128212a6e0539a458da092e83dcced8ffb1d9280e76593a239d3e87858905d3b4ae3864cd55972f5610759bb7d929d24ae262a1e028f140e90aa7375e43032c0bc28fe5fc25d53a26f4f9e6de18da2f697f82e409308e5b316413df8e85487391c46e784f9303f133ed332c88e6d1467cebffd9547592e907ceba2992a0442410c7a87104697a4ab3483d9b2af9df574edf23811cec0e681246f07ac74e1ddf64a7f7abc72d0a23b70d5f7c9649188eec8644f2437951640af4f673e6bb7d36a10c5c4c857f518974929824011dc79f484107388b92762acb11839c7cafec7daabdbe651f500930386b403ccec90a507829c18df23a800250d412a82b4072c94de24da9fa25720f1ee433953fca2d9b38ffc5c8b6328e69bf928936218bd253cac5a7122b74639ed7f4085d27efda2a698aff4bce385b475470adb19ab2095b3979e74e63914ef5430094e2028440f4d2aa448bb41f1d4481ad76c9b6671f4a7aafdbea44316aa97993fa31c56c34f0acd6295cd2fca8be9ea6af2f4d656f89b113cb3b3ce35753bc0128629372fade890397c297ee4c22e735e2b5f3c7383ed154cf0941884136bc6e51f860803b963c145795c8f573ab43953d25c0837bb13adbcfc506795db26fbd7a277d9532a23b5c472628944a3dcfc424e42fc54b2ed2cc8166cb82e9364af9120881313c97e429bed15bd9d46fe407f229cbc6daf1442e42c57664a7e832a809364750396a0b134efccf9a31e1ef1fdd2279d1179a673feda330b9989681c94d69eb197b6c3048623e49c98cc7cfc8d845c17f9059e7f15b72af8680cad2591cc9c135b2044fe7df45b8b6ef6e8af85ddb677f0897ffbda8131fff0eba1f94200f435bc26cfe5093c63f547620efb3bf8f905fe4ca1c40e163dfb6432c4acf068540c2c81c0392d375e99e3960973447beceefbd437f51616f85236d75815c51073277cc7ceca622bb76236d05a830e024a231566fb07f6f4e3671bc7fd5e22e4da1f4d4f4e56a179325b2ea9e51d6484df0941e0b46bcf4148e98530e9b3641e351b67073ace8438fac6d9a19988af4d594048f12eac4bbaa73eb15d597b1fdbf34ce9410520d9dc4b6bb7a99a12dcdc530c49bb67ca942adecb7adf27456eba9a9b416bb98b25c8020f4c2507b74a9ddb94f197ea42f03500bde751c04ec2c6b427ce0f80322a6b356f0d9d26531843639c7c7938b83541c58fedd0398d81b93032cb4892903a5b1cfd205b333702e7f80c1461a15edd6058c2e08d8afe44e4c5bfd7d9ac2578b5a16b4c4e43bad5f7b22041de5a95c6f64422db270e1f616e379a034fb3c08cf892af6df8af91c2767eb76bcf018e35d66fbf4ac1e5a6a10033ea118f8cd2edf57c2288a93f2f85b6ff41283b029e5c7b04bdac33b5aa79bf799292a0a046b98e6d13a2bb53a970dd0a5784034600000006c3faf2b844e6f17384998ae0616755eb7578458b7096078a36f9e556a2a091be47c0f85ffd8ee916734855a6d116fa1431ad6cff45d2a8a7f6c122f4d492df32438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":0 + +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. The private key is stored in +# data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv and +# data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux. Note that this +# signature uses leaf key 0, so must be the first signature generated by the key +# if the signature is to be reproduced. Message data is random. Note that +# hash-sigs stores public keys and signatures in HSS form, which appends a +# 4-byte "levels" word at the start of the key/sig. We strip these 4 bytes from +# the signature and the public key before including them in a the test data. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# * +# * cp data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv tmp/lms.prv +# * cp data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux tmp/lms.aux +# * +# * /demo sign tmp/lms message.bin +# * cat message.bin.sig | xxd +# +# To validate the signature: +# * Save message and signature in binary format +# * echo -n -e "\0\0\0\0" > message.bin.sig; cat sig.bin >> message.bin.sig (restore the +# HSS levels) +# * cp data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_pub tmp/lms.pub +# * verify tmp/lms message.bin +lms_verify_test:"6b7439e31ef128c54f1536f745ff1246":"0000000000000004163fc2e3d3267d8c0d9fd9e7bb7a4eae84c3d98cd565de361edc426067960fc3201d9be1c30f4e4edce91844753aa13ff21e92648ac795b7c29dd6140962b5a1fb97b02570402a498a495044edcb26d1321c52e91c60cc3feb8f8e84fc77f97fb6e7afbfe4c2f2203d8d84303e2dd212b652e08a2e5a24a333df859cea3c5a547561f7ce6d182e2a3f2f018ef7e0578621916cff905c0713fa5f2bf73248ae6985aebc4086b79ebf71b8dcbb592eb61dc6303d06dbda88063690361b0dd25ea1c2c6b4d82dddbe11740864c65c228d67e9a1710506e585a748e7e02b36706e5cff83b3589613f07c636ab7784d6a8288d33e80f063165a2ddcbb0d7da815df8043dfa500c3e313c533bf6aec959237c923813d3109bdaeb195b1337f4cf21c1c863f6261dca411819603a3ea60cf34c81b462c4979b357da2bcdf3128343ca5a8a957e3ca4eebb914d743862e29ef48e43e7c5a7aaf7a2fe1251c309c65e9143dcfb298fa0d353084f60c0779e1a09b040f13c1025ec99402b844ff9996decf4b5f0d32a0858126ff293472aa93fbc2017d39fee93ff9f0ca2752b25cfa12542bf19cc1b8c102d65b70dccf760f26cb546742ce909d45345f802a985bae6a0f922a9c2a3dc992fae9f6f2fba0c52cad82564bde6ed8af880ee7a5eb5c6436611e5da1c690831bed34e3dd65acf2b8f496b6448e957afc16c48b6cd733bc84e3606a1d0609f08015c14b5619a2723f9b22950efc7ff7b733c299fcd84ed89c4d5cd43a9a54f25fc0fa1370d184f9e8011b60ba38dfca0eeeb56ae37a5823718c8210db20c2de13c39e43970b0b53b85b9cf9ea0dd025e7db558b463c683980fe59e0defde41afe825cfb8606ca861602a7fefd7506edc81b7ab4a1e0626e0bac1f99be118dbc1e291028fc73d0a0ea6559ae1dcf7477d64742c9bef88ef04b2ee4d392cf1efa23d8b05d11d2414e64f4540623e11bbf57fb8ae219331db0df459a9849f2700e6fa7ff4edb0fc01764949e279e84374e7a57fb5ee6221b2b72dbcf2ab9c988fe07d21e169b4338887129ac503cc6c0912787778d51b4b921cf7bb17d4028b7faf6c21dd616a1ac3b50d595ae0e3662e7faa16b9dec7694462c7fb8539ece0af33cc5a3dc33641b8827bf4751a708d7bf286cf2e795b8f45b76e1109abd908d0388d6ab8ecea67b187aabd80349e4bd286e3b6eeb3535cc9c343a39fe90cb443906b19d2483b4c93d0e35cd68d9f5523d5400a2b1708ba3361bd0757ed69b1da8845594edf053995b2d96bed8210aaab25fc34b2dd58004ce800360f24861e5912ac339ed0a78548e303e728a41e05c11d79013e3971eafa8034e63ecf1c842f0d9e735ff3b5badfd63ae07f051c94a9a867260b517e5c2c75e88e03d069bd39816a2255c90de81bb79622145b7469853a02eac45289fd9f9f40e2fccdd8ddb740469331f61badc1b7f6e0145dfe30141ad2f26ac8d7ff5125dc4dff1fec57629cea4f7de4401fc056e9a38ea028ac9c666ccd3f527947672408a759a5791d9efdeb1ff25392413728a03d4c641f4ce1542b6952e7595f1eecf1060000000671b0912d734442146e128d0029101ad34a6d2d586640235c828d427dfaffdb156771f06926678fa50aa7167684c1de108944b2c4a3358f5e926368009e4500a8d4d501124bc25a4c9b1cfb954503f4ae26c92221e39c680843ae55cfca972e139c82e2e4469a703a1866fa0e6d76636591f4ad07f7d1eaa19077660ad46a6f9d534970e6a49e24621b7c7c283253dd22fb24eb7819fab84bab88e42555d5437d5afe06615a7e0d103cc8595616690f1337f4345cf418724f07d0dc4d2c0899b691691f397202204ef34342b5725dc6adfe549ab0b887572ad38113c407f96fcdfeea0ffc4f333addfec296169e53e3c5b24797a20f3b2f043f5e96920de9927da466f09389d3e52a5665f380f68666a019c201e710ab4c168d5ac952a02d5909a6fcaf498a33e2124e6a828203744ee3fe70465adde0cfbccc1b4634541638ab":"0000000600000004e18760ef2c86192aee88579e376f35cd153419d622803a483e79f6d368629308a8ab6ff663c4f108b2033af290dcedfa":0 + +LMS hash-sigs interop test #2 +# This test uses data from https://github.com/cisco/hash-sigs due to the +# limited amount of available test vectors for LMS. The private key is stored in +# data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv and +# data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux. Note that this +# signature uses leaf key 1, so must be the second signature generated by the key +# if the signature is to be reproduced. Message data is random. Note that +# hash-sigs stores public keys and signatures in HSS form, which appends a +# 4-byte "levels" word at the start of the key/sig. We strip these 4 bytes from +# the signature and the public key before including them in a the test data. +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# * +# * cp data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv tmp/lms.prv +# * cp data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux tmp/lms.aux +# * +# * /demo sign tmp/lms message.bin (incorrect signature using leaf node 0) +# * rm message.bin.sig +# * /demo sign tmp/lms message.bin (correct signature using leaf node 1) +# * cat message.bin.sig | xxd +# +# To validate the signature: +# * Save message and signature in binary format +# * echo -n -e "\0\0\0\0" > message.bin.sig; cat sig.bin >> message.bin.sig (restore the +# HSS levels) +# * cp data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_pub tmp/lms.pub +# * verify tmp/lms message.bin +lms_verify_test:"0705ba8297c7b9fa5f08e37825ad24a0":"00000001000000040a432454b99750f7b703f0280f92818b0570d0267a423b377be7cf0561305d4ce987b9d8dbc1c3f8ba410bbe6b921406eb802688d2dd8a1a6fa4a124cbcae9b5a210f583a956384c06311953b038b4ad2c2808224fc3a6410cd3b89274371956bcd4253a251cba6409b09c822e1d29d7a037648a6f2562d0df6359a043622f256f5ac79736c08fc4185758ff002a8397e560d5812373946348afba2ccf2cc0f3ba741ec076d4587a54b8b625804b814c30540152a3dc843a590c94cc23ba857e4c458c8ab687b5b9b68837ee890454cc19bb5f42a1e6dc051803fab50b440067a903013f675a774b5d02cd56289518d65f869f22b2e2b58d499e9e3929ec5a9f5d6d6e03cf91486094aba7c88491cde35b81c175c40410bc402d20f0a73a4da844d3a1d47e57618b7f18fa5ac85e877b5faa1e0b6733c2d96b2970fdd6e606435e3ec50eafa88f84fb7512217aa4be5858a140f242603bda634d76c484a184298c4da903094468d032b88586fd2f35182405cd85115af6a0bbd431f2e44217a1691dd8887db91d3b97264ff552ae7dc110a3a111f2bf74ce42079055dfb8390a16d67f28b738f837aa7880f3134deabcf6ec74cdb521bff44df61c999bf7a8ddc43b64812cd4f3bfb15104867d5e585d1cbf99738e0df92660b3e9135a4377d1199b8b97362fc87ce3c99db3b8aba63ba35eb353e5ec79bcee82b9ccc1b4f7d1b8ce7e5f8813d007be3d0e45cb8e7173337a5a7c4d32ea5116e0fdbd7846ea1f366a531449c78cd7a16ce5bffcd6cccf54b7f249a74e0df6b07f6b48db42eb958ff18b06995368af0cadd82f44cf44e4b53f0993de5f06b289bee41cd25f90a9fbd1bfb1ab2451c96b07adcfb5210d291dd505ea30e5d30395c8d84eabccdd2c7d6f28a88f5e5d245a6980c57810cfe17c9a37ef5e79b7b9ca755d56a789d21985372bed42ae2830d81ebf0fad6c721bd1d3ee91ae363f40d386aac23e7c0db965539ce9bff38f0f24bec3227b5a24f4cd7fa71ca9d306faa3fc4726cdb6634f218897b79a4aed67a58799285104eed74703ec4af6d5738b27b4d6fb71e52c1149069483a7cca6c3fccbdff77312ff5c635d8b0ccd53dbaf7b498727f7c7a70d3fd1c3f217e2cbd0dfe91258acb7f79f53f56012a82da997ea777b76dac0472e5f9830a93fb09703b1c0e45cbfbf641de94fcc6c609f02a5b31ad5821ba6cd48829fc5e0c4ad78e11e4cac8efbb1b170c794b7b131b0c1c4e39fdef81db9e7acced5ec824aed0c4e6b57fd1add4191e87be1446c7c519eb671205ce8c5855ad7a2b9ff7a9cd5c45336f508d0f8d2c1152dc2656650bdaf8fced642f3a4d445b5fc49910bdbdc9635de0086ee9582a796ca9f6052de805f41dfbd3e94982a05cbd36bab583dd5b1586ddbb3b1a45f1a265bec062c1a50d220870c0c622d852e650a67f31e8eb3d19e964de0926712b7f429ad05024b8db51eb6702c39580f62f037388862251bf66f02edee9615a63957eab75b28501f9f26cecd09a5c949127c9a3095036667fce8e45ba75568d5160fa1725a9e0038145d948f437640dc4441000000066e8db13a9e79d10a4e067aad448a1847b5489a62cde3054ee1e5ff2e37549d516771f06926678fa50aa7167684c1de108944b2c4a3358f5e926368009e4500a8d4d501124bc25a4c9b1cfb954503f4ae26c92221e39c680843ae55cfca972e139c82e2e4469a703a1866fa0e6d76636591f4ad07f7d1eaa19077660ad46a6f9d534970e6a49e24621b7c7c283253dd22fb24eb7819fab84bab88e42555d5437d5afe06615a7e0d103cc8595616690f1337f4345cf418724f07d0dc4d2c0899b691691f397202204ef34342b5725dc6adfe549ab0b887572ad38113c407f96fcdfeea0ffc4f333addfec296169e53e3c5b24797a20f3b2f043f5e96920de9927da466f09389d3e52a5665f380f68666a019c201e710ab4c168d5ac952a02d5909a6fcaf498a33e2124e6a828203744ee3fe70465adde0cfbccc1b4634541638ab":"0000000600000004e18760ef2c86192aee88579e376f35cd153419d622803a483e79f6d368629308a8ab6ff663c4f108b2033af290dcedfa":0 + +LMS hsslms interop test #1 +# This test uses data from https://github.com/pmvr/python-hsslms due to the +# limited amount of available test vectors for LMS. The private key is stored in +# data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# pip3 install --user hsslms==0.1.2 +# +# from hsslms import LMS_Priv, LMS_ALGORITHM_TYPE, LMOTS_ALGORITHM_TYPE +# import pickle +# +# with open('tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv', 'rb') as private_key_file: +# private_key = pickle.load(private_key_file) +# +# public_key = private_key.gen_pub() +# +# message = bytes.fromhex('60da1a17c88c59da8a730e6ca8effd37') +# sig = private_key.sign(message) +# +# print('lms_verify_test:"{}":"{}":"{}":0'.format(message.hex(), sig.hex(), public_key.get_pubkey().hex())) +lms_verify_test:"60da1a17c88c59da8a730e6ca8effd37":"00000000000000041394a893e40be29923751880ca3cd10b5a67c2356c87c240e0733c3a3781b421f89dcedd553f5c1c0fdf4e53e4e484766860bf77e6a1e5911c9a389390f7d62accb581ddd4d6479c88a9ba3c20235805bb544db1da6667c5bd6caec6a5cc0afb02ebb35c1db7aac5d16446d4f8fc3518ed44ceb4eab20974627ccea5b1571a292bb2fa08ccb284957605083bfba07a33f233c66187bebd4523d095e21546be84ba56ed61768b9210fc754c78ca2d6a788e1558533d5407c45b04a0bbe6a20d0660efec80e1e468874d81c98d81acc87981c236b68695fbaf70d188617fdb86a5840c835687249f688434159035778260026d536570f24a422d5255d2572603fdf631668074dce5fc469710aa99a1f21280708e73bcd4e50492d2ff1dbaea1058974fbe9bc393a4f837987faf0175b814ebafa02095bffee2518a6fbb3555de9b3ff0c87c0c7b2c61ce3ef25e70e1a2ff5aa6dc7dfb3533e53192bc68807727b76c8752bdaa2c8d0c66e6bd94ff4df2f9fcb5609cf9bd04635743340736b84a98c3769bef074c081ebdd0fd17e853165dfa4764b23c63dd8a4a8c10b58a790ab92f81a32973f0f60d9f33d801a2c476190a7f8521a998220d8f838c3932da4dab89f62e028973b1891aa0954faf3da6174ea445c0e6ec27a58bb74000253fd3d76909298d44b3beaea58f130102cba5d928afcec92991f9483294f0fb52c16df4e98c0839e058d064921582b144602306d0a1ff623bbc1b1de106045384cb0f20db3198d99b266f83cb7c4585786477cb38b140f7cc48fecb9c5c272df2881750af48da8ace04e1b109de3a295c91373c55e8dd36cdf455c17a0b9c27cbbaa80a7571cf5d5074c384948a7e006ea6346e2e8fd1082f0d7a498c6445ed2da31014f4476e41e1367cffac8ac93b7a59bab5e23dcc9130f8e3264b2920e503246e11fbb15b599e58350cdd60e3a370c7cb0a81e73fa17eb2f12702ff3c1cb6a75d7718687d545cf9d00d4bb277905291ee86f1dfc045d9c59d6aca2faa90d2654dffc652fe89c4b37048f8c46a6410aff4e46c281c1d4b2f6ea1408d0615bac721ece31a9a69c70f3b860d730996ad735eeb376022c4828135466101cdfb2c88cf02864c40bf5c5aa63e44d58c8f28933d8d3c53883a95f4109a185b7fe6eb1d87d76823e63bf9d72d96b60d2cdcf942ca06d4f278711eb1eaadf11e9bffc7af361ae0c0fa23ba2bbc2f673a05c1ee3f3ccb3bfab4dffc4b9c234b0b9c34fd1b5f0d01c4e10cfd0800f90ade702dff2c893f098de1637de094fd959440009ccb34dff6cab72fe80e839e6e89551274e6cf6e862532f524c804259a0c8e4622c106df6431dbac870cac64f7099674c8050f5149326d961af7486e8229f5b5eba743ef78dc56b4f3acb1ed5029fba223223a5e835abd61409316a68c899abe85c0514642dff696da0be97416d774fa7f5dcc3aa2c8469b47516f7b27cbbc66faa4e62b6a3201f7976ea20b89ef349a497967c093e3431df9d619a11ed2cd930324438f4cc9d11654e0c9d229d6bd239487598a3482f63294e9e85c29a576b1c86d0884000000064c6b6388b7436123dac99e0ec7fe53b075e2ba9844505ce1eb3c7f70332c6ac543dcda2e63b26f5efa39ced6095a54625e67ab25d3df068e903eaaee894ac0f1fdeb4a2f1390f655db3608583eacfb0be4282f7bd1c42c5d748d524d7cdcd45878dea56cbc11a63bebbd74a5413ce72a931b1d4794c78c4cf16315bf2e055bb3305fe0272c8b916856cc27aa7a773ddce62afa7bb4da76c287e0ed3ed10452512de82c051f17b49c608b1a259e16a3812c0de684f2cb1ee59296c375376f146e2b0cc299ef41ed8e6fdf0557ec8d95fa026970f8d47c8347fed1e37e018413c5e813d1726ea18bc926ed02840349ab3b2adc8758a9cd57be38e9e76869762a81bb79721ca1c031c9dfdc3735fe9318064b62c2a7e8e2ec099963257b0705aac812dbc8cc3fbeea81af7c0d592c7e2ad1c21e877d4ae392b13ac1b57a8311d406":"000000060000000447cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d":0 + +LMS hsslms interop test #2 +# This test uses data from https://github.com/pmvr/python-hsslms due to the +# limited amount of available test vectors for LMS. The private key is stored in +# data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv +# +# To produce another signature with this message and key (note that the actual +# signature bytes will differ due to randomization): +# pip3 install --user hsslms==0.1.2 +# +# from hsslms import LMS_Priv, LMS_ALGORITHM_TYPE, LMOTS_ALGORITHM_TYPE +# import pickle +# +# with open('tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv', 'rb') as private_key_file: +# private_key = pickle.load(private_key_file) +# +# public_key = private_key.gen_pub() +# +# message = bytes.fromhex('60da1a17c88c59da8a730e6ca8effd37') +# sig = private_key.sign(message) +# +# message = bytes.fromhex('92d036bde8c45b8bb5dea2a072560b1e29fc4bb7dc4549ce90bccee8a6e962a1') +# sig = private_key.sign(message) +# +# print('lms_verify_test:"{}":"{}":"{}":0'.format(message.hex(), sig.hex(), public_key.get_pubkey().hex())) +lms_verify_test:"92d036bde8c45b8bb5dea2a072560b1e29fc4bb7dc4549ce90bccee8a6e962a1":"00000001000000042e758f2a0e8f301af58847b6973a15fe4856b91af88a1ff601e2f0e87bde33afbc39202a66e38931fbecd7d493cf957b37eeb57ed2e4d8f693b97adafa8241746d775cfb9471688d935e090581eb8586e7004d6b8855963d82ccb6f79df2d93dd35848556da6735def0f0c6c8fc969c1df692341f6a99626eff37d20226cef361c8802a995fa43535fe2336d8ae468c78eb6a7082e27c2c6317c034369b588e3d65a2eeac9804b427702dc49f681a841076813ed407399aa778259e7c34c750baa6d296a384e1274facaba9e2214d197628f5df7b2bf3896fedeab377b8edb775d6e8d67f1474ba3066794447f8f8e0c13552a007557a1f1c3b9bd2b41d9b446c6bcf36c828fd4c80850a31ee603065f5cc90d198df03835195b14e27da7bf727a16081fcc787f1dc7fa6da8b9ff908fb2c02d6f2a183486de0e39cd7da7fcdee0c8e96876c56ad9b0b18e4e4999e2c81a618aa4b27e050ce488dbb1e79089131afacc446cdf15b625f4e011f8d8160bb93f326bca3bb56fa41e34893d55f17d746fc142297997c5dbbba8f6b6c80678168ba455f12bac6982e5192de5462a46e14a45a01ce9e07279aa301dfd0fa9a12c6a55059b19a19d7afbe99779ea130ddeeb5ecb67d2ddb6c1c5d198e421b78091efa5aa429e1eb052760c0d8e2eb0c0ced000e93f7f265611a385f77c0cece0496eb29010f710e70a768d3713f0b7fc60c8ce372dc3234f27c7a1c2776a939ef70c7be869337b967df2223d4f20dca697e3bb6d0e53bbad153ff08d579f60c8535710f253b90e73ee9a19e1e57df66ec6c85ad1b4cea28a9d62fc5a4cf130f70b910dbc7e6f0e6b0cce1a1b5ff106b7f0b101405c0989084b2c94977116b98d15d6062a8d77d660aa813d432cf3338484308b7beed10236081f52da44eb807f9a75fd4cc1ba998ef3fc2e4791712597c786dd46431468bb4a1975a6cd854a1da23912fc99160f51df484efc9371c2d8e028d9468635cf93226f5a8834d14cead59e5d2a61dd6440d7b91c903ae8823907b75595c4828c7710036b347dcfb67f8561e835a53f569c8b3a1cd4317b2a6b2243100ee3d9468f9191acf2276d18dde9ebf2e11a48ba1fc1a15dc51091d3358d8d1f65ec7d84b97bb1669a9141f74065454f08e5ef25432b7635b8ec673ca70e4b3c25d07975a6fb725a56f28c1b5a81a6da2fe0a2c3474275926f9819a25b942462a68097e1cf6d9ae94f6b1f76b54addaeda04f9fc8db025fd6c453e1ad928f9323bf1381fce1893938828612728185d22a3d45d21ce762c066ab53a582c487d76d431e5b8f65a382142dd823d4620931e5572a4e6aee69986421afa119634bc8ea88aa6535c4d619ca0e0af94934637bc0c834e5e2a7a2853fa73835d00e13e5f26ad085ef66c8efb60097860cb199e03596a3b8f0ec78690d527bbc9363dd9702226788b1529871df74918ae2a4e02745043bd5ee8ab027826fb4cd54b0c27d99076757a1b41e2725ec02adc7926e8213796a8aa1740a2dc675437771e0364a83b0bd64c9620f6c203d92626ff29ef736eac0e13c71fd1957333ee0048000000061f7b7d6f916710efe9ed625ae689c67b3cc1cdf0d672e58c0b86b3839bbba2c243dcda2e63b26f5efa39ced6095a54625e67ab25d3df068e903eaaee894ac0f1fdeb4a2f1390f655db3608583eacfb0be4282f7bd1c42c5d748d524d7cdcd45878dea56cbc11a63bebbd74a5413ce72a931b1d4794c78c4cf16315bf2e055bb3305fe0272c8b916856cc27aa7a773ddce62afa7bb4da76c287e0ed3ed10452512de82c051f17b49c608b1a259e16a3812c0de684f2cb1ee59296c375376f146e2b0cc299ef41ed8e6fdf0557ec8d95fa026970f8d47c8347fed1e37e018413c5e813d1726ea18bc926ed02840349ab3b2adc8758a9cd57be38e9e76869762a81bb79721ca1c031c9dfdc3735fe9318064b62c2a7e8e2ec099963257b0705aac812dbc8cc3fbeea81af7c0d592c7e2ad1c21e877d4ae392b13ac1b57a8311d406":"000000060000000447cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d":0 + +LMS negative test (invalid lms type) #1 +# This test uses the data from hash-sigs interop test #1. This test has a valid +# LMOTS type (0x4) but an invalid LMS type (0x5), and should fail. +lms_verify_test:"bfff9cd687351db88a98c71fd2f9b927a0ee600130a112533b791041d30cb91665fc369a5ac7cc9a04547414ac45288081d19d4a600579c73ac4bc953de03ad6":"0000000000000004e474c96c496b231ecedcd92566ab3e1cdfac83f7e56abaae6571401e212e59bdbcc18105e0709249510d13d7ae1091558c217033316ac70a36aae764bd0f4032e369453c634b81061079d216d03d3c55976a1aabc00287b307297ae03587ba20839460daae85d26dfcef7638c10a1d8559612e5e9ced1a4205a52ca0ce88e58602e59cf9ab34adf2e958e56570573297b99f733fbbf58d2440526fd4dc15c5509e8a11405f6c08772abcf58731fbf9a73642670e3247c5f70905f0fa81f6174bf32977209923507525a170fcd260e81f04193583fbcd305ac245c80eb337ca326fd1105e73748fab8a1f0c8d8a99f011718e7aae027a34d2a85ff18769fa277810126a86b51b096a04d8e28a4fb8c5e14e50a67cb1ee88e43e5cc077902442f5d9c55ac2b8acdd76c67bc740c6083aba4e3cb404c23f1f3118337563fef6a4b01fb476810c5b5682d0aecdccd55c85a4af50e9150f7d83dcccd8e822a302e6e5a52e00505e6e65338dcfb9cfbe22594e9e18ebde36af29450c5ea31523019cf64fd6eca8c77d98c2a146dcedd51bf6c61c1f7cacbce3ab20c8606930cc42737e17f2703cf0980aef560768d1ac5585c05a60a5f94db15f5ac4d4df5cbd81430878d4e9b77346e3a6538b80b80873e3e6c37860470091979296631440adb8cc71991aba2a4dd2884764878306fe774a25512cfbf080f2829ea2903ffa748dd187c21aef918ee3436a1bd336c3d09cc1f748d7528db90a98f69078b82c4d23de7bcec092a292d2b8cac71a5c87d008f128b89a5e608a4501aef41e9f17e4056ed4767957188f780159daebf327751386980b0fca5a2d36b141acff957f46ce2381897099619475db9d3a613e7ef98b056f42b4d6eafb1d62eebbe46a7502f893fbd36ccfb12a280f45ffb93f050eb280bf0a6cd640abdea8590bffb98bdb29ee3a31daa0fa3ab35fee11dc1b7d1fcea82b0e284b2e35b34e77c3f401ed887e7fc6c97b327f76f99caca2f355afa2753a8923bfb06fb2a9df08d31c93882e34ef5a3cccc9d078855334bdf909ae418b177724c42fb1d586d212c4474932acce295236030f4379158957300fe9fdc5cc9145e3ded50cf9f5a8e19321961536c4a47fffc3eb4383fb78a5d2aeed5b45b92132b5e2a53e3b67841fa2e1bd217ee2c30812c4eb1bd4f8c85b328e23d27f12a2fad5c6b236c87f8fd1aad441416e53ebd4d45d4bf601b94eb37dc9a065218ae58e69dba1250bb20626baeda961b3ef11d217697e73f41fa3870d726a032bc4a388fb12c023822945df058e22f54e5f6377eab34297c57883515204fc189d0d4b0ad9bacb24acf7f9d55e7c6368bb8ababd7622f586ec22683306c5d88d5244219a3952adbd85c89893a441a58b532e15600cd5afdbb5b441e1670b72656c7995189bdf993154e09912db8c4ddaff0e75591720230cf99c8b71cd841dffc4372c5e0f9ff906a379d28d6884351609bf7c849ebfabfb049ae986bbb8467251dbf5ccdd05a86ff6ce1392f7ca1bd49595ad9ad805d71b389afab1865f7f69dc24662af19934f025ced212536509500c132aec000000058b991bed50319a6cb9ff040b92f1563889b3787c37145fc9737d4643f66ade33ebd85a2c29b8c64a581cff01b89d59807d6fade2d2c88872f77d0ed83d97c4b5438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMS negative test (invalid lms type) #2 +# This test uses the data from hash-sigs interop test #1. This test has a valid +# LMOTS type (0x4) but an invalid LMS type (0x7), and should fail. +lms_verify_test:"bfff9cd687351db88a98c71fd2f9b927a0ee600130a112533b791041d30cb91665fc369a5ac7cc9a04547414ac45288081d19d4a600579c73ac4bc953de03ad6":"0000000000000004e474c96c496b231ecedcd92566ab3e1cdfac83f7e56abaae6571401e212e59bdbcc18105e0709249510d13d7ae1091558c217033316ac70a36aae764bd0f4032e369453c634b81061079d216d03d3c55976a1aabc00287b307297ae03587ba20839460daae85d26dfcef7638c10a1d8559612e5e9ced1a4205a52ca0ce88e58602e59cf9ab34adf2e958e56570573297b99f733fbbf58d2440526fd4dc15c5509e8a11405f6c08772abcf58731fbf9a73642670e3247c5f70905f0fa81f6174bf32977209923507525a170fcd260e81f04193583fbcd305ac245c80eb337ca326fd1105e73748fab8a1f0c8d8a99f011718e7aae027a34d2a85ff18769fa277810126a86b51b096a04d8e28a4fb8c5e14e50a67cb1ee88e43e5cc077902442f5d9c55ac2b8acdd76c67bc740c6083aba4e3cb404c23f1f3118337563fef6a4b01fb476810c5b5682d0aecdccd55c85a4af50e9150f7d83dcccd8e822a302e6e5a52e00505e6e65338dcfb9cfbe22594e9e18ebde36af29450c5ea31523019cf64fd6eca8c77d98c2a146dcedd51bf6c61c1f7cacbce3ab20c8606930cc42737e17f2703cf0980aef560768d1ac5585c05a60a5f94db15f5ac4d4df5cbd81430878d4e9b77346e3a6538b80b80873e3e6c37860470091979296631440adb8cc71991aba2a4dd2884764878306fe774a25512cfbf080f2829ea2903ffa748dd187c21aef918ee3436a1bd336c3d09cc1f748d7528db90a98f69078b82c4d23de7bcec092a292d2b8cac71a5c87d008f128b89a5e608a4501aef41e9f17e4056ed4767957188f780159daebf327751386980b0fca5a2d36b141acff957f46ce2381897099619475db9d3a613e7ef98b056f42b4d6eafb1d62eebbe46a7502f893fbd36ccfb12a280f45ffb93f050eb280bf0a6cd640abdea8590bffb98bdb29ee3a31daa0fa3ab35fee11dc1b7d1fcea82b0e284b2e35b34e77c3f401ed887e7fc6c97b327f76f99caca2f355afa2753a8923bfb06fb2a9df08d31c93882e34ef5a3cccc9d078855334bdf909ae418b177724c42fb1d586d212c4474932acce295236030f4379158957300fe9fdc5cc9145e3ded50cf9f5a8e19321961536c4a47fffc3eb4383fb78a5d2aeed5b45b92132b5e2a53e3b67841fa2e1bd217ee2c30812c4eb1bd4f8c85b328e23d27f12a2fad5c6b236c87f8fd1aad441416e53ebd4d45d4bf601b94eb37dc9a065218ae58e69dba1250bb20626baeda961b3ef11d217697e73f41fa3870d726a032bc4a388fb12c023822945df058e22f54e5f6377eab34297c57883515204fc189d0d4b0ad9bacb24acf7f9d55e7c6368bb8ababd7622f586ec22683306c5d88d5244219a3952adbd85c89893a441a58b532e15600cd5afdbb5b441e1670b72656c7995189bdf993154e09912db8c4ddaff0e75591720230cf99c8b71cd841dffc4372c5e0f9ff906a379d28d6884351609bf7c849ebfabfb049ae986bbb8467251dbf5ccdd05a86ff6ce1392f7ca1bd49595ad9ad805d71b389afab1865f7f69dc24662af19934f025ced212536509500c132aec000000078b991bed50319a6cb9ff040b92f1563889b3787c37145fc9737d4643f66ade33ebd85a2c29b8c64a581cff01b89d59807d6fade2d2c88872f77d0ed83d97c4b5438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMS negative test (invalid lm_ots type) #1 +# This test uses the data from hash-sigs interop test #1. This test has an +# invalid LMOTS type (0x3) but a valid LMS type (0x6), and should fail. +lms_verify_test:"bfff9cd687351db88a98c71fd2f9b927a0ee600130a112533b791041d30cb91665fc369a5ac7cc9a04547414ac45288081d19d4a600579c73ac4bc953de03ad6":"0000000000000003e474c96c496b231ecedcd92566ab3e1cdfac83f7e56abaae6571401e212e59bdbcc18105e0709249510d13d7ae1091558c217033316ac70a36aae764bd0f4032e369453c634b81061079d216d03d3c55976a1aabc00287b307297ae03587ba20839460daae85d26dfcef7638c10a1d8559612e5e9ced1a4205a52ca0ce88e58602e59cf9ab34adf2e958e56570573297b99f733fbbf58d2440526fd4dc15c5509e8a11405f6c08772abcf58731fbf9a73642670e3247c5f70905f0fa81f6174bf32977209923507525a170fcd260e81f04193583fbcd305ac245c80eb337ca326fd1105e73748fab8a1f0c8d8a99f011718e7aae027a34d2a85ff18769fa277810126a86b51b096a04d8e28a4fb8c5e14e50a67cb1ee88e43e5cc077902442f5d9c55ac2b8acdd76c67bc740c6083aba4e3cb404c23f1f3118337563fef6a4b01fb476810c5b5682d0aecdccd55c85a4af50e9150f7d83dcccd8e822a302e6e5a52e00505e6e65338dcfb9cfbe22594e9e18ebde36af29450c5ea31523019cf64fd6eca8c77d98c2a146dcedd51bf6c61c1f7cacbce3ab20c8606930cc42737e17f2703cf0980aef560768d1ac5585c05a60a5f94db15f5ac4d4df5cbd81430878d4e9b77346e3a6538b80b80873e3e6c37860470091979296631440adb8cc71991aba2a4dd2884764878306fe774a25512cfbf080f2829ea2903ffa748dd187c21aef918ee3436a1bd336c3d09cc1f748d7528db90a98f69078b82c4d23de7bcec092a292d2b8cac71a5c87d008f128b89a5e608a4501aef41e9f17e4056ed4767957188f780159daebf327751386980b0fca5a2d36b141acff957f46ce2381897099619475db9d3a613e7ef98b056f42b4d6eafb1d62eebbe46a7502f893fbd36ccfb12a280f45ffb93f050eb280bf0a6cd640abdea8590bffb98bdb29ee3a31daa0fa3ab35fee11dc1b7d1fcea82b0e284b2e35b34e77c3f401ed887e7fc6c97b327f76f99caca2f355afa2753a8923bfb06fb2a9df08d31c93882e34ef5a3cccc9d078855334bdf909ae418b177724c42fb1d586d212c4474932acce295236030f4379158957300fe9fdc5cc9145e3ded50cf9f5a8e19321961536c4a47fffc3eb4383fb78a5d2aeed5b45b92132b5e2a53e3b67841fa2e1bd217ee2c30812c4eb1bd4f8c85b328e23d27f12a2fad5c6b236c87f8fd1aad441416e53ebd4d45d4bf601b94eb37dc9a065218ae58e69dba1250bb20626baeda961b3ef11d217697e73f41fa3870d726a032bc4a388fb12c023822945df058e22f54e5f6377eab34297c57883515204fc189d0d4b0ad9bacb24acf7f9d55e7c6368bb8ababd7622f586ec22683306c5d88d5244219a3952adbd85c89893a441a58b532e15600cd5afdbb5b441e1670b72656c7995189bdf993154e09912db8c4ddaff0e75591720230cf99c8b71cd841dffc4372c5e0f9ff906a379d28d6884351609bf7c849ebfabfb049ae986bbb8467251dbf5ccdd05a86ff6ce1392f7ca1bd49595ad9ad805d71b389afab1865f7f69dc24662af19934f025ced212536509500c132aec000000068b991bed50319a6cb9ff040b92f1563889b3787c37145fc9737d4643f66ade33ebd85a2c29b8c64a581cff01b89d59807d6fade2d2c88872f77d0ed83d97c4b5438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMS negative test (invalid lm_ots type) #2 +# This test uses the data from hash-sigs interop test #1. This test has an +# invalid LMOTS type (0x5) but a valid LMS type (0x6), and should fail. +lms_verify_test:"bfff9cd687351db88a98c71fd2f9b927a0ee600130a112533b791041d30cb91665fc369a5ac7cc9a04547414ac45288081d19d4a600579c73ac4bc953de03ad6":"0000000000000005e474c96c496b231ecedcd92566ab3e1cdfac83f7e56abaae6571401e212e59bdbcc18105e0709249510d13d7ae1091558c217033316ac70a36aae764bd0f4032e369453c634b81061079d216d03d3c55976a1aabc00287b307297ae03587ba20839460daae85d26dfcef7638c10a1d8559612e5e9ced1a4205a52ca0ce88e58602e59cf9ab34adf2e958e56570573297b99f733fbbf58d2440526fd4dc15c5509e8a11405f6c08772abcf58731fbf9a73642670e3247c5f70905f0fa81f6174bf32977209923507525a170fcd260e81f04193583fbcd305ac245c80eb337ca326fd1105e73748fab8a1f0c8d8a99f011718e7aae027a34d2a85ff18769fa277810126a86b51b096a04d8e28a4fb8c5e14e50a67cb1ee88e43e5cc077902442f5d9c55ac2b8acdd76c67bc740c6083aba4e3cb404c23f1f3118337563fef6a4b01fb476810c5b5682d0aecdccd55c85a4af50e9150f7d83dcccd8e822a302e6e5a52e00505e6e65338dcfb9cfbe22594e9e18ebde36af29450c5ea31523019cf64fd6eca8c77d98c2a146dcedd51bf6c61c1f7cacbce3ab20c8606930cc42737e17f2703cf0980aef560768d1ac5585c05a60a5f94db15f5ac4d4df5cbd81430878d4e9b77346e3a6538b80b80873e3e6c37860470091979296631440adb8cc71991aba2a4dd2884764878306fe774a25512cfbf080f2829ea2903ffa748dd187c21aef918ee3436a1bd336c3d09cc1f748d7528db90a98f69078b82c4d23de7bcec092a292d2b8cac71a5c87d008f128b89a5e608a4501aef41e9f17e4056ed4767957188f780159daebf327751386980b0fca5a2d36b141acff957f46ce2381897099619475db9d3a613e7ef98b056f42b4d6eafb1d62eebbe46a7502f893fbd36ccfb12a280f45ffb93f050eb280bf0a6cd640abdea8590bffb98bdb29ee3a31daa0fa3ab35fee11dc1b7d1fcea82b0e284b2e35b34e77c3f401ed887e7fc6c97b327f76f99caca2f355afa2753a8923bfb06fb2a9df08d31c93882e34ef5a3cccc9d078855334bdf909ae418b177724c42fb1d586d212c4474932acce295236030f4379158957300fe9fdc5cc9145e3ded50cf9f5a8e19321961536c4a47fffc3eb4383fb78a5d2aeed5b45b92132b5e2a53e3b67841fa2e1bd217ee2c30812c4eb1bd4f8c85b328e23d27f12a2fad5c6b236c87f8fd1aad441416e53ebd4d45d4bf601b94eb37dc9a065218ae58e69dba1250bb20626baeda961b3ef11d217697e73f41fa3870d726a032bc4a388fb12c023822945df058e22f54e5f6377eab34297c57883515204fc189d0d4b0ad9bacb24acf7f9d55e7c6368bb8ababd7622f586ec22683306c5d88d5244219a3952adbd85c89893a441a58b532e15600cd5afdbb5b441e1670b72656c7995189bdf993154e09912db8c4ddaff0e75591720230cf99c8b71cd841dffc4372c5e0f9ff906a379d28d6884351609bf7c849ebfabfb049ae986bbb8467251dbf5ccdd05a86ff6ce1392f7ca1bd49595ad9ad805d71b389afab1865f7f69dc24662af19934f025ced212536509500c132aec000000068b991bed50319a6cb9ff040b92f1563889b3787c37145fc9737d4643f66ade33ebd85a2c29b8c64a581cff01b89d59807d6fade2d2c88872f77d0ed83d97c4b5438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMS negative test (invalid leaf ID) +# This test uses the data from hash-sigs interop test #1. In this case, +# the leaf ID is 1024, which is invalid for MBEDTLS_LMS_SHA256_M32_H10. This +# test should fail to verify the signature. +lms_verify_test:"bfff9cd687351db88a98c71fd2f9b927a0ee600130a112533b791041d30cb91665fc369a5ac7cc9a04547414ac45288081d19d4a600579c73ac4bc953de03ad6":"0000040000000004e474c96c496b231ecedcd92566ab3e1cdfac83f7e56abaae6571401e212e59bdbcc18105e0709249510d13d7ae1091558c217033316ac70a36aae764bd0f4032e369453c634b81061079d216d03d3c55976a1aabc00287b307297ae03587ba20839460daae85d26dfcef7638c10a1d8559612e5e9ced1a4205a52ca0ce88e58602e59cf9ab34adf2e958e56570573297b99f733fbbf58d2440526fd4dc15c5509e8a11405f6c08772abcf58731fbf9a73642670e3247c5f70905f0fa81f6174bf32977209923507525a170fcd260e81f04193583fbcd305ac245c80eb337ca326fd1105e73748fab8a1f0c8d8a99f011718e7aae027a34d2a85ff18769fa277810126a86b51b096a04d8e28a4fb8c5e14e50a67cb1ee88e43e5cc077902442f5d9c55ac2b8acdd76c67bc740c6083aba4e3cb404c23f1f3118337563fef6a4b01fb476810c5b5682d0aecdccd55c85a4af50e9150f7d83dcccd8e822a302e6e5a52e00505e6e65338dcfb9cfbe22594e9e18ebde36af29450c5ea31523019cf64fd6eca8c77d98c2a146dcedd51bf6c61c1f7cacbce3ab20c8606930cc42737e17f2703cf0980aef560768d1ac5585c05a60a5f94db15f5ac4d4df5cbd81430878d4e9b77346e3a6538b80b80873e3e6c37860470091979296631440adb8cc71991aba2a4dd2884764878306fe774a25512cfbf080f2829ea2903ffa748dd187c21aef918ee3436a1bd336c3d09cc1f748d7528db90a98f69078b82c4d23de7bcec092a292d2b8cac71a5c87d008f128b89a5e608a4501aef41e9f17e4056ed4767957188f780159daebf327751386980b0fca5a2d36b141acff957f46ce2381897099619475db9d3a613e7ef98b056f42b4d6eafb1d62eebbe46a7502f893fbd36ccfb12a280f45ffb93f050eb280bf0a6cd640abdea8590bffb98bdb29ee3a31daa0fa3ab35fee11dc1b7d1fcea82b0e284b2e35b34e77c3f401ed887e7fc6c97b327f76f99caca2f355afa2753a8923bfb06fb2a9df08d31c93882e34ef5a3cccc9d078855334bdf909ae418b177724c42fb1d586d212c4474932acce295236030f4379158957300fe9fdc5cc9145e3ded50cf9f5a8e19321961536c4a47fffc3eb4383fb78a5d2aeed5b45b92132b5e2a53e3b67841fa2e1bd217ee2c30812c4eb1bd4f8c85b328e23d27f12a2fad5c6b236c87f8fd1aad441416e53ebd4d45d4bf601b94eb37dc9a065218ae58e69dba1250bb20626baeda961b3ef11d217697e73f41fa3870d726a032bc4a388fb12c023822945df058e22f54e5f6377eab34297c57883515204fc189d0d4b0ad9bacb24acf7f9d55e7c6368bb8ababd7622f586ec22683306c5d88d5244219a3952adbd85c89893a441a58b532e15600cd5afdbb5b441e1670b72656c7995189bdf993154e09912db8c4ddaff0e75591720230cf99c8b71cd841dffc4372c5e0f9ff906a379d28d6884351609bf7c849ebfabfb049ae986bbb8467251dbf5ccdd05a86ff6ce1392f7ca1bd49595ad9ad805d71b389afab1865f7f69dc24662af19934f025ced212536509500c132aec000000068b991bed50319a6cb9ff040b92f1563889b3787c37145fc9737d4643f66ade33ebd85a2c29b8c64a581cff01b89d59807d6fade2d2c88872f77d0ed83d97c4b5438681d0b95feb973125e4ee70ebe11699290b831e86571e36513a71f159d48ce563f6814cc2a89851d2520c5275b34cc83614cab14c0d197166580d800ee6b9004b8fd72daac8d73c36c1623c37be93ba49a06c4efde238a3a10a05daba5d4942f7de52648af2be31f33e723b3605346282f5d5e356c5d0004eea40fe0b80abf658f6c96c56319ab53d7fefb5879e0136d1cf320973a2f47c1ee3d21554910f09d17afac4607657c4309890957a4954bf86e2a8491ba37dd88b2c3fe3c7edebd767c400bc23e40d165b27133c726b90bc26cbb2a86a6aa400c47aa7ffc538388de8490b9349afa53b814ffe2a56ff16a496e9648284193754f989f6f12aeb6e":"0000000600000004d96bb26744d99ef624e32161c36d3d6efcdd0484e2b17a6dd183125be4b1af1cda931a91a3acb1151877c174f7943fd9":MBEDTLS_ERR_LMS_VERIFY_FAILED + +LMS import/export test +# This test uses the key from hsslms interop test 1, imports it, exports it and +# tests that it is the same. It also checks if the export correctly fail when +# the buffer is too small. +lms_import_export_test:"000000060000000447cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d":0 + +LMS key import too large key test +# This test uses the valid public key for hsslms interop test 1, add an extra +# byte, and then imports it. This should fail. +lms_import_export_test:"000000060000000447cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d00":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMS key import too small key test +# This test uses the valid public key for hsslms interop test 1, removes a byte, +# and then imports it. This should fail. +lms_import_export_test:"000000060000000447cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMS key import no LMS type test +# This test uses the valid public key for hsslms interop test 1, cuts it down so +# it's smaller than the LMS type offset, and imports it. This should fail, and +# not attempt to read invalidly outside the buffer. +lms_import_export_test:"000000":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMS key import no LMOTS type test +# This test uses the valid public key for hsslms interop test 1, cuts it down so +# it's smaller than the LMOTS type offset, and imports it. This should fail, and +# not attempt to read invalidly outside the buffer. +lms_import_export_test:"00000006000000":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMS key import invalid LMS type test #1 +# This test uses the valid public key for hsslms interop test 1, alters the +# LMS type to 0x5, and imports it. This should fail. +lms_import_export_test:"000000050000000447cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMS key import invalid LMS type test #2 +# This test uses the valid public key for hsslms interop test 1, alters the +# LMS type to 0x7, and imports it. This should fail, and not attempt to read +# invalidly outside the buffer. +lms_import_export_test:"000000070000000447cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMS key import invalid LMOTS type test #1 +# This test uses the valid public key for hsslms interop test 1, alters the +# LMOTS type to 0x3, and imports it. This should fail. +lms_import_export_test:"000000060000000347cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d":MBEDTLS_ERR_LMS_BAD_INPUT_DATA + +LMS key import invalid LMOTS type test #2 +# This test uses the valid public key for hsslms interop test 1, alters the +# LMOTS type to 0x5, and imports it. This should fail, and not attempt to read +# invalidly outside the buffer. +lms_import_export_test:"000000060000000547cc5b29dd0cecd01c382434a6d16864d51b60cdb2a9eed2419015d8524c717ce38a865d7a37da6c84f94621ad595f5d":MBEDTLS_ERR_LMS_BAD_INPUT_DATA diff --git a/tests/suites/test_suite_lms.function b/tests/suites/test_suite_lms.function new file mode 100644 index 000000000..c5c8aa4b9 --- /dev/null +++ b/tests/suites/test_suite_lms.function @@ -0,0 +1,201 @@ +/* BEGIN_HEADER */ +#include "mbedtls/lms.h" + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_LMS_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE depends_on:MBEDTLS_LMS_PRIVATE */ +void lms_sign_verify_test ( data_t *msg, 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)]; + + mbedtls_lms_public_init( &pub_ctx ); + mbedtls_lms_private_init( &priv_ctx ); + + /* Allocation failure isn't a test failure, since it likely just means + * there's not enough memory to run the test. + */ + TEST_EQUAL( 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 ), 0 ); + + TEST_EQUAL( mbedtls_lms_calculate_public_key( &pub_ctx, &priv_ctx ), 0 ); + + TEST_EQUAL( mbedtls_lms_sign( &priv_ctx, mbedtls_test_rnd_std_rand, NULL, + msg->x, msg->len, sig, sizeof( sig ), + NULL ), 0 ); + + TEST_EQUAL( mbedtls_lms_verify( &pub_ctx, msg->x, msg->len, sig, + sizeof( sig ) ), 0 ); + +exit: + mbedtls_lms_public_free( &pub_ctx ); + mbedtls_lms_private_free( &priv_ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_LMS_PRIVATE */ +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)]; + + mbedtls_lms_public_init( &pub_ctx ); + mbedtls_lms_private_init( &priv_ctx ); + + /* Allocation failure isn't a test failure, since it likely just means + * there's not enough memory to run the test. + */ + TEST_EQUAL( 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 ), 0 ); + + TEST_EQUAL( mbedtls_lms_calculate_public_key( &pub_ctx, &priv_ctx ), 0 ); + + TEST_EQUAL( mbedtls_lms_sign( &priv_ctx, mbedtls_test_rnd_std_rand, NULL, + NULL, 0, sig, sizeof( sig ), + NULL ), 0 ); + + TEST_EQUAL( mbedtls_lms_verify( &pub_ctx, NULL, 0, sig, + sizeof( sig ) ), 0 ); + +exit: + mbedtls_lms_public_free( &pub_ctx ); + mbedtls_lms_private_free( &priv_ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void lms_verify_test ( data_t * msg, data_t * sig, data_t * pub_key, + int expected_rc ) +{ + mbedtls_lms_public_t ctx; + unsigned int size; + unsigned char *tmp_sig = NULL; + + mbedtls_lms_public_init( &ctx); + + TEST_EQUAL(mbedtls_lms_import_public_key( &ctx, pub_key->x, pub_key->len ), 0); + + TEST_EQUAL(mbedtls_lms_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), expected_rc); + + /* Test negative cases if the input data is valid */ + if( expected_rc == 0 ) + { + if( msg->len >= 1 ) + { + /* Altering first message byte must cause verification failure */ + msg->x[0] ^= 1; + TEST_EQUAL(mbedtls_lms_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + msg->x[0] ^= 1; + + /* Altering last message byte must cause verification failure */ + msg->x[msg->len - 1] ^= 1; + TEST_EQUAL(mbedtls_lms_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + msg->x[msg->len - 1] ^= 1; + } + + if( sig->len >= 1 ) + { + /* Altering first signature byte must cause verification failure */ + sig->x[0] ^= 1; + TEST_EQUAL(mbedtls_lms_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + sig->x[0] ^= 1; + + /* Altering last signature byte must cause verification failure */ + sig->x[sig->len - 1] ^= 1; + TEST_EQUAL(mbedtls_lms_verify( &ctx, msg->x, msg->len, sig->x, sig->len ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + sig->x[sig->len - 1] ^= 1; + } + + /* Signatures of all sizes must not verify, whether shorter or longer */ + for( size = 0; size < sig->len; size++ ) { + if( size == sig->len ) + continue; + + ASSERT_ALLOC( tmp_sig, size ); + if( tmp_sig != NULL ) + memcpy( tmp_sig, sig->x, MIN(size, sig->len) ); + + TEST_EQUAL(mbedtls_lms_verify( &ctx, msg->x, msg->len, tmp_sig, size ), + MBEDTLS_ERR_LMS_VERIFY_FAILED); + mbedtls_free( tmp_sig ); + tmp_sig = NULL; + } + } + +exit: + mbedtls_free( tmp_sig ); + mbedtls_lms_public_free( &ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void lms_import_export_test ( data_t * pub_key, int expected_import_rc ) +{ + mbedtls_lms_public_t ctx; + size_t exported_pub_key_buf_size = 0; + size_t exported_pub_key_size = 0; + unsigned char *exported_pub_key = NULL; + + mbedtls_lms_public_init(&ctx); + TEST_EQUAL( mbedtls_lms_import_public_key( &ctx, pub_key->x, pub_key->len ), + expected_import_rc ); + + if( expected_import_rc == 0 ) + { + exported_pub_key_buf_size = MBEDTLS_LMS_PUBLIC_KEY_LEN(MBEDTLS_LMS_SHA256_M32_H10); + ASSERT_ALLOC( exported_pub_key, exported_pub_key_buf_size ); + + TEST_EQUAL( mbedtls_lms_export_public_key( &ctx, exported_pub_key, + exported_pub_key_buf_size, + &exported_pub_key_size ), 0 ); + + TEST_EQUAL( exported_pub_key_size, + MBEDTLS_LMS_PUBLIC_KEY_LEN(MBEDTLS_LMS_SHA256_M32_H10 ) ); + ASSERT_COMPARE( pub_key->x, pub_key->len, + exported_pub_key, exported_pub_key_size ); + mbedtls_free(exported_pub_key); + exported_pub_key = NULL; + + /* Export into too-small buffer should fail */ + exported_pub_key_buf_size = MBEDTLS_LMS_PUBLIC_KEY_LEN(MBEDTLS_LMS_SHA256_M32_H10) - 1; + ASSERT_ALLOC( exported_pub_key, exported_pub_key_buf_size); + TEST_EQUAL( mbedtls_lms_export_public_key( &ctx, exported_pub_key, + exported_pub_key_buf_size, NULL ), + MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL ); + mbedtls_free(exported_pub_key); + exported_pub_key = NULL; + + /* Export into too-large buffer should succeed */ + exported_pub_key_buf_size = MBEDTLS_LMS_PUBLIC_KEY_LEN(MBEDTLS_LMS_SHA256_M32_H10) + 1; + ASSERT_ALLOC( exported_pub_key, exported_pub_key_buf_size); + TEST_EQUAL( mbedtls_lms_export_public_key( &ctx, exported_pub_key, + exported_pub_key_buf_size, + &exported_pub_key_size ), + 0 ); + ASSERT_COMPARE( pub_key->x, pub_key->len, + exported_pub_key, exported_pub_key_size ); + mbedtls_free(exported_pub_key); + exported_pub_key = NULL; + } + +exit: + mbedtls_free( exported_pub_key ); + mbedtls_lms_public_free( &ctx ); +} +/* END_CASE */ +