Merge pull request #6419 from mpg/fix-assert-alloc-usage
Fix usage of ASSERT_ALLOC()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 319f02d..66fe5f1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,7 +7,7 @@
Coding Standards
----------------
-- We would ask that contributions conform to [our coding standards](https://tls.mbed.org/kb/development/mbedtls-coding-standards), and that contributions are fully tested before submission, as mentioned in the [Tests](#tests) and [Continuous Integration](#continuous-integration-tests) sections.
+- We would ask that contributions conform to [our coding standards](https://mbed-tls.readthedocs.io/en/latest/kb/development/mbedtls-coding-standards/), and that contributions are fully tested before submission, as mentioned in the [Tests](#tests) and [Continuous Integration](#continuous-integration-tests) sections.
- The code should be written in a clean and readable style.
- The code should be written in a portable generic way, that will benefit the whole community, and not only your own needs.
- The code should be secure, and will be reviewed from a security point of view as well.
@@ -56,7 +56,7 @@
Mbed TLS includes a comprehensive set of test suites in the `tests/` directory that are dynamically generated to produce the actual test source files (e.g. `test_suite_mpi.c`). These files are generated from a `function file` (e.g. `suites/test_suite_mpi.function`) and a `data file` (e.g. `suites/test_suite_mpi.data`). The function file contains the test functions. The data file contains the test cases, specified as parameters that will be passed to the test function.
-[A Knowledge Base article describing how to add additional tests is available on the Mbed TLS website](https://tls.mbed.org/kb/development/test_suites).
+[A Knowledge Base article describing how to add additional tests is available on the Mbed TLS website](https://mbed-tls.readthedocs.io/en/latest/kb/development/test_suites/).
A test script `tests/scripts/basic-build-test.sh` is available to show test coverage of the library. New code contributions should provide a similar level of code coverage to that which already exists for the library.
@@ -75,7 +75,7 @@
1. All interfaces should be documented through Doxygen. New APIs should introduce Doxygen documentation.
1. Complex parts in the code should include comments.
1. If needed, a Readme file is advised.
-1. If a [Knowledge Base (KB)](https://tls.mbed.org/kb) article should be added, write this as a comment in the PR description.
+1. If a [Knowledge Base (KB)](https://mbed-tls.readthedocs.io/en/latest/kb/) article should be added, write this as a comment in the PR description.
1. A [ChangeLog](https://github.com/Mbed-TLS/mbedtls/blob/development/ChangeLog.d/00README.md) entry should be added for this contribution.
License and Copyright
diff --git a/ChangeLog b/ChangeLog
index 6dfb23f..80b8617 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -463,7 +463,7 @@
provides better randomness. Instead of HAVEGE, declare OS or hardware RNG
interfaces with mbedtls_entropy_add_source() and/or use an entropy seed
file created securely during device provisioning. See
- https://tls.mbed.org/kb/how-to/add-entropy-sources-to-entropy-pool for
+ https://mbed-tls.readthedocs.io/en/latest/kb/how-to/add-entropy-sources-to-entropy-pool/ for
more information.
* Add missing const attributes to API functions.
* Remove helpers for the transition from Mbed TLS 1.3 to Mbed TLS 2.0: the
diff --git a/ChangeLog.d/LMS.txt b/ChangeLog.d/LMS.txt
new file mode 100644
index 0000000..6de374f
--- /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/ChangeLog.d/fix_aead_psa_driver_build.txt b/ChangeLog.d/fix_aead_psa_driver_build.txt
new file mode 100644
index 0000000..a6d11d3
--- /dev/null
+++ b/ChangeLog.d/fix_aead_psa_driver_build.txt
@@ -0,0 +1,3 @@
+Bugfix
+ * Fix compilation errors when trying to build with
+ PSA drivers for AEAD (GCM, CCM, Chacha20-Poly1305).
diff --git a/README.md b/README.md
index 8978faf..9c9cf91 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,9 @@
Documentation
-------------
-Documentation for the Mbed TLS interfaces in the default library configuration is available as part of the [Mbed TLS documentation](https://tls.mbed.org/api/).
+The main Mbed TLS documentation is available via [ReadTheDocs](https://mbed-tls.readthedocs.io/).
+
+Documentation for the PSA Cryptography API is available [on GitHub](https://armmbed.github.io/mbed-crypto/psa/#application-programming-interface).
To generate a local copy of the library documentation in HTML format, tailored to your compile-time configuration:
@@ -103,9 +105,9 @@
Please note that setting `CFLAGS` overrides its default value of `-O2` and setting `WARNING_CFLAGS` overrides its default value (starting with `-Wall -Wextra`), so if you just want to add some warning options to the default ones, you can do so by setting `CFLAGS=-O2 -Werror` for example. Setting `WARNING_CFLAGS` is useful when you want to get rid of its default content (for example because your compiler doesn't accept `-Wall` as an option). Directory-specific options cannot be overridden from the command line.
-Depending on your platform, you might run into some issues. Please check the Makefiles in `library/`, `programs/` and `tests/` for options to manually add or remove for specific platforms. You can also check [the Mbed TLS Knowledge Base](https://tls.mbed.org/kb) for articles on your platform or issue.
+Depending on your platform, you might run into some issues. Please check the Makefiles in `library/`, `programs/` and `tests/` for options to manually add or remove for specific platforms. You can also check [the Mbed TLS Knowledge Base](https://mbed-tls.readthedocs.io/en/latest/kb/) for articles on your platform or issue.
-In case you find that you need to do something else as well, please let us know what, so we can add it to the [Mbed TLS Knowledge Base](https://tls.mbed.org/kb).
+In case you find that you need to do something else as well, please let us know what, so we can add it to the [Mbed TLS Knowledge Base](https://mbed-tls.readthedocs.io/en/latest/kb/).
### CMake
@@ -248,9 +250,9 @@
Mbed TLS can be ported to many different architectures, OS's and platforms. Before starting a port, you may find the following Knowledge Base articles useful:
-- [Porting Mbed TLS to a new environment or OS](https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS)
-- [What external dependencies does Mbed TLS rely on?](https://tls.mbed.org/kb/development/what-external-dependencies-does-mbedtls-rely-on)
-- [How do I configure Mbed TLS](https://tls.mbed.org/kb/compiling-and-building/how-do-i-configure-mbedtls)
+- [Porting Mbed TLS to a new environment or OS](https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS/)
+- [What external dependencies does Mbed TLS rely on?](https://mbed-tls.readthedocs.io/en/latest/kb/development/what-external-dependencies-does-mbedtls-rely-on/)
+- [How do I configure Mbed TLS](https://mbed-tls.readthedocs.io/en/latest/kb/compiling-and-building/how-do-i-configure-mbedtls/)
Mbed TLS is mostly written in portable C99; however, it has a few platform requirements that go beyond the standard, but are met by most modern architectures:
diff --git a/SUPPORT.md b/SUPPORT.md
index dab7ac5..b550e08 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -2,10 +2,11 @@
Here are some useful sources of information about using Mbed TLS:
+- [ReadTheDocs](https://mbed-tls.readthedocs.io/);
- API documentation, see the [Documentation section of the
- README](README.md#License);
+ README](README.md#documentation);
- the `docs` directory in the source tree;
-- the [Mbed TLS knowledge Base](https://tls.mbed.org/kb);
+- the [Mbed TLS Knowledge Base](https://mbed-tls.readthedocs.io/en/latest/kb/);
- the [Mbed TLS mailing-list
archives](https://lists.trustedfirmware.org/archives/list/mbed-tls@lists.trustedfirmware.org/).
diff --git a/docs/3.0-migration-guide.md b/docs/3.0-migration-guide.md
index 884810d..63a13ad 100644
--- a/docs/3.0-migration-guide.md
+++ b/docs/3.0-migration-guide.md
@@ -141,7 +141,7 @@
only source of entropy. If you're in that case, please declare OS or hardware
RNG interfaces with `mbedtls_entropy_add_source()` and/or use an entropy seed
file created securely during device provisioning. See
-<https://tls.mbed.org/kb/how-to/add-entropy-sources-to-entropy-pool> for more
+<https://mbed-tls.readthedocs.io/en/latest/kb/how-to/add-entropy-sources-to-entropy-pool> for more
information.
### Remove helpers for the transition from Mbed TLS 1.3 to Mbed TLS 2.0
diff --git a/docs/architecture/alternative-implementations.md b/docs/architecture/alternative-implementations.md
index 7fe6332..eacdea7 100644
--- a/docs/architecture/alternative-implementations.md
+++ b/docs/architecture/alternative-implementations.md
@@ -38,7 +38,7 @@
* Create a header file `xxx_alt.h` that defines the context type(s) used by the module. For example, `mbedtls_aes_context` for AES.
* Implement all the functions from the module, i.e. the functions declared in `include/mbedtls/xxx.h`.
-See https://tls.mbed.org/kb/development/hw_acc_guidelines for a more detailed guide.
+See https://mbed-tls.readthedocs.io/en/latest/kb/development/hw_acc_guidelines for a more detailed guide.
### Constraints on context types
diff --git a/docs/architecture/testing/test-framework.md b/docs/architecture/testing/test-framework.md
index d0d4776..7780949 100644
--- a/docs/architecture/testing/test-framework.md
+++ b/docs/architecture/testing/test-framework.md
@@ -6,7 +6,7 @@
## Unit tests
-See <https://tls.mbed.org/kb/development/test_suites>
+See <https://mbed-tls.readthedocs.io/en/latest/kb/development/test_suites>
### Unit test descriptions
diff --git a/docs/architecture/tls13-support.md b/docs/architecture/tls13-support.md
index 10da3c5..fc0087f 100644
--- a/docs/architecture/tls13-support.md
+++ b/docs/architecture/tls13-support.md
@@ -181,7 +181,7 @@
The following coding rules are aimed to be a checklist for TLS 1.3 upstreaming
work to reduce review rounds and the number of comments in each round. They
come along (do NOT replace) the project coding rules
-(https://tls.mbed.org/kb/development/mbedtls-coding-standards). They have been
+(https://mbed-tls.readthedocs.io/en/latest/kb/development/mbedtls-coding-standards). They have been
established and discussed following the review of #4882 that was the
PR upstreaming the first part of TLS 1.3 ClientHello writing code.
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index 6b330a7..0081ca3 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 8b2b9ea..eb83913 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 0000000..5e03d9b
--- /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 <stdint.h>
+#include <stddef.h>
+
+#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 23e601b..61007d8 100644
--- a/include/mbedtls/mbedtls_config.h
+++ b/include/mbedtls/mbedtls_config.h
@@ -2462,6 +2462,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
*
* Enable the Key Wrapping mode for 128-bit block ciphers,
@@ -2554,7 +2580,7 @@
*
* \note See also our Knowledge Base article about porting to a new
* environment:
- * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
+ * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
*
* Module: library/net_sockets.c
*
@@ -3111,7 +3137,7 @@
* contexts are not shared between threads. If you do intend to use contexts
* between threads, you will need to enable this layer to prevent race
* conditions. See also our Knowledge Base article about threading:
- * https://tls.mbed.org/kb/development/thread-safety-and-multi-threading
+ * https://mbed-tls.readthedocs.io/en/latest/kb/development/thread-safety-and-multi-threading
*
* Module: library/threading.c
*
@@ -3143,7 +3169,7 @@
*
* \note See also our Knowledge Base article about porting to a new
* environment:
- * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
+ * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
*
* Module: library/timing.c
*/
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 09b1dfc..219d820 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -2328,7 +2328,7 @@
* here, except if using an event-driven style.
*
* \note See also the "DTLS tutorial" article in our knowledge base.
- * https://tls.mbed.org/kb/how-to/dtls-tutorial
+ * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/dtls-tutorial
*/
void mbedtls_ssl_set_timer_cb( mbedtls_ssl_context *ssl,
void *p_timer,
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 9513814..7c325f7 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -44,6 +44,8 @@
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 9c3af3b..624773d 100644
--- a/library/Makefile
+++ b/library/Makefile
@@ -109,6 +109,8 @@
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 0000000..788063c
--- /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 <string.h>
+
+#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 0000000..39e8699
--- /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 <stdint.h>
+#include <stddef.h>
+
+
+#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 0000000..46ea567
--- /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 <string.h>
+
+#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/library/psa_crypto.c b/library/psa_crypto.c
index 7e2b686..2ce5e43 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -3705,39 +3705,34 @@
return( status );
}
-static psa_status_t psa_validate_tag_length( psa_aead_operation_t *operation,
- psa_algorithm_t alg ) {
- uint8_t tag_len = 0;
- if( psa_driver_get_tag_len( operation, &tag_len ) != PSA_SUCCESS )
- {
- return( PSA_ERROR_INVALID_ARGUMENT );
- }
+static psa_status_t psa_validate_tag_length( psa_algorithm_t alg ) {
+ const uint8_t tag_len = PSA_ALG_AEAD_GET_TAG_LENGTH( alg );
switch( PSA_ALG_AEAD_WITH_SHORTENED_TAG( alg, 0 ) )
{
-#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+#if defined(PSA_WANT_ALG_CCM)
case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_CCM, 0 ):
/* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.*/
if( tag_len < 4 || tag_len > 16 || tag_len % 2 )
return( PSA_ERROR_INVALID_ARGUMENT );
break;
-#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#endif /* PSA_WANT_ALG_CCM */
-#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+#if defined(PSA_WANT_ALG_GCM)
case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_GCM, 0 ):
/* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16. */
if( tag_len != 4 && tag_len != 8 && ( tag_len < 12 || tag_len > 16 ) )
return( PSA_ERROR_INVALID_ARGUMENT );
break;
-#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#endif /* PSA_WANT_ALG_GCM */
-#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_CHACHA20_POLY1305, 0 ):
/* We only support the default tag length. */
if( tag_len != 16 )
return( PSA_ERROR_INVALID_ARGUMENT );
break;
-#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
default:
(void) tag_len;
@@ -3788,6 +3783,9 @@
.core = slot->attr
};
+ if( ( status = psa_validate_tag_length( alg ) ) != PSA_SUCCESS )
+ goto exit;
+
if( is_encrypt )
status = psa_driver_wrapper_aead_encrypt_setup( operation,
&attributes,
@@ -3803,9 +3801,6 @@
if( status != PSA_SUCCESS )
goto exit;
- if( ( status = psa_validate_tag_length( operation, alg ) ) != PSA_SUCCESS )
- goto exit;
-
operation->key_type = psa_get_key_type( &attributes );
exit:
diff --git a/library/psa_crypto_aead.c b/library/psa_crypto_aead.c
index 76d95bc..48969b3 100644
--- a/library/psa_crypto_aead.c
+++ b/library/psa_crypto_aead.c
@@ -45,7 +45,6 @@
size_t key_bits;
const mbedtls_cipher_info_t *cipher_info;
mbedtls_cipher_id_t cipher_id;
- size_t full_tag_length = 0;
( void ) key_buffer_size;
@@ -62,7 +61,6 @@
#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_CCM, 0 ):
operation->alg = PSA_ALG_CCM;
- full_tag_length = 16;
/* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.
* The call to mbedtls_ccm_encrypt_and_tag or
* mbedtls_ccm_auth_decrypt will validate the tag length. */
@@ -81,7 +79,6 @@
#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_GCM, 0 ):
operation->alg = PSA_ALG_GCM;
- full_tag_length = 16;
/* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16.
* The call to mbedtls_gcm_crypt_and_tag or
* mbedtls_gcm_auth_decrypt will validate the tag length. */
@@ -100,7 +97,6 @@
#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
case PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_CHACHA20_POLY1305, 0 ):
operation->alg = PSA_ALG_CHACHA20_POLY1305;
- full_tag_length = 16;
/* We only support the default tag length. */
if( alg != PSA_ALG_CHACHA20_POLY1305 )
return( PSA_ERROR_NOT_SUPPORTED );
@@ -120,16 +116,9 @@
return( PSA_ERROR_NOT_SUPPORTED );
}
- if( PSA_AEAD_TAG_LENGTH( attributes->core.type,
- key_bits, alg )
- > full_tag_length )
- return( PSA_ERROR_INVALID_ARGUMENT );
-
operation->key_type = psa_get_key_type( attributes );
- operation->tag_length = PSA_AEAD_TAG_LENGTH( operation->key_type,
- key_bits,
- alg );
+ operation->tag_length = PSA_ALG_AEAD_GET_TAG_LENGTH( alg );
return( PSA_SUCCESS );
}
diff --git a/library/psa_crypto_driver_wrappers.h b/library/psa_crypto_driver_wrappers.h
index 12c649d..ee23b6f 100644
--- a/library/psa_crypto_driver_wrappers.h
+++ b/library/psa_crypto_driver_wrappers.h
@@ -226,10 +226,6 @@
const uint8_t *ciphertext, size_t ciphertext_length,
uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length );
-psa_status_t psa_driver_get_tag_len(
- psa_aead_operation_t *operation,
- uint8_t *tag_len );
-
psa_status_t psa_driver_wrapper_aead_encrypt_setup(
psa_aead_operation_t *operation,
const psa_key_attributes_t *attributes,
diff --git a/scripts/data_files/driver_templates/psa_crypto_driver_wrappers.c.jinja b/scripts/data_files/driver_templates/psa_crypto_driver_wrappers.c.jinja
index a5ae6a2..8ef2e6d 100644
--- a/scripts/data_files/driver_templates/psa_crypto_driver_wrappers.c.jinja
+++ b/scripts/data_files/driver_templates/psa_crypto_driver_wrappers.c.jinja
@@ -1616,22 +1616,6 @@
}
}
-psa_status_t psa_driver_get_tag_len( psa_aead_operation_t *operation,
- uint8_t *tag_len )
-{
- if( operation == NULL || tag_len == NULL )
- return( PSA_ERROR_INVALID_ARGUMENT );
-
-#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT)
-#if defined(PSA_CRYPTO_DRIVER_TEST)
- *tag_len = operation->ctx.transparent_test_driver_ctx.tag_length;
- return ( PSA_SUCCESS );
-#endif
-#endif
- *tag_len = operation->ctx.mbedtls_ctx.tag_length;
- return ( PSA_SUCCESS );
-}
-
psa_status_t psa_driver_wrapper_aead_encrypt_setup(
psa_aead_operation_t *operation,
const psa_key_attributes_t *attributes,
diff --git a/scripts/generate_errors.pl b/scripts/generate_errors.pl
index 0a03f02..41b0337 100755
--- a/scripts/generate_errors.pl
+++ b/scripts/generate_errors.pl
@@ -47,7 +47,7 @@
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 0000000..967f8f5
--- /dev/null
+++ b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_aux
Binary files 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 0000000..ab1b23f
--- /dev/null
+++ b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_prv
Binary files 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 0000000..5397d60
--- /dev/null
+++ b/tests/data_files/lms_hash-sigs_sha256_m32_h5_lmots_sha256_n32_w8_pub
Binary files 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 0000000..db85e01
--- /dev/null
+++ b/tests/data_files/lms_hsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv
Binary files 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 0000000..6e827ce
--- /dev/null
+++ b/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_prv
Binary files 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 0000000..652c089
--- /dev/null
+++ b/tests/data_files/lms_pyhsslms_sha256_m32_h5_lmots_sha256_n32_w8_pub
Binary files differ
diff --git a/tests/include/test/drivers/crypto_config_test_driver_extension.h b/tests/include/test/drivers/crypto_config_test_driver_extension.h
index 8052a85..0bbca4a 100644
--- a/tests/include/test/drivers/crypto_config_test_driver_extension.h
+++ b/tests/include/test/drivers/crypto_config_test_driver_extension.h
@@ -142,6 +142,14 @@
#endif
#endif
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+#if defined(MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305)
+#undef MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305
+#else
+#define MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305 1
+#endif
+#endif
+
#if defined(PSA_WANT_KEY_TYPE_AES)
#if defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_AES)
#undef MBEDTLS_PSA_ACCEL_KEY_TYPE_AES
@@ -182,9 +190,16 @@
#endif
#endif
+#if defined(PSA_WANT_KEY_TYPE_CHACHA20)
+#if defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20)
+#undef MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20
+#else
+#define MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20 1
+#endif
+#endif
+
#define MBEDTLS_PSA_ACCEL_ALG_CBC_MAC 1
#define MBEDTLS_PSA_ACCEL_ALG_CCM 1
-#define MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305 1
#define MBEDTLS_PSA_ACCEL_ALG_CMAC 1
#define MBEDTLS_PSA_ACCEL_ALG_ECB_NO_PADDING 1
#define MBEDTLS_PSA_ACCEL_ALG_ECDH 1
@@ -217,7 +232,6 @@
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_DERIVE 1
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_HMAC 1
-#define MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20 1
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_DES 1
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_PUBLIC_KEY 1
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_RAW_DATA 1
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 1d2ee40..f1b2f0e 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -987,6 +987,8 @@
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 @@
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 @@
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 @@
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
@@ -2098,6 +2106,41 @@
make test
}
+component_test_psa_crypto_config_accel_aead () {
+ msg "test: MBEDTLS_PSA_CRYPTO_CONFIG with accelerated AEAD"
+
+ # Disable ALG_STREAM_CIPHER and ALG_ECB_NO_PADDING to avoid having
+ # partial support for cipher operations in the driver test library.
+ scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_STREAM_CIPHER
+ scripts/config.py -f include/psa/crypto_config.h unset PSA_WANT_ALG_ECB_NO_PADDING
+
+ loc_accel_list="ALG_GCM ALG_CCM ALG_CHACHA20_POLY1305 KEY_TYPE_AES KEY_TYPE_CHACHA20 KEY_TYPE_ARIA KEY_TYPE_CAMELLIA"
+ loc_accel_flags=$( echo "$loc_accel_list" | sed 's/[^ ]* */-DLIBTESTDRIVER1_MBEDTLS_PSA_ACCEL_&/g' )
+ make -C tests libtestdriver1.a CFLAGS="$ASAN_CFLAGS $loc_accel_flags" LDFLAGS="$ASAN_CFLAGS"
+
+ scripts/config.py set MBEDTLS_PSA_CRYPTO_DRIVERS
+ scripts/config.py set MBEDTLS_PSA_CRYPTO_CONFIG
+
+ scripts/config.py unset MBEDTLS_GCM_C
+ scripts/config.py unset MBEDTLS_CCM_C
+ scripts/config.py unset MBEDTLS_CHACHAPOLY_C
+ # Features that depend on AEAD
+ scripts/config.py unset MBEDTLS_SSL_CONTEXT_SERIALIZATION
+ scripts/config.py unset MBEDTLS_SSL_TICKET_C
+
+ loc_accel_flags="$loc_accel_flags $( echo "$loc_accel_list" | sed 's/[^ ]* */-DMBEDTLS_PSA_ACCEL_&/g' )"
+ make CFLAGS="$ASAN_CFLAGS -Werror -I../tests/include -I../tests -I../../tests -DPSA_CRYPTO_DRIVER_TEST -DMBEDTLS_TEST_LIBTESTDRIVER1 $loc_accel_flags" LDFLAGS="-ltestdriver1 $ASAN_CFLAGS"
+
+ # There's a risk of something getting re-enabled via config_psa.h
+ # make sure it did not happen.
+ not grep mbedtls_ccm library/ccm.o
+ not grep mbedtls_gcm library/gcm.o
+ not grep mbedtls_chachapoly library/chachapoly.o
+
+ msg "test: MBEDTLS_PSA_CRYPTO_CONFIG with accelerated AEAD"
+ make test
+}
+
component_test_psa_crypto_config_no_driver() {
# full plus MBEDTLS_PSA_CRYPTO_CONFIG
msg "build: full + MBEDTLS_PSA_CRYPTO_CONFIG minus MBEDTLS_PSA_CRYPTO_DRIVERS"
@@ -2240,6 +2283,8 @@
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"
}
@@ -2260,6 +2305,8 @@
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"
}
@@ -2280,6 +2327,8 @@
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"
}
@@ -2337,6 +2386,8 @@
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"
}
@@ -2357,6 +2408,8 @@
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 68297a6..db18a92 100755
--- a/tests/scripts/depends-hashes.pl
+++ b/tests/scripts/depends-hashes.pl
@@ -57,7 +57,7 @@
['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/src/drivers/test_driver_aead.c b/tests/src/drivers/test_driver_aead.c
index b561960..93a75f6 100644
--- a/tests/src/drivers/test_driver_aead.c
+++ b/tests/src/drivers/test_driver_aead.c
@@ -25,6 +25,10 @@
#include "test/drivers/aead.h"
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1)
+#include "libtestdriver1/library/psa_crypto_aead.h"
+#endif
+
mbedtls_test_driver_aead_hooks_t
mbedtls_test_driver_aead_hooks = MBEDTLS_TEST_DRIVER_AEAD_INIT;
@@ -46,7 +50,18 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_encrypt(
+ (const libtestdriver1_psa_key_attributes_t *)attributes,
+ key_buffer, key_buffer_size,
+ alg,
+ nonce, nonce_length,
+ additional_data, additional_data_length,
+ plaintext, plaintext_length,
+ ciphertext, ciphertext_size, ciphertext_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_encrypt(
attributes, key_buffer, key_buffer_size,
@@ -94,7 +109,18 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_decrypt(
+ (const libtestdriver1_psa_key_attributes_t *)attributes,
+ key_buffer, key_buffer_size,
+ alg,
+ nonce, nonce_length,
+ additional_data, additional_data_length,
+ ciphertext, ciphertext_length,
+ plaintext, plaintext_size, plaintext_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_decrypt(
attributes, key_buffer, key_buffer_size,
@@ -139,7 +165,14 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_encrypt_setup( operation,
+ (const libtestdriver1_psa_key_attributes_t *)attributes,
+ key_buffer,
+ key_buffer_size, alg );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_encrypt_setup( operation, attributes, key_buffer,
key_buffer_size, alg );
@@ -171,7 +204,13 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_decrypt_setup( operation,
+ (const libtestdriver1_psa_key_attributes_t *)attributes,
+ key_buffer, key_buffer_size, alg );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_decrypt_setup( operation, attributes, key_buffer,
key_buffer_size, alg );
@@ -202,7 +241,11 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_set_nonce( operation, nonce, nonce_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_set_nonce( operation, nonce, nonce_length );
#else
@@ -230,7 +273,12 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_set_lengths( operation, ad_length,
+ plaintext_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_set_lengths( operation, ad_length,
plaintext_length );
@@ -259,7 +307,11 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_update_ad( operation, input, input_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_update_ad( operation, input, input_length );
#else
@@ -290,7 +342,13 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_update( operation, input,
+ input_length, output,
+ output_size, output_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_update( operation, input, input_length, output,
output_size, output_length );
@@ -326,7 +384,13 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_finish( operation, ciphertext,
+ ciphertext_size, ciphertext_length,
+ tag, tag_size, tag_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_finish( operation, ciphertext, ciphertext_size,
ciphertext_length, tag, tag_size,
@@ -364,9 +428,19 @@
else
{
uint8_t check_tag[PSA_AEAD_TAG_MAX_SIZE];
- size_t check_tag_length;
+ size_t check_tag_length = 0;
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_finish( operation,
+ plaintext,
+ plaintext_size,
+ plaintext_length,
+ check_tag,
+ sizeof( check_tag ),
+ &check_tag_length );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_finish( operation,
plaintext,
@@ -410,7 +484,11 @@
}
else
{
-#if defined(MBEDTLS_PSA_BUILTIN_AEAD)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+ defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD)
+ mbedtls_test_driver_aead_hooks.driver_status =
+ libtestdriver1_mbedtls_psa_aead_abort( operation );
+#elif defined(MBEDTLS_PSA_BUILTIN_AEAD)
mbedtls_test_driver_aead_hooks.driver_status =
mbedtls_psa_aead_abort( operation );
#else
diff --git a/tests/suites/test_suite_lmots.data b/tests/suites/test_suite_lmots.data
new file mode 100644
index 0000000..2737272
--- /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 0000000..53ab02f
--- /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 0000000..7802a70
--- /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:
+# * <Save signature in binary format>
+# * 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):
+# * <download and build hash-sigs>
+# * 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
+# * <Save message in binary format>
+# * <hash-sigs>/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
+# * <hash-sigs/demo> 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):
+# * <download and build hash-sigs>
+# * 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
+# * <Save message in binary format>
+# * <hash-sigs>/demo sign tmp/lms message.bin (incorrect signature using leaf node 0)
+# * rm message.bin.sig
+# * <hash-sigs>/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
+# * <hash-sigs/demo> 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 0000000..c5c8aa4
--- /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 */
+
diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data
index f2478be..cded3a8 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -3643,15 +3643,15 @@
PSA Multipart AEAD verify: ChaCha20 - Poly1305, invalid tag length 0
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
-aead_multipart_verify:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,0):"070000004041424344454647":"50515253c0c1c2c3c4c5c6c7":"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116":"1ae10b594f09e26a7e902ecbd0600690":1:PSA_ERROR_NOT_SUPPORTED:PSA_ERROR_INVALID_ARGUMENT
+aead_multipart_verify:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,0):"070000004041424344454647":"50515253c0c1c2c3c4c5c6c7":"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116":"1ae10b594f09e26a7e902ecbd0600690":1:PSA_ERROR_INVALID_ARGUMENT:PSA_ERROR_INVALID_ARGUMENT
PSA Multipart AEAD verify: ChaCha20 - Poly1305, invalid tag length 15
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
-aead_multipart_verify:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,15):"070000004041424344454647":"50515253c0c1c2c3c4c5c6c7":"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116":"1ae10b594f09e26a7e902ecbd0600690":1:PSA_ERROR_NOT_SUPPORTED:PSA_ERROR_INVALID_ARGUMENT
+aead_multipart_verify:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,15):"070000004041424344454647":"50515253c0c1c2c3c4c5c6c7":"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116":"1ae10b594f09e26a7e902ecbd0600690":1:PSA_ERROR_INVALID_ARGUMENT:PSA_ERROR_INVALID_ARGUMENT
PSA Multipart AEAD verify: ChaCha20 - Poly1305, invalid tag length 17
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
-aead_multipart_verify:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,17):"070000004041424344454647":"50515253c0c1c2c3c4c5c6c7":"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116":"1ae10b594f09e26a7e902ecbd0600690":1:PSA_ERROR_NOT_SUPPORTED:PSA_ERROR_INVALID_ARGUMENT
+aead_multipart_verify:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,17):"070000004041424344454647":"50515253c0c1c2c3c4c5c6c7":"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116":"1ae10b594f09e26a7e902ecbd0600690":1:PSA_ERROR_INVALID_ARGUMENT:PSA_ERROR_INVALID_ARGUMENT
PSA Multipart AEAD verify: ChaCha20 - Poly1305 (RFC7539, bad tag)
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
@@ -3951,7 +3951,7 @@
PSA AEAD setup: invalid algorithm (ChaCha20 - Poly1305 with short tag)
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
-aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,12):PSA_ERROR_NOT_SUPPORTED
+aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,12):PSA_ERROR_INVALID_ARGUMENT
PSA AEAD setup: AES - CCM, invalid tag length 0
depends_on:PSA_WANT_ALG_CCM:PSA_WANT_KEY_TYPE_AES
@@ -4031,15 +4031,15 @@
PSA AEAD setup: ChaCha20-Poly1305, invalid tag length 0
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
-aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,0):PSA_ERROR_NOT_SUPPORTED
+aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,0):PSA_ERROR_INVALID_ARGUMENT
PSA AEAD setup: ChaCha20-Poly1305, invalid tag length 15
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
-aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,15):PSA_ERROR_NOT_SUPPORTED
+aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,15):PSA_ERROR_INVALID_ARGUMENT
PSA AEAD setup: ChaCha20-Poly1305, invalid tag length 17
depends_on:PSA_WANT_ALG_CHACHA20_POLY1305:PSA_WANT_KEY_TYPE_CHACHA20
-aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,17):PSA_ERROR_NOT_SUPPORTED
+aead_multipart_setup:PSA_KEY_TYPE_CHACHA20:"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f":PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305,17):PSA_ERROR_INVALID_ARGUMENT
PSA Multipart State Checks, AES - GCM
depends_on:PSA_WANT_ALG_GCM:PSA_WANT_KEY_TYPE_AES