Update LMS and LMOTS api
Fix function names and parameters. Move macros to be more private. Update implementation. Signed-off-by: Raef Coles <raef.coles@arm.com>
This commit is contained in:
parent
c8f9604d7b
commit
01c71a17b3
@ -32,33 +32,24 @@
|
|||||||
|
|
||||||
#include "lmots.h"
|
#include "lmots.h"
|
||||||
|
|
||||||
#include "mbedtls/private_access.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_BAD_INPUT_DATA -0x0011 /**< Bad data has been input to an LMS function */
|
||||||
#define MBEDTLS_ERR_LMS_OUT_OF_PRIV_KEYS -0x0013 /**< Specified LMS key has utilised all of its private keys */
|
#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_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_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 */
|
#define MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL -0x0019 /**< Input/output buffer is too small to contain requited data */
|
||||||
|
|
||||||
#define MBEDTLS_LMS_TYPE_LEN (4)
|
|
||||||
#define MBEDTLS_LMS_H_TREE_HEIGHT (10)
|
|
||||||
#define MBEDTLS_LMS_M_NODE_BYTES (32) /* The length of a hash output, 32 for SHA256 */
|
#define MBEDTLS_LMS_M_NODE_BYTES (32) /* The length of a hash output, 32 for SHA256 */
|
||||||
|
#define MBEDTLS_LMS_TYPE_LEN (4)
|
||||||
|
#define MBEDTLS_LMS_H_TREE_HEIGHT (10u)
|
||||||
|
|
||||||
#define MBEDTLS_LMS_SIG_LEN (MBEDTLS_LMOTS_Q_LEAF_ID_LEN + MBEDTLS_LMOTS_SIG_LEN + \
|
#define MBEDTLS_LMS_SIG_LEN (MBEDTLS_LMOTS_Q_LEAF_ID_LEN + MBEDTLS_LMOTS_SIG_LEN + \
|
||||||
MBEDTLS_LMS_TYPE_LEN + MBEDTLS_LMS_H_TREE_HEIGHT * MBEDTLS_LMS_M_NODE_BYTES)
|
MBEDTLS_LMS_TYPE_LEN + MBEDTLS_LMS_H_TREE_HEIGHT * MBEDTLS_LMS_M_NODE_BYTES)
|
||||||
|
|
||||||
#define MBEDTLS_LMS_PUBKEY_LEN (MBEDTLS_LMS_TYPE_LEN + MBEDTLS_LMOTS_TYPE_LEN + \
|
#define MBEDTLS_LMS_PUBLIC_KEY_LEN (MBEDTLS_LMS_TYPE_LEN + MBEDTLS_LMOTS_TYPE_LEN + \
|
||||||
MBEDTLS_LMOTS_I_KEY_ID_LEN + MBEDTLS_LMS_M_NODE_BYTES)
|
MBEDTLS_LMOTS_I_KEY_ID_LEN + MBEDTLS_LMS_M_NODE_BYTES)
|
||||||
|
|
||||||
#define MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET (0)
|
|
||||||
#define MBEDTLS_LMS_SIG_OTS_SIG_OFFSET (MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET + MBEDTLS_LMOTS_Q_LEAF_ID_LEN)
|
|
||||||
#define MBEDTLS_LMS_SIG_TYPE_OFFSET (MBEDTLS_LMS_SIG_OTS_SIG_OFFSET + MBEDTLS_LMOTS_SIG_LEN)
|
|
||||||
#define MBEDTLS_LMS_SIG_PATH_OFFSET (MBEDTLS_LMS_SIG_TYPE_OFFSET + MBEDTLS_LMS_TYPE_LEN)
|
|
||||||
|
|
||||||
#define MBEDTLS_LMS_PUBKEY_TYPE_OFFSET (0)
|
|
||||||
#define MBEDTLS_LMS_PUBKEY_OTSTYPE_OFFSET (MBEDTLS_LMS_PUBKEY_TYPE_OFFSET + MBEDTLS_LMS_TYPE_LEN)
|
|
||||||
#define MBEDTLS_LMS_PUBKEY_I_KEY_ID_OFFSET (MBEDTLS_LMS_PUBKEY_OTSTYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN)
|
|
||||||
#define MBEDTLS_LMS_PUBKEY_ROOT_NODE_OFFSET (MBEDTLS_LMS_PUBKEY_I_KEY_ID_OFFSET + MBEDTLS_LMOTS_I_KEY_ID_LEN)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -72,85 +63,234 @@ typedef enum {
|
|||||||
} mbedtls_lms_algorithm_type_t;
|
} mbedtls_lms_algorithm_type_t;
|
||||||
|
|
||||||
|
|
||||||
/** LMS context structure.
|
/** 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
|
* The context must be initialized before it is used. A public key must either
|
||||||
* be imported, or an algorithm type set, a private key generated and the public
|
* be imported or generated from a private context.
|
||||||
* key calculated from it. A context that does not contain a public key cannot
|
|
||||||
* verify, and a context that does not contain a private key cannot sign.
|
|
||||||
*
|
*
|
||||||
* \dot
|
* \dot
|
||||||
* digraph lmots {
|
* digraph lms_public_t {
|
||||||
* UNINITIALIZED -> INIT [label="init"];
|
* UNINITIALIZED -> INIT [label="init"];
|
||||||
* TYPE_SET -> INIT [label="free"];
|
* HAVE_PUBLIC_KEY -> INIT [label="free"];
|
||||||
* PRIVATE -> INIT [label="free"];
|
* INIT -> HAVE_PUBLIC_KEY [label="import_public_key"];
|
||||||
* PUBLIC -> INIT [label="free"];
|
* INIT -> HAVE_PUBLIC_KEY [label="calculate_public_key from private key"];
|
||||||
* "PRIVATE+PUBLIC" -> INIT [label="free"];
|
* HAVE_PUBLIC_KEY -> HAVE_PUBLIC_KEY [label="export_public_key"];
|
||||||
* INIT -> TYPE_SET [label="set_algorithm_type"];
|
|
||||||
* INIT -> PUBLIC [label="import_public"];
|
|
||||||
* PUBLIC -> PUBLIC [label="export_pubkey"];
|
|
||||||
* "PRIVATE+PUBLIC" -> "PRIVATE+PUBLIC" [label="export_pubkey"];
|
|
||||||
* PRIVATE -> "PRIVATE+PUBLIC" [label="gen_pubkey"];
|
|
||||||
* TYPE_SET -> PRIVATE [label="gen_privkey"];
|
|
||||||
* }
|
* }
|
||||||
* \enddot
|
* \enddot
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned char MBEDTLS_PRIVATE(have_privkey); /*!< Whether the context contains a private key.
|
mbedtls_lms_parameters_t MBEDTLS_PRIVATE(params);
|
||||||
Boolean values only. */
|
|
||||||
unsigned char MBEDTLS_PRIVATE(have_pubkey); /*!< Whether the context contains a public key.
|
|
||||||
Boolean values only. */
|
|
||||||
unsigned char MBEDTLS_PRIVATE(I_key_identifier)[MBEDTLS_LMOTS_I_KEY_ID_LEN]; /*!< The key
|
|
||||||
identifier. */
|
|
||||||
mbedtls_lms_algorithm_type_t MBEDTLS_PRIVATE(type); /*!< The LMS key type identifier as per
|
|
||||||
IANA. Only SHA256_M32_H10 is currently
|
|
||||||
supported. */
|
|
||||||
mbedtls_lmots_algorithm_type_t MBEDTLS_PRIVATE(otstype); /*!< The LM-OTS key type identifier as
|
|
||||||
per IANA. Only SHA256_N32_W8 is currently
|
|
||||||
supported. */
|
|
||||||
unsigned int MBEDTLS_PRIVATE(q_next_usable_key); /*!< The index of the next OTS key that has not
|
|
||||||
been used. */
|
|
||||||
mbedtls_lmots_context *MBEDTLS_PRIVATE(priv_keys); /*!< The private key material. One OTS key
|
|
||||||
for each leaf node in the merkle tree. */
|
|
||||||
unsigned char MBEDTLS_PRIVATE(T_1_pub_key)[MBEDTLS_LMS_M_NODE_BYTES]; /*!< The public key, in
|
unsigned char MBEDTLS_PRIVATE(T_1_pub_key)[MBEDTLS_LMS_M_NODE_BYTES]; /*!< The public key, in
|
||||||
the form of the merkle tree root node. */
|
the form of the merkle tree root node. */
|
||||||
} mbedtls_lms_context;
|
unsigned char MBEDTLS_PRIVATE(have_public_key); /*!< Whether the context contains a public key.
|
||||||
|
Boolean values only. */
|
||||||
|
} mbedtls_lms_public_t;
|
||||||
|
|
||||||
|
|
||||||
|
/** 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. */
|
||||||
|
mbedtls_lmots_public_t *MBEDTLS_PRIVATE(ots_public_keys); /*!< The OTS key public keys, used to
|
||||||
|
build the merkle tree. */
|
||||||
|
unsigned char MBEDTLS_PRIVATE(have_private_key); /*!< Whether the context contains a private key.
|
||||||
|
Boolean values only. */
|
||||||
|
} mbedtls_lms_private_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function initializes an LMS context
|
* \brief This function initializes an LMS public context
|
||||||
*
|
*
|
||||||
* \param ctx The uninitialized LMS context that will then be
|
* \param ctx The uninitialized LMS context that will then be
|
||||||
* initialized.
|
* initialized.
|
||||||
*/
|
*/
|
||||||
void mbedtls_lms_init( mbedtls_lms_context *ctx );
|
void mbedtls_lms_init_public( mbedtls_lms_public_t *ctx );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function uninitializes an LMS context
|
* \brief This function uninitializes an LMS public context
|
||||||
*
|
*
|
||||||
* \param ctx The initialized LMS context that will then be
|
* \param ctx The initialized LMS context that will then be
|
||||||
* uninitialized.
|
* uninitialized.
|
||||||
*/
|
*/
|
||||||
void mbedtls_lms_free( mbedtls_lms_context *ctx );
|
void mbedtls_lms_free_public( mbedtls_lms_public_t *ctx );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function sets the type of an LMS context
|
* \brief This function imports an LMS public key into a
|
||||||
|
* public LMS context.
|
||||||
*
|
*
|
||||||
* \note The parameter set in the context will then be used
|
* \note Before this function is called, the context must
|
||||||
* for keygen operations etc.
|
* have been initialized.
|
||||||
*
|
*
|
||||||
* \param ctx The initialized LMS context.
|
* \note See IETF RFC8554 for details of the encoding of
|
||||||
* \param type The type that will be set in the context.
|
* this public key.
|
||||||
* \param otstype The type of the LMOTS implementation used by this
|
*
|
||||||
* context.
|
* \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_set_algorithm_type( mbedtls_lms_context *ctx,
|
int mbedtls_lms_import_public_key( mbedtls_lms_public_t *ctx,
|
||||||
mbedtls_lms_algorithm_type_t type,
|
const unsigned char *key, size_t key_size );
|
||||||
mbedtls_lmots_algorithm_type_t otstype);
|
|
||||||
|
/**
|
||||||
|
* \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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief This function initializes an LMS private context
|
||||||
|
*
|
||||||
|
* \param ctx The uninitialized LMS private context that will
|
||||||
|
* then be initialized. */
|
||||||
|
void mbedtls_lms_init_private( 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_free_private( 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.
|
||||||
|
*
|
||||||
|
* \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, unsigned char *seed,
|
||||||
|
size_t seed_size );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief This function generates 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 generate the key
|
||||||
|
* from and store it into.
|
||||||
|
*
|
||||||
|
* \param 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,
|
||||||
|
mbedtls_lms_private_t *priv_ctx );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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( mbedtls_lms_public_t *ctx, unsigned char *key,
|
||||||
|
size_t key_size, size_t *key_len );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function creates a LMS signature, using a
|
* \brief This function creates a LMS signature, using a
|
||||||
* LMOTS context that contains a private key.
|
* LMS context that contains unused private keys.
|
||||||
*
|
*
|
||||||
* \warning This function is **not intended for use in
|
* \warning This function is **not intended for use in
|
||||||
* production**, due to as-yet unsolved problems with
|
* production**, due to as-yet unsolved problems with
|
||||||
@ -167,135 +307,27 @@ int mbedtls_lms_set_algorithm_type( mbedtls_lms_context *ctx,
|
|||||||
* important to not perform copy operations on LMS
|
* important to not perform copy operations on LMS
|
||||||
* contexts that contain private key material.
|
* contexts that contain private key material.
|
||||||
*
|
*
|
||||||
* \param ctx The initialized LMS context from which the
|
* \param ctx The initialized LMS private context from which the
|
||||||
* private key will be read.
|
* private key will be read.
|
||||||
* \param f_rng The RNG function to be used for signature
|
* \param f_rng The RNG function to be used for signature
|
||||||
* generation.
|
* generation.
|
||||||
* \param p_rng The RNG context to be passed to f_rng
|
* \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 The buffer from which the message will be read.
|
||||||
* \param msg_len The size of the message that 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.
|
* \param sig The buf into which the signature will be stored.
|
||||||
* Must be at least #MBEDTLS_LMOTS_SIG_LEN in size.
|
* 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 \c 0 on success.
|
||||||
* \return A non-zero error code on failure.
|
* \return A non-zero error code on failure.
|
||||||
*/
|
*/
|
||||||
int mbedtls_lms_sign( mbedtls_lms_context *ctx,
|
int mbedtls_lms_sign( mbedtls_lms_private_t *ctx,
|
||||||
int (*f_rng)(void *, unsigned char *, size_t),
|
int (*f_rng)(void *, unsigned char *, size_t),
|
||||||
void* p_rng, unsigned char *msg, unsigned int msg_len,
|
void* p_rng, unsigned char *msg, unsigned int msg_size,
|
||||||
unsigned char *sig);
|
unsigned char *sig, size_t sig_size, size_t *sig_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 context from which the public
|
|
||||||
* key will be read.
|
|
||||||
* \param msg The buffer from which the message will be read.
|
|
||||||
* \param msg_len 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.
|
|
||||||
*
|
|
||||||
* \return \c 0 on successful verification.
|
|
||||||
* \return A non-zero error code on failure.
|
|
||||||
*/
|
|
||||||
int mbedtls_lms_verify( const mbedtls_lms_context *ctx,
|
|
||||||
const unsigned char *msg, unsigned int msg_len,
|
|
||||||
const unsigned char *sig );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief This function imports an LMOTS public key into a
|
|
||||||
* 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_PUBKEY_LEN bytes will be read from
|
|
||||||
* this.
|
|
||||||
*
|
|
||||||
* \return \c 0 on success.
|
|
||||||
* \return A non-zero error code on failure.
|
|
||||||
*/
|
|
||||||
int mbedtls_lms_import_pubkey( mbedtls_lms_context *ctx,
|
|
||||||
const unsigned char *key );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief This function exports an LMOTS public key from a
|
|
||||||
* LMS 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 context that contains the
|
|
||||||
* publc key.
|
|
||||||
* \param key The buffer into which the key will be output. Must
|
|
||||||
* be at least #MBEDTLS_LMS_PUBKEY_LEN in size.
|
|
||||||
*
|
|
||||||
* \return \c 0 on success.
|
|
||||||
* \return A non-zero error code on failure.
|
|
||||||
*/
|
|
||||||
int mbedtls_lms_export_pubkey( mbedtls_lms_context *ctx,
|
|
||||||
unsigned char *key );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief This function generates 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 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_lms_gen_pubkey( mbedtls_lms_context *ctx );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief This function generates an LMS private key, and
|
|
||||||
* stores in into an LMS context.
|
|
||||||
*
|
|
||||||
* \warning This function is **not intended for use in
|
|
||||||
* production**, due to as-yet unsolved problems with
|
|
||||||
* handling stateful keys.
|
|
||||||
*
|
|
||||||
* \note Before this function is called, the context must
|
|
||||||
* have been initialized and the type of the LMS
|
|
||||||
* context set using mbedtls_lmots_set_algorithm_type
|
|
||||||
*
|
|
||||||
* \note The seed must have at least 256 bits of entropy.
|
|
||||||
*
|
|
||||||
* \param ctx The initialized LMOTS context to generate the key
|
|
||||||
* into.
|
|
||||||
* \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_len The length of the seed.
|
|
||||||
*
|
|
||||||
* \return \c 0 on success.
|
|
||||||
* \return A non-zero error code on failure.
|
|
||||||
*/
|
|
||||||
int mbedtls_lms_gen_privkey( mbedtls_lms_context *ctx,
|
|
||||||
int (*f_rng)(void *, unsigned char *, size_t),
|
|
||||||
void* p_rng, unsigned char *seed,
|
|
||||||
size_t seed_len );
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
835
library/lmots.c
835
library/lmots.c
File diff suppressed because it is too large
Load Diff
419
library/lmots.h
419
library/lmots.h
@ -26,7 +26,7 @@
|
|||||||
#ifndef MBEDTLS_LMOTS_H
|
#ifndef MBEDTLS_LMOTS_H
|
||||||
#define MBEDTLS_LMOTS_H
|
#define MBEDTLS_LMOTS_H
|
||||||
|
|
||||||
#include "mbedtls/private_access.h"
|
#include "mbedtls/build_info.h"
|
||||||
|
|
||||||
#include "psa/crypto.h"
|
#include "psa/crypto.h"
|
||||||
|
|
||||||
@ -34,27 +34,19 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#define MBEDTLS_LMOTS_N_HASH_LEN (32)
|
#define MBEDTLS_LMOTS_N_HASH_LEN (32)
|
||||||
#define MBEDTLS_LMOTS_P_SIG_SYMBOL_LEN (34)
|
#define MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT (34)
|
||||||
#define MBEDTLS_LMOTS_TYPE_LEN (4)
|
#define MBEDTLS_LMOTS_TYPE_LEN (4)
|
||||||
#define MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN (MBEDTLS_LMOTS_N_HASH_LEN)
|
#define MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN (MBEDTLS_LMOTS_N_HASH_LEN)
|
||||||
#define MBEDTLS_LMOTS_I_KEY_ID_LEN (16)
|
#define MBEDTLS_LMOTS_I_KEY_ID_LEN (16)
|
||||||
#define MBEDTLS_LMOTS_Q_LEAF_ID_LEN (4)
|
#define MBEDTLS_LMOTS_Q_LEAF_ID_LEN (4)
|
||||||
|
|
||||||
#define MBEDTLS_LMOTS_SIG_LEN (MBEDTLS_LMOTS_TYPE_LEN + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN + \
|
#define MBEDTLS_LMOTS_SIG_LEN (MBEDTLS_LMOTS_TYPE_LEN + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN + \
|
||||||
(MBEDTLS_LMOTS_P_SIG_SYMBOL_LEN * MBEDTLS_LMOTS_N_HASH_LEN))
|
(MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT * MBEDTLS_LMOTS_N_HASH_LEN))
|
||||||
|
|
||||||
#define MBEDTLS_LMOTS_PUBKEY_LEN (MBEDTLS_LMOTS_TYPE_LEN + MBEDTLS_LMOTS_I_KEY_ID_LEN + \
|
#define MBEDTLS_LMOTS_PUBLIC_KEY_LEN (MBEDTLS_LMOTS_TYPE_LEN + MBEDTLS_LMOTS_I_KEY_ID_LEN + \
|
||||||
MBEDTLS_LMOTS_Q_LEAF_ID_LEN + MBEDTLS_LMOTS_N_HASH_LEN)
|
MBEDTLS_LMOTS_Q_LEAF_ID_LEN + MBEDTLS_LMOTS_N_HASH_LEN)
|
||||||
|
|
||||||
#define MBEDTLS_LMOTS_SIG_TYPE_OFFSET (0)
|
#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 (MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN)
|
|
||||||
|
|
||||||
#define MBEDTLS_LMOTS_PUBKEY_TYPE_OFFSET (0)
|
|
||||||
#define MBEDTLS_LMOTS_PUBKEY_I_KEY_ID_OFFSET (MBEDTLS_LMOTS_PUBKEY_TYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN)
|
|
||||||
#define MBEDTLS_LMOTS_PUBKEY_Q_LEAF_ID_OFFSET (MBEDTLS_LMOTS_PUBKEY_I_KEY_ID_OFFSET + MBEDTLS_LMOTS_I_KEY_ID_LEN)
|
|
||||||
#define MBEDTLS_LMOTS_PUBKEY_KEY_HASH_OFFSET (MBEDTLS_LMOTS_PUBKEY_Q_LEAF_ID_OFFSET + MBEDTLS_LMOTS_Q_LEAF_ID_LEN)
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -68,54 +60,93 @@ typedef enum {
|
|||||||
} mbedtls_lmots_algorithm_type_t;
|
} mbedtls_lmots_algorithm_type_t;
|
||||||
|
|
||||||
|
|
||||||
/** LMOTS context structure.
|
/** 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
|
* The context must be initialized before it is used. A public key must either
|
||||||
* be imported, or an algorithm type set, a private key generated and the public
|
* be imported or generated from a private context.
|
||||||
* key calculated from it. A context that does not contain a public key cannot
|
|
||||||
* verify, and a context that does not contain a private key cannot sign.
|
|
||||||
* Signing a message will remove the private key from the context, as private
|
|
||||||
* keys can only be used a single time.
|
|
||||||
*
|
*
|
||||||
* \dot
|
* \dot
|
||||||
* digraph lmots {
|
* digraph lmots_public_t {
|
||||||
* UNINITIALIZED -> INIT [label="init"];
|
* UNINITIALIZED -> INIT [label="init"];
|
||||||
* TYPE_SET -> INIT [label="free"];
|
* HAVE_PUBLIC_KEY -> INIT [label="free"];
|
||||||
* PRIVATE -> INIT [label="free"];
|
* INIT -> HAVE_PUBLIC_KEY [label="import_public_key"];
|
||||||
* PUBLIC -> INIT [label="free"];
|
* INIT -> HAVE_PUBLIC_KEY [label="calculate_public_key from private key"];
|
||||||
* "PRIVATE+PUBLIC" -> INIT [label="free"];
|
* HAVE_PUBLIC_KEY -> HAVE_PUBLIC_KEY [label="export_public_key"];
|
||||||
* INIT -> TYPE_SET [label="set_algorithm_type"];
|
|
||||||
* PRIVATE -> TYPE_SET [label="sign"];
|
|
||||||
* "PRIVATE+PUBLIC" -> PUBLIC [label="sign"];
|
|
||||||
* INIT -> PUBLIC [label="import_public"];
|
|
||||||
* PUBLIC -> PUBLIC [label="export_pubkey"];
|
|
||||||
* "PRIVATE+PUBLIC" -> "PRIVATE+PUBLIC" [label="export_pubkey"];
|
|
||||||
* PRIVATE -> "PRIVATE+PUBLIC" [label="gen_pubkey"];
|
|
||||||
* TYPE_SET -> PRIVATE [label="gen_privkey"];
|
|
||||||
* }
|
* }
|
||||||
* \enddot
|
* \enddot
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned char MBEDTLS_PRIVATE(have_privkey); /*!< Whether the context contains a private key.
|
mbedtls_lmots_parameters_t MBEDTLS_PRIVATE(params);
|
||||||
|
unsigned char MBEDTLS_PRIVATE(public_key)[32];
|
||||||
|
unsigned char MBEDTLS_PRIVATE(have_public_key); /*!< Whether the context contains a public key.
|
||||||
Boolean values only. */
|
Boolean values only. */
|
||||||
unsigned char MBEDTLS_PRIVATE(have_pubkey); /*!< Whether the context contains a public key.
|
} mbedtls_lmots_public_t;
|
||||||
|
|
||||||
|
/** 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][32];
|
||||||
|
unsigned char MBEDTLS_PRIVATE(have_private_key); /*!< Whether the context contains a private key.
|
||||||
Boolean values only. */
|
Boolean values only. */
|
||||||
unsigned char MBEDTLS_PRIVATE(I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN]); /*!< The key
|
} mbedtls_lmots_private_t;
|
||||||
identifier. */
|
|
||||||
unsigned int MBEDTLS_PRIVATE(q_leaf_identifier); /*!< Which leaf of the LMS key this is.
|
/**
|
||||||
0 if the key is not part of an LMS key. */
|
* \brief This function converts an unsigned int into a
|
||||||
unsigned char MBEDTLS_PRIVATE(q_leaf_identifier_bytes)[MBEDTLS_LMOTS_Q_LEAF_ID_LEN];/*!< The
|
* network-byte-order (big endian) string.
|
||||||
leaf identifier in network bytes form. */
|
*
|
||||||
mbedtls_lmots_algorithm_type_t MBEDTLS_PRIVATE(type); /*!< The LM-OTS key type identifier as
|
* \param val The unsigned integer value
|
||||||
per IANA. Only SHA256_N32_W8 is currently
|
* \param len The length of the string.
|
||||||
supported. */
|
* \param bytes The string to output into.
|
||||||
unsigned char MBEDTLS_PRIVATE(priv_key[MBEDTLS_LMOTS_P_SIG_SYMBOL_LEN][32]); /*!< The private
|
*
|
||||||
key, one hash output per byte of the encoded
|
* \return The corresponding LMS error code.
|
||||||
symbol string P (32 bytes of hash output +
|
*/
|
||||||
2 bytes of checksum). */
|
void unsigned_int_to_network_bytes(unsigned int val, size_t len, unsigned char *bytes);
|
||||||
unsigned char MBEDTLS_PRIVATE(pub_key[32]); /*!< The public key, in the form of a SHA256
|
|
||||||
output. */
|
/**
|
||||||
} mbedtls_lmots_context;
|
* \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 network_bytes_to_unsigned_int(size_t len, const unsigned char *bytes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function converts a \ref psa_status_t to a
|
* \brief This function converts a \ref psa_status_t to a
|
||||||
@ -129,35 +160,41 @@ int mbedtls_lms_error_from_psa(psa_status_t status);
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function initializes an LMOTS context
|
* \brief This function initializes a public LMOTS context
|
||||||
*
|
*
|
||||||
* \param ctx The uninitialized LMOTS context that will then be
|
* \param ctx The uninitialized LMOTS context that will then be
|
||||||
* initialized.
|
* initialized.
|
||||||
*/
|
*/
|
||||||
void mbedtls_lmots_init( mbedtls_lmots_context *ctx );
|
void mbedtls_lmots_init_public( mbedtls_lmots_public_t *ctx );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function uninitializes an LMOTS context
|
* \brief This function uninitializes a public LMOTS context
|
||||||
*
|
*
|
||||||
* \param ctx The initialized LMOTS context that will then be
|
* \param ctx The initialized LMOTS context that will then be
|
||||||
* uninitialized.
|
* uninitialized.
|
||||||
*/
|
*/
|
||||||
void mbedtls_lmots_free( mbedtls_lmots_context *ctx );
|
void mbedtls_lmots_free_public( mbedtls_lmots_public_t *ctx );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function sets the type of an LMOTS context
|
* \brief This function imports an LMOTS public key into a
|
||||||
|
* LMOTS context.
|
||||||
*
|
*
|
||||||
* \note The parameter set in the context will then be used
|
* \note Before this function is called, the context must
|
||||||
* for keygen operations etc.
|
* have been initialized.
|
||||||
*
|
*
|
||||||
* \param ctx The initialized LMOTS context.
|
* \note See IETF RFC8554 for details of the encoding of
|
||||||
* \param type The type that will be set in the context.
|
* 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 \c 0 on success.
|
||||||
* \return A non-zero error code on failure.
|
* \return A non-zero error code on failure.
|
||||||
*/
|
*/
|
||||||
int mbedtls_lmots_set_algorithm_type( mbedtls_lmots_context *ctx,
|
int mbedtls_lmots_import_public_key( mbedtls_lmots_public_t *ctx,
|
||||||
mbedtls_lmots_algorithm_type_t type );
|
const unsigned char *key, size_t key_size );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief This function creates a candidate public key from
|
* \brief This function creates a candidate public key from
|
||||||
@ -170,12 +207,10 @@ int mbedtls_lmots_set_algorithm_type( mbedtls_lmots_context *ctx,
|
|||||||
* mbedtls_lmots_verify will be used for LMOTS
|
* mbedtls_lmots_verify will be used for LMOTS
|
||||||
* signature verification.
|
* signature verification.
|
||||||
*
|
*
|
||||||
* \param I_key_identifier The key identifier of the key, as a 16-byte string.
|
* \param params The LMOTS parameter set, q and I values as an
|
||||||
* \param q_leaf_identifier The leaf identifier of key. If this LMOTS key is
|
* mbedtls_lmots_parameters_t struct.
|
||||||
* not being used as part of an LMS key, this should
|
|
||||||
* be set to 0.
|
|
||||||
* \param msg The buffer from which the message will be read.
|
* \param msg The buffer from which the message will be read.
|
||||||
* \param msg_len The size of the message that 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.
|
* \param sig The buffer from which the signature will be read.
|
||||||
* #MBEDTLS_LMOTS_SIG_LEN bytes will be read from this.
|
* #MBEDTLS_LMOTS_SIG_LEN bytes will be read from this.
|
||||||
* \param out The buffer where the candidate public key will be
|
* \param out The buffer where the candidate public key will be
|
||||||
@ -185,13 +220,128 @@ int mbedtls_lmots_set_algorithm_type( mbedtls_lmots_context *ctx,
|
|||||||
* \return \c 0 on success.
|
* \return \c 0 on success.
|
||||||
* \return A non-zero error code on failure.
|
* \return A non-zero error code on failure.
|
||||||
*/
|
*/
|
||||||
int mbedtls_lmots_generate_pub_key_candidate( const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN],
|
int mbedtls_lmots_calculate_public_key_candidate( const mbedtls_lmots_parameters_t *params,
|
||||||
const unsigned char q_leaf_identifier[MBEDTLS_LMOTS_Q_LEAF_ID_LEN],
|
const unsigned char *msg,
|
||||||
const unsigned char *msg,
|
size_t msg_size,
|
||||||
size_t msg_len,
|
const unsigned char *sig,
|
||||||
const unsigned char *sig,
|
size_t sig_size,
|
||||||
unsigned char *out );
|
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.
|
||||||
|
*
|
||||||
|
* \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( mbedtls_lmots_public_t *ctx, const unsigned char *msg,
|
||||||
|
size_t msg_size, const unsigned char *sig,
|
||||||
|
size_t sig_size );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief This function initializes a private LMOTS context
|
||||||
|
*
|
||||||
|
* \param ctx The uninitialized LMOTS context that will then be
|
||||||
|
* initialized.
|
||||||
|
*/
|
||||||
|
void mbedtls_lmots_init_private( 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_free_private( mbedtls_lmots_private_t *ctx );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief This function generates 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.
|
||||||
|
*
|
||||||
|
* \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,
|
||||||
|
mbedtls_lmots_private_t *priv_ctx);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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( mbedtls_lmots_public_t *ctx,
|
||||||
|
unsigned char *key, size_t key_size,
|
||||||
|
size_t *key_len );
|
||||||
/**
|
/**
|
||||||
* \brief This function creates a LMOTS signature, using a
|
* \brief This function creates a LMOTS signature, using a
|
||||||
* LMOTS context that contains a private key.
|
* LMOTS context that contains a private key.
|
||||||
@ -213,135 +363,18 @@ int mbedtls_lmots_generate_pub_key_candidate( const unsigned char I_key_identifi
|
|||||||
* generation.
|
* generation.
|
||||||
* \param p_rng The RNG context to be passed to f_rng
|
* \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 The buffer from which the message will be read.
|
||||||
* \param msg_len The size of the message that 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.
|
* \param sig The buf into which the signature will be stored.
|
||||||
* Must be at least #MBEDTLS_LMOTS_SIG_LEN in size.
|
* Must be at least #MBEDTLS_LMOTS_SIG_LEN in size.
|
||||||
*
|
*
|
||||||
* \return \c 0 on success.
|
* \return \c 0 on success.
|
||||||
* \return A non-zero error code on failure.
|
* \return A non-zero error code on failure.
|
||||||
*/
|
*/
|
||||||
int mbedtls_lmots_sign( mbedtls_lmots_context *ctx,
|
int mbedtls_lmots_sign( mbedtls_lmots_private_t *ctx,
|
||||||
int (*f_rng)(void *, unsigned char *, size_t),
|
int (*f_rng)(void *, unsigned char *, size_t),
|
||||||
void *p_rng, const unsigned char *msg, size_t msg_len,
|
void *p_rng, const unsigned char *msg, size_t msg_size,
|
||||||
unsigned char *sig );
|
unsigned char *sig, size_t sig_size, size_t* sig_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.
|
|
||||||
*
|
|
||||||
* \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 LMOTS context from which the public
|
|
||||||
* key will be read.
|
|
||||||
* \param msg The buffer from which the message will be read.
|
|
||||||
* \param msg_len 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( mbedtls_lmots_context *ctx, const unsigned char *msg,
|
|
||||||
size_t msg_len, const unsigned char *sig );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \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_PUBKEY_LEN bytes will be read from
|
|
||||||
* this.
|
|
||||||
*
|
|
||||||
* \return \c 0 on success.
|
|
||||||
* \return A non-zero error code on failure.
|
|
||||||
*/
|
|
||||||
int mbedtls_lmots_import_pubkey( mbedtls_lmots_context *ctx,
|
|
||||||
const unsigned char *key );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \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_PUBKEY_LEN in size.
|
|
||||||
*
|
|
||||||
* \return \c 0 on success.
|
|
||||||
* \return A non-zero error code on failure.
|
|
||||||
*/
|
|
||||||
int mbedtls_lmots_export_pubkey( mbedtls_lmots_context *ctx,
|
|
||||||
unsigned char *key );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \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_gen_pubkey( mbedtls_lmots_context *ctx );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief This function generates 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.
|
|
||||||
*
|
|
||||||
* \note Before this function is called, the context must
|
|
||||||
* have been initialized and the type of the LMOTS
|
|
||||||
* context set using mbedtls_lmots_set_algorithm_type
|
|
||||||
*
|
|
||||||
* \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_len The length of the seed.
|
|
||||||
*
|
|
||||||
* \return \c 0 on success.
|
|
||||||
* \return A non-zero error code on failure.
|
|
||||||
*/
|
|
||||||
int mbedtls_lmots_gen_privkey( mbedtls_lmots_context *ctx,
|
|
||||||
const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN],
|
|
||||||
unsigned int q_leaf_identifier,
|
|
||||||
const unsigned char *seed,
|
|
||||||
size_t seed_len );
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
701
library/lms.c
701
library/lms.c
@ -54,45 +54,33 @@
|
|||||||
#define mbedtls_free free
|
#define mbedtls_free free
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MERKLE_TREE_NODE_AM (1 << (MBEDTLS_LMS_H_TREE_HEIGHT + 1))
|
#define MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET (0)
|
||||||
#define MERKLE_TREE_LEAF_AM (1 << MBEDTLS_LMS_H_TREE_HEIGHT)
|
#define MBEDTLS_LMS_SIG_OTS_SIG_OFFSET (MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET + MBEDTLS_LMOTS_Q_LEAF_ID_LEN)
|
||||||
#define MERKLE_TREE_INTR_AM (1 << MBEDTLS_LMS_H_TREE_HEIGHT)
|
#define MBEDTLS_LMS_SIG_TYPE_OFFSET (MBEDTLS_LMS_SIG_OTS_SIG_OFFSET + MBEDTLS_LMOTS_SIG_LEN)
|
||||||
|
#define MBEDTLS_LMS_SIG_PATH_OFFSET (MBEDTLS_LMS_SIG_TYPE_OFFSET + MBEDTLS_LMS_TYPE_LEN)
|
||||||
|
|
||||||
|
#define MBEDTLS_LMS_PUBLIC_KEY_TYPE_OFFSET (0)
|
||||||
|
#define MBEDTLS_LMS_PUBLIC_KEY_OTSTYPE_OFFSET (MBEDTLS_LMS_PUBLIC_KEY_TYPE_OFFSET + MBEDTLS_LMS_TYPE_LEN)
|
||||||
|
#define MBEDTLS_LMS_PUBLIC_KEY_I_KEY_ID_OFFSET (MBEDTLS_LMS_PUBLIC_KEY_OTSTYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN)
|
||||||
|
#define MBEDTLS_LMS_PUBLIC_KEY_ROOT_NODE_OFFSET (MBEDTLS_LMS_PUBLIC_KEY_I_KEY_ID_OFFSET + MBEDTLS_LMOTS_I_KEY_ID_LEN)
|
||||||
|
|
||||||
|
|
||||||
|
#define MERKLE_TREE_NODE_AM (1u << (MBEDTLS_LMS_H_TREE_HEIGHT + 1u))
|
||||||
|
#define MERKLE_TREE_LEAF_NODE_AM (1u << MBEDTLS_LMS_H_TREE_HEIGHT)
|
||||||
|
#define MERKLE_TREE_INTERNAL_NODE_AM (1u << MBEDTLS_LMS_H_TREE_HEIGHT)
|
||||||
|
|
||||||
#define D_CONST_LEN (2)
|
#define D_CONST_LEN (2)
|
||||||
|
static const unsigned char D_LEAF_CONSTANT_BYTES[D_CONST_LEN] = {0x82, 0x82};
|
||||||
|
static const unsigned char D_INTERNAL_CONSTANT_BYTES[D_CONST_LEN] = {0x83, 0x83};
|
||||||
|
|
||||||
#define D_LEAF_CONSTANT (0x8282)
|
static int create_merkle_leaf_node( const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN],
|
||||||
#define D_INTR_CONSTANT (0x8383)
|
|
||||||
|
|
||||||
static void val_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int network_bytes_to_val(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_merkle_leaf_node( const mbedtls_lms_context *ctx,
|
|
||||||
unsigned char pub_key[MBEDTLS_LMOTS_N_HASH_LEN],
|
unsigned char pub_key[MBEDTLS_LMOTS_N_HASH_LEN],
|
||||||
unsigned int r_node_idx,
|
unsigned int r_node_idx,
|
||||||
unsigned char out[32] )
|
unsigned char out[MBEDTLS_LMS_M_NODE_BYTES] )
|
||||||
{
|
{
|
||||||
psa_hash_operation_t op;
|
psa_hash_operation_t op;
|
||||||
psa_status_t status;
|
psa_status_t status;
|
||||||
size_t output_hash_len;
|
size_t output_hash_len;
|
||||||
unsigned char D_LEAF_bytes[D_CONST_LEN];
|
|
||||||
unsigned char r_node_idx_bytes[4];
|
unsigned char r_node_idx_bytes[4];
|
||||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
@ -100,64 +88,49 @@ static int create_merkle_leaf_node( const mbedtls_lms_context *ctx,
|
|||||||
status = psa_hash_setup( &op, PSA_ALG_SHA_256 );
|
status = psa_hash_setup( &op, PSA_ALG_SHA_256 );
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if ( ret != 0 )
|
if ( ret != 0 )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = psa_hash_update( &op, ctx->MBEDTLS_PRIVATE(I_key_identifier),
|
status = psa_hash_update( &op, I_key_identifier, MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
||||||
MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
val_to_network_bytes( r_node_idx, 4, r_node_idx_bytes );
|
unsigned_int_to_network_bytes( r_node_idx, 4, r_node_idx_bytes );
|
||||||
status = psa_hash_update( &op, r_node_idx_bytes, 4 );
|
status = psa_hash_update( &op, r_node_idx_bytes, 4 );
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
val_to_network_bytes( D_LEAF_CONSTANT, D_CONST_LEN, D_LEAF_bytes );
|
status = psa_hash_update( &op, D_LEAF_CONSTANT_BYTES, D_CONST_LEN );
|
||||||
status = psa_hash_update( &op, D_LEAF_bytes, D_CONST_LEN );
|
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = psa_hash_update( &op, pub_key, MBEDTLS_LMOTS_N_HASH_LEN );
|
status = psa_hash_update( &op, pub_key, MBEDTLS_LMOTS_N_HASH_LEN );
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = psa_hash_finish( &op, out, 32, &output_hash_len);
|
status = psa_hash_finish( &op, out, MBEDTLS_LMS_M_NODE_BYTES, &output_hash_len);
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
exit:
|
||||||
psa_hash_abort( &op );
|
psa_hash_abort( &op );
|
||||||
|
|
||||||
return( ret );
|
return( ret );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_merkle_intr_node( const mbedtls_lms_context *ctx,
|
static int create_merkle_internal_node( const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN],
|
||||||
const unsigned char left_node[32],
|
const unsigned char left_node[MBEDTLS_LMS_M_NODE_BYTES],
|
||||||
const unsigned char rght_node[32],
|
const unsigned char right_node[MBEDTLS_LMS_M_NODE_BYTES],
|
||||||
unsigned int r_node_idx,
|
unsigned int r_node_idx,
|
||||||
unsigned char out[32] )
|
unsigned char out[MBEDTLS_LMS_M_NODE_BYTES] )
|
||||||
{
|
{
|
||||||
psa_hash_operation_t op;
|
psa_hash_operation_t op;
|
||||||
psa_status_t status;
|
psa_status_t status;
|
||||||
size_t output_hash_len;
|
size_t output_hash_len;
|
||||||
unsigned char D_INTR_bytes[D_CONST_LEN];
|
|
||||||
unsigned char r_node_idx_bytes[4];
|
unsigned char r_node_idx_bytes[4];
|
||||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
@ -165,75 +138,62 @@ static int create_merkle_intr_node( const mbedtls_lms_context *ctx,
|
|||||||
status = psa_hash_setup( &op, PSA_ALG_SHA_256 );
|
status = psa_hash_setup( &op, PSA_ALG_SHA_256 );
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if ( ret != 0 )
|
if ( ret != 0 )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = psa_hash_update( &op, ctx->MBEDTLS_PRIVATE(I_key_identifier),
|
status = psa_hash_update( &op, I_key_identifier, MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
||||||
MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
val_to_network_bytes( r_node_idx, 4, r_node_idx_bytes );
|
unsigned_int_to_network_bytes( r_node_idx, 4, r_node_idx_bytes );
|
||||||
status = psa_hash_update( &op, r_node_idx_bytes, 4 );
|
status = psa_hash_update( &op, r_node_idx_bytes, 4 );
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
val_to_network_bytes( D_INTR_CONSTANT, D_CONST_LEN, D_INTR_bytes );
|
status = psa_hash_update( &op, D_INTERNAL_CONSTANT_BYTES, D_CONST_LEN );
|
||||||
status = psa_hash_update( &op, D_INTR_bytes, D_CONST_LEN );
|
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = psa_hash_update( &op, left_node, MBEDTLS_LMOTS_N_HASH_LEN );
|
status = psa_hash_update( &op, left_node, MBEDTLS_LMOTS_N_HASH_LEN );
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = psa_hash_update( &op, rght_node, MBEDTLS_LMOTS_N_HASH_LEN );
|
status = psa_hash_update( &op, right_node, MBEDTLS_LMOTS_N_HASH_LEN );
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = psa_hash_finish( &op, out, 32, &output_hash_len);
|
ret = psa_hash_finish( &op, out, MBEDTLS_LMS_M_NODE_BYTES, &output_hash_len);
|
||||||
ret = mbedtls_lms_error_from_psa( status );
|
ret = mbedtls_lms_error_from_psa( status );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
goto exit;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
exit:
|
||||||
psa_hash_abort( &op );
|
psa_hash_abort( &op );
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int generate_merkle_tree( mbedtls_lms_context *ctx,
|
static int calculate_merkle_tree( mbedtls_lms_private_t *ctx,
|
||||||
unsigned char tree[MERKLE_TREE_NODE_AM][32] )
|
unsigned char tree[MERKLE_TREE_NODE_AM][MBEDTLS_LMS_M_NODE_BYTES] )
|
||||||
{
|
{
|
||||||
unsigned int priv_key_idx;
|
unsigned int priv_key_idx;
|
||||||
unsigned int r_node_idx;
|
unsigned int r_node_idx;
|
||||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
/* First create the leaf nodes, in ascending order */
|
/* First create the leaf nodes, in ascending order */
|
||||||
for( priv_key_idx = 0; priv_key_idx < MERKLE_TREE_INTR_AM; priv_key_idx++ )
|
for( priv_key_idx = 0; priv_key_idx < MERKLE_TREE_INTERNAL_NODE_AM;
|
||||||
|
priv_key_idx++ )
|
||||||
{
|
{
|
||||||
r_node_idx = MERKLE_TREE_INTR_AM + priv_key_idx;
|
r_node_idx = MERKLE_TREE_INTERNAL_NODE_AM + priv_key_idx;
|
||||||
|
|
||||||
ret = create_merkle_leaf_node( ctx, ctx->MBEDTLS_PRIVATE(priv_keys)[priv_key_idx].pub_key,
|
ret = create_merkle_leaf_node(
|
||||||
r_node_idx, tree[r_node_idx] );
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
|
ctx->MBEDTLS_PRIVATE(ots_public_keys)[priv_key_idx].MBEDTLS_PRIVATE(public_key),
|
||||||
|
r_node_idx, tree[r_node_idx] );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
return( ret );
|
return( ret );
|
||||||
@ -242,11 +202,12 @@ static int generate_merkle_tree( mbedtls_lms_context *ctx,
|
|||||||
|
|
||||||
/* Then the internal nodes, in reverse order so that we can guarantee the
|
/* Then the internal nodes, in reverse order so that we can guarantee the
|
||||||
* parent has been created */
|
* parent has been created */
|
||||||
for( r_node_idx = MERKLE_TREE_INTR_AM - 1; r_node_idx > 0; r_node_idx-- )
|
for( r_node_idx = MERKLE_TREE_INTERNAL_NODE_AM - 1; r_node_idx > 0;
|
||||||
|
r_node_idx-- )
|
||||||
{
|
{
|
||||||
ret = create_merkle_intr_node( ctx, tree[(r_node_idx * 2)],
|
ret = create_merkle_internal_node(
|
||||||
tree[(r_node_idx * 2 + 1)],
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
r_node_idx, tree[r_node_idx] );
|
tree[(r_node_idx * 2)], tree[(r_node_idx * 2 + 1)], r_node_idx, tree[r_node_idx] );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
return( ret );
|
return( ret );
|
||||||
@ -256,18 +217,17 @@ static int generate_merkle_tree( mbedtls_lms_context *ctx,
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_merkle_path( mbedtls_lms_context *ctx,
|
static int get_merkle_path( mbedtls_lms_private_t *ctx,
|
||||||
unsigned int leaf_node_id, unsigned char path[MBEDTLS_LMS_H_TREE_HEIGHT][32] )
|
unsigned int leaf_node_id,
|
||||||
|
unsigned char path[MBEDTLS_LMS_H_TREE_HEIGHT][MBEDTLS_LMS_M_NODE_BYTES] )
|
||||||
{
|
{
|
||||||
unsigned char tree[MERKLE_TREE_NODE_AM][32];
|
unsigned char tree[MERKLE_TREE_NODE_AM][MBEDTLS_LMS_M_NODE_BYTES];
|
||||||
unsigned int curr_node_id = leaf_node_id;
|
unsigned int curr_node_id = leaf_node_id;
|
||||||
unsigned int parent_node_id;
|
|
||||||
unsigned char sibling_relative_id;
|
|
||||||
unsigned int adjacent_node_id;
|
unsigned int adjacent_node_id;
|
||||||
unsigned int height;
|
unsigned int height;
|
||||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
ret = generate_merkle_tree( ctx, tree);
|
ret = calculate_merkle_tree( ctx, tree);
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
return( ret );
|
return( ret );
|
||||||
@ -275,195 +235,107 @@ static int get_merkle_path( mbedtls_lms_context *ctx,
|
|||||||
|
|
||||||
for( height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT; height++ )
|
for( height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT; height++ )
|
||||||
{
|
{
|
||||||
parent_node_id = ( curr_node_id / 2 );
|
adjacent_node_id = curr_node_id ^ 1;
|
||||||
|
|
||||||
/* 0 if the node is a left child, 1 if the node is a right child */
|
|
||||||
sibling_relative_id = curr_node_id & 1;
|
|
||||||
|
|
||||||
adjacent_node_id = ( parent_node_id * 2 ) + ( 1 - sibling_relative_id );
|
|
||||||
|
|
||||||
memcpy( &path[height], &tree[adjacent_node_id], MBEDTLS_LMOTS_N_HASH_LEN );
|
memcpy( &path[height], &tree[adjacent_node_id], MBEDTLS_LMOTS_N_HASH_LEN );
|
||||||
|
|
||||||
curr_node_id = parent_node_id;
|
curr_node_id >>=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void mbedtls_lms_init( mbedtls_lms_context *ctx )
|
void mbedtls_lms_init_public( mbedtls_lms_public_t *ctx )
|
||||||
{
|
{
|
||||||
if( ctx == NULL )
|
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_lms_public_t ) ) ;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_lms_context ) ) ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mbedtls_lms_free( mbedtls_lms_context *ctx )
|
void mbedtls_lms_free_public( mbedtls_lms_public_t *ctx )
|
||||||
{
|
{
|
||||||
unsigned int idx;
|
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_lms_public_t ) );
|
||||||
|
|
||||||
if( ctx == NULL )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(have_privkey) )
|
|
||||||
{
|
|
||||||
for( idx = 0; idx < MERKLE_TREE_LEAF_AM; idx++ )
|
|
||||||
{
|
|
||||||
mbedtls_lmots_free( &ctx->MBEDTLS_PRIVATE(priv_keys)[idx] );
|
|
||||||
}
|
|
||||||
|
|
||||||
mbedtls_free( ctx->MBEDTLS_PRIVATE(priv_keys) );
|
|
||||||
}
|
|
||||||
|
|
||||||
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_lms_context ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mbedtls_lms_set_algorithm_type( mbedtls_lms_context *ctx,
|
int mbedtls_lms_import_public_key( mbedtls_lms_public_t *ctx,
|
||||||
mbedtls_lms_algorithm_type_t type,
|
const unsigned char *key, size_t key_size )
|
||||||
mbedtls_lmots_algorithm_type_t otstype )
|
|
||||||
{
|
{
|
||||||
if( ctx == NULL )
|
mbedtls_lms_algorithm_type_t type;
|
||||||
|
mbedtls_lmots_algorithm_type_t otstype;
|
||||||
|
|
||||||
|
if( key_size < MBEDTLS_LMS_PUBLIC_KEY_LEN )
|
||||||
|
{
|
||||||
|
return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL );
|
||||||
|
}
|
||||||
|
|
||||||
|
type = network_bytes_to_unsigned_int( MBEDTLS_LMS_TYPE_LEN, key + MBEDTLS_LMS_PUBLIC_KEY_TYPE_OFFSET );
|
||||||
|
if( type != MBEDTLS_LMS_SHA256_M32_H10 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(type) = type;
|
||||||
|
|
||||||
ctx->MBEDTLS_PRIVATE(type) = type;
|
otstype = network_bytes_to_unsigned_int( MBEDTLS_LMOTS_TYPE_LEN,
|
||||||
ctx->MBEDTLS_PRIVATE(otstype) = otstype;
|
key + MBEDTLS_LMS_PUBLIC_KEY_OTSTYPE_OFFSET );
|
||||||
|
if( otstype != MBEDTLS_LMOTS_SHA256_N32_W8 )
|
||||||
|
{
|
||||||
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
|
}
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(otstype) = otstype;
|
||||||
|
|
||||||
|
memcpy( ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
|
key + MBEDTLS_LMS_PUBLIC_KEY_I_KEY_ID_OFFSET,
|
||||||
|
MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
||||||
|
memcpy( ctx->MBEDTLS_PRIVATE(T_1_pub_key), key + MBEDTLS_LMS_PUBLIC_KEY_ROOT_NODE_OFFSET,
|
||||||
|
MBEDTLS_LMOTS_N_HASH_LEN );
|
||||||
|
|
||||||
|
ctx->MBEDTLS_PRIVATE(have_public_key) = 1;
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int mbedtls_lms_sign( mbedtls_lms_context *ctx,
|
int mbedtls_lms_verify( const mbedtls_lms_public_t *ctx,
|
||||||
int ( *f_rng)(void *, unsigned char *, size_t),
|
const unsigned char *msg, size_t msg_size,
|
||||||
void* p_rng, unsigned char *msg, unsigned int msg_len,
|
const unsigned char *sig, size_t sig_size )
|
||||||
unsigned char *sig )
|
|
||||||
{
|
|
||||||
unsigned int q_leaf_identifier;
|
|
||||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
|
||||||
|
|
||||||
if( ctx == NULL )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ! ctx->MBEDTLS_PRIVATE(have_privkey) )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( msg == NULL )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( sig == NULL )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(type) != MBEDTLS_LMS_SHA256_M32_H10 )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(otstype) != MBEDTLS_LMOTS_SHA256_N32_W8 )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(q_next_usable_key) >= MERKLE_TREE_LEAF_AM )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_OUT_OF_PRIV_KEYS );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
q_leaf_identifier = ctx->MBEDTLS_PRIVATE(q_next_usable_key);
|
|
||||||
/* This new value must _always_ be written back to the disk before the
|
|
||||||
* signature is returned.
|
|
||||||
*/
|
|
||||||
ctx->MBEDTLS_PRIVATE(q_next_usable_key) += 1;
|
|
||||||
|
|
||||||
ret = mbedtls_lmots_sign( &ctx->MBEDTLS_PRIVATE(priv_keys)[q_leaf_identifier],
|
|
||||||
f_rng, p_rng, msg, msg_len,
|
|
||||||
sig + MBEDTLS_LMS_SIG_OTS_SIG_OFFSET );
|
|
||||||
if( ret )
|
|
||||||
{
|
|
||||||
return( ret );
|
|
||||||
}
|
|
||||||
|
|
||||||
val_to_network_bytes( ctx->MBEDTLS_PRIVATE(type), MBEDTLS_LMS_TYPE_LEN,
|
|
||||||
sig + MBEDTLS_LMS_SIG_TYPE_OFFSET );
|
|
||||||
val_to_network_bytes( q_leaf_identifier, MBEDTLS_LMOTS_Q_LEAF_ID_LEN,
|
|
||||||
sig + MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET);
|
|
||||||
|
|
||||||
ret = get_merkle_path( ctx, MERKLE_TREE_INTR_AM + q_leaf_identifier,
|
|
||||||
( unsigned char( * )[32] )( sig + MBEDTLS_LMS_SIG_PATH_OFFSET ) );
|
|
||||||
if( ret )
|
|
||||||
{
|
|
||||||
return( ret );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
int mbedtls_lms_verify( const mbedtls_lms_context *ctx,
|
|
||||||
const unsigned char *msg, unsigned int msg_len,
|
|
||||||
const unsigned char *sig )
|
|
||||||
{
|
{
|
||||||
unsigned int q_leaf_identifier;
|
unsigned int q_leaf_identifier;
|
||||||
unsigned char Kc_candidate_ots_pub_key[MBEDTLS_LMOTS_N_HASH_LEN];
|
unsigned char Kc_candidate_ots_pub_key[MBEDTLS_LMOTS_N_HASH_LEN];
|
||||||
unsigned char Tc_candidate_root_node[32];
|
unsigned char Tc_candidate_root_node[MBEDTLS_LMS_M_NODE_BYTES];
|
||||||
unsigned int height;
|
unsigned int height;
|
||||||
unsigned int curr_node_id;
|
unsigned int curr_node_id;
|
||||||
unsigned int parent_node_id;
|
unsigned int parent_node_id;
|
||||||
const unsigned char* left_node;
|
const unsigned char* left_node;
|
||||||
const unsigned char* rght_node;
|
const unsigned char* right_node;
|
||||||
|
mbedtls_lmots_parameters_t ots_params;
|
||||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
if( ctx == NULL )
|
if( ! ctx->MBEDTLS_PRIVATE(have_public_key) )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ! ctx->MBEDTLS_PRIVATE(have_pubkey) )
|
if( sig_size != MBEDTLS_LMS_SIG_LEN )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( msg == NULL)
|
if( ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(type)
|
||||||
|
!= MBEDTLS_LMS_SHA256_M32_H10 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( sig == NULL)
|
if( ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(otstype)
|
||||||
|
!= MBEDTLS_LMOTS_SHA256_N32_W8 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(type) != MBEDTLS_LMS_SHA256_M32_H10 )
|
if( network_bytes_to_unsigned_int( MBEDTLS_LMS_TYPE_LEN,
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(otstype) != MBEDTLS_LMOTS_SHA256_N32_W8 )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if( network_bytes_to_val( MBEDTLS_LMS_TYPE_LEN,
|
|
||||||
sig + MBEDTLS_LMS_SIG_TYPE_OFFSET) != MBEDTLS_LMS_SHA256_M32_H10 )
|
sig + MBEDTLS_LMS_SIG_TYPE_OFFSET) != MBEDTLS_LMS_SHA256_M32_H10 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_VERIFY_FAILED );
|
return( MBEDTLS_ERR_LMS_VERIFY_FAILED );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( network_bytes_to_val( MBEDTLS_LMOTS_TYPE_LEN,
|
if( network_bytes_to_unsigned_int( MBEDTLS_LMOTS_TYPE_LEN,
|
||||||
sig + MBEDTLS_LMS_SIG_OTS_SIG_OFFSET + MBEDTLS_LMOTS_SIG_TYPE_OFFSET)
|
sig + MBEDTLS_LMS_SIG_OTS_SIG_OFFSET + MBEDTLS_LMOTS_SIG_TYPE_OFFSET)
|
||||||
!= MBEDTLS_LMOTS_SHA256_N32_W8 )
|
!= MBEDTLS_LMOTS_SHA256_N32_W8 )
|
||||||
{
|
{
|
||||||
@ -471,29 +343,39 @@ int mbedtls_lms_verify( const mbedtls_lms_context *ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
q_leaf_identifier = network_bytes_to_val( MBEDTLS_LMOTS_Q_LEAF_ID_LEN,
|
q_leaf_identifier = network_bytes_to_unsigned_int( MBEDTLS_LMOTS_Q_LEAF_ID_LEN,
|
||||||
sig + MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET );
|
sig + MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET );
|
||||||
|
|
||||||
if( q_leaf_identifier >= MERKLE_TREE_LEAF_AM )
|
if( q_leaf_identifier >= MERKLE_TREE_LEAF_NODE_AM )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_VERIFY_FAILED );
|
return( MBEDTLS_ERR_LMS_VERIFY_FAILED );
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mbedtls_lmots_generate_pub_key_candidate( ctx->MBEDTLS_PRIVATE(I_key_identifier),
|
memcpy(ots_params.MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
sig + MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET,
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
msg, msg_len,
|
MBEDTLS_LMOTS_I_KEY_ID_LEN);
|
||||||
sig + MBEDTLS_LMS_SIG_OTS_SIG_OFFSET,
|
unsigned_int_to_network_bytes( q_leaf_identifier,
|
||||||
Kc_candidate_ots_pub_key );
|
MBEDTLS_LMOTS_Q_LEAF_ID_LEN,
|
||||||
|
ots_params.MBEDTLS_PRIVATE(q_leaf_identifier) );
|
||||||
|
ots_params.type = ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(otstype);
|
||||||
|
|
||||||
|
ret = mbedtls_lmots_calculate_public_key_candidate( &ots_params, msg, msg_size,
|
||||||
|
sig + MBEDTLS_LMS_SIG_OTS_SIG_OFFSET,
|
||||||
|
MBEDTLS_LMOTS_SIG_LEN,
|
||||||
|
Kc_candidate_ots_pub_key,
|
||||||
|
sizeof(Kc_candidate_ots_pub_key),
|
||||||
|
NULL );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
return( ret );
|
return( ret );
|
||||||
}
|
}
|
||||||
|
|
||||||
create_merkle_leaf_node( ctx, Kc_candidate_ots_pub_key,
|
create_merkle_leaf_node(
|
||||||
MERKLE_TREE_INTR_AM + q_leaf_identifier,
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
Tc_candidate_root_node );
|
Kc_candidate_ots_pub_key, MERKLE_TREE_INTERNAL_NODE_AM + q_leaf_identifier,
|
||||||
|
Tc_candidate_root_node );
|
||||||
|
|
||||||
curr_node_id = MERKLE_TREE_INTR_AM + q_leaf_identifier;
|
curr_node_id = MERKLE_TREE_INTERNAL_NODE_AM + q_leaf_identifier;
|
||||||
|
|
||||||
for( height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT; height++ )
|
for( height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT; height++ )
|
||||||
{
|
{
|
||||||
@ -502,17 +384,18 @@ int mbedtls_lms_verify( const mbedtls_lms_context *ctx,
|
|||||||
/* Left/right node ordering matters for the hash */
|
/* Left/right node ordering matters for the hash */
|
||||||
if( curr_node_id & 1 )
|
if( curr_node_id & 1 )
|
||||||
{
|
{
|
||||||
left_node = ( ( const unsigned char( * )[32] )( sig + MBEDTLS_LMS_SIG_PATH_OFFSET ) )[height];
|
left_node = ( ( const unsigned char( * )[MBEDTLS_LMS_M_NODE_BYTES] )( sig + MBEDTLS_LMS_SIG_PATH_OFFSET ) )[height];
|
||||||
rght_node = Tc_candidate_root_node;
|
right_node = Tc_candidate_root_node;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
left_node = Tc_candidate_root_node;
|
left_node = Tc_candidate_root_node;
|
||||||
rght_node = ( ( const unsigned char( * )[32] )( sig + MBEDTLS_LMS_SIG_PATH_OFFSET ) )[height];
|
right_node = ( ( const unsigned char( * )[MBEDTLS_LMS_M_NODE_BYTES] )( sig + MBEDTLS_LMS_SIG_PATH_OFFSET ) )[height];
|
||||||
}
|
}
|
||||||
|
|
||||||
create_merkle_intr_node( ctx, left_node, rght_node, parent_node_id,
|
create_merkle_internal_node(
|
||||||
Tc_candidate_root_node);
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
|
left_node, right_node, parent_node_id, Tc_candidate_root_node);
|
||||||
|
|
||||||
curr_node_id /= 2;
|
curr_node_id /= 2;
|
||||||
}
|
}
|
||||||
@ -526,115 +409,148 @@ int mbedtls_lms_verify( const mbedtls_lms_context *ctx,
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int mbedtls_lms_import_pubkey( mbedtls_lms_context *ctx,
|
void mbedtls_lms_init_private( mbedtls_lms_private_t *ctx )
|
||||||
const unsigned char *key )
|
|
||||||
{
|
{
|
||||||
mbedtls_lms_algorithm_type_t type;
|
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_lms_public_t ) ) ;
|
||||||
mbedtls_lmots_algorithm_type_t otstype;
|
}
|
||||||
|
|
||||||
if( ctx == NULL )
|
void mbedtls_lms_free_private( mbedtls_lms_private_t *ctx )
|
||||||
|
{
|
||||||
|
unsigned int idx;
|
||||||
|
|
||||||
|
if( ctx->MBEDTLS_PRIVATE(have_private_key) )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
for( idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM; idx++ )
|
||||||
|
{
|
||||||
|
mbedtls_lmots_free_private( &ctx->MBEDTLS_PRIVATE(ots_private_keys)[idx] );
|
||||||
|
mbedtls_lmots_free_public( &ctx->MBEDTLS_PRIVATE(ots_public_keys)[idx] );
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_free( ctx->MBEDTLS_PRIVATE(ots_private_keys) );
|
||||||
|
mbedtls_free( ctx->MBEDTLS_PRIVATE(ots_public_keys) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( key == NULL )
|
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_lms_public_t ) );
|
||||||
{
|
}
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
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, unsigned char *seed,
|
||||||
|
size_t seed_size )
|
||||||
|
{
|
||||||
|
unsigned int idx = 0;
|
||||||
|
unsigned int free_idx = 0;
|
||||||
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
type = network_bytes_to_val( MBEDTLS_LMS_TYPE_LEN, key + MBEDTLS_LMS_PUBKEY_TYPE_OFFSET );
|
|
||||||
if( type != MBEDTLS_LMS_SHA256_M32_H10 )
|
if( type != MBEDTLS_LMS_SHA256_M32_H10 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
ctx->MBEDTLS_PRIVATE(type) = type;
|
|
||||||
|
|
||||||
otstype = network_bytes_to_val( MBEDTLS_LMOTS_TYPE_LEN,
|
|
||||||
key + MBEDTLS_LMS_PUBKEY_OTSTYPE_OFFSET );
|
|
||||||
if( otstype != MBEDTLS_LMOTS_SHA256_N32_W8 )
|
if( otstype != MBEDTLS_LMOTS_SHA256_N32_W8 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
ctx->MBEDTLS_PRIVATE(otstype) = otstype;
|
|
||||||
|
|
||||||
memcpy( ctx->MBEDTLS_PRIVATE(I_key_identifier), key + MBEDTLS_LMS_PUBKEY_I_KEY_ID_OFFSET,
|
if( ctx->MBEDTLS_PRIVATE(have_private_key) )
|
||||||
MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
{
|
||||||
memcpy( ctx->MBEDTLS_PRIVATE(T_1_pub_key), key + MBEDTLS_LMS_PUBKEY_ROOT_NODE_OFFSET,
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
MBEDTLS_LMOTS_N_HASH_LEN );
|
}
|
||||||
|
|
||||||
ctx->MBEDTLS_PRIVATE(have_pubkey) = 1;
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(type) = type;
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(otstype) = otstype;
|
||||||
|
|
||||||
|
f_rng( p_rng,
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
|
MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
||||||
|
|
||||||
|
ctx->MBEDTLS_PRIVATE(ots_private_keys) = mbedtls_calloc( MERKLE_TREE_LEAF_NODE_AM,
|
||||||
|
sizeof( mbedtls_lmots_private_t));
|
||||||
|
if( ctx->MBEDTLS_PRIVATE(ots_private_keys) == NULL )
|
||||||
|
{
|
||||||
|
ret = MBEDTLS_ERR_LMS_ALLOC_FAILED;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->MBEDTLS_PRIVATE(ots_public_keys) = mbedtls_calloc( MERKLE_TREE_LEAF_NODE_AM,
|
||||||
|
sizeof( mbedtls_lmots_public_t));
|
||||||
|
if( ctx->MBEDTLS_PRIVATE(ots_public_keys) == NULL )
|
||||||
|
{
|
||||||
|
ret = MBEDTLS_ERR_LMS_ALLOC_FAILED;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM; idx++ )
|
||||||
|
{
|
||||||
|
mbedtls_lmots_init_private( &ctx->MBEDTLS_PRIVATE(ots_private_keys)[idx] );
|
||||||
|
mbedtls_lmots_init_public( &ctx->MBEDTLS_PRIVATE(ots_public_keys)[idx] );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for( idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM; idx++ )
|
||||||
|
{
|
||||||
|
ret = mbedtls_lmots_generate_private_key( &ctx->MBEDTLS_PRIVATE(ots_private_keys)[idx],
|
||||||
|
otstype,
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
|
idx, seed, seed_size );
|
||||||
|
if( ret)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
ret = mbedtls_lmots_calculate_public_key( &ctx->MBEDTLS_PRIVATE(ots_public_keys)[idx],
|
||||||
|
&ctx->MBEDTLS_PRIVATE(ots_private_keys)[idx] );
|
||||||
|
if( ret)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->MBEDTLS_PRIVATE(q_next_usable_key) = 0;
|
||||||
|
ctx->MBEDTLS_PRIVATE(have_private_key) = 1;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if( ret )
|
||||||
|
{
|
||||||
|
for ( free_idx = 0; free_idx < idx; free_idx++ ) {
|
||||||
|
mbedtls_lmots_free_private( &ctx->MBEDTLS_PRIVATE(ots_private_keys)[free_idx] );
|
||||||
|
mbedtls_lmots_free_public( &ctx->MBEDTLS_PRIVATE(ots_public_keys)[free_idx] );
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_free( ctx->MBEDTLS_PRIVATE(ots_private_keys) );
|
||||||
|
mbedtls_free( ctx->MBEDTLS_PRIVATE(ots_public_keys) );
|
||||||
|
return( ret );
|
||||||
|
}
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int mbedtls_lms_export_pubkey( mbedtls_lms_context *ctx,
|
int mbedtls_lms_calculate_public_key( mbedtls_lms_public_t *ctx,
|
||||||
unsigned char *key )
|
mbedtls_lms_private_t *priv_ctx )
|
||||||
{
|
{
|
||||||
if( ctx == NULL )
|
unsigned char tree[MERKLE_TREE_NODE_AM][MBEDTLS_LMS_M_NODE_BYTES];
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( key == NULL )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ! ctx->MBEDTLS_PRIVATE(have_pubkey) )
|
|
||||||
{
|
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
val_to_network_bytes( ctx->MBEDTLS_PRIVATE(type),
|
|
||||||
MBEDTLS_LMS_TYPE_LEN, key + MBEDTLS_LMS_PUBKEY_TYPE_OFFSET );
|
|
||||||
val_to_network_bytes( ctx->MBEDTLS_PRIVATE(otstype),
|
|
||||||
MBEDTLS_LMOTS_TYPE_LEN, key + MBEDTLS_LMS_PUBKEY_OTSTYPE_OFFSET );
|
|
||||||
memcpy( key + MBEDTLS_LMS_PUBKEY_I_KEY_ID_OFFSET,
|
|
||||||
ctx->MBEDTLS_PRIVATE(I_key_identifier),
|
|
||||||
MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
|
||||||
memcpy( key + MBEDTLS_LMS_PUBKEY_ROOT_NODE_OFFSET,
|
|
||||||
ctx->MBEDTLS_PRIVATE(T_1_pub_key),
|
|
||||||
MBEDTLS_LMOTS_N_HASH_LEN );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
int mbedtls_lms_gen_pubkey( mbedtls_lms_context *ctx )
|
|
||||||
{
|
|
||||||
unsigned char tree[MERKLE_TREE_NODE_AM][32];
|
|
||||||
unsigned int idx;
|
|
||||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
if( ctx == NULL )
|
if( ! priv_ctx->MBEDTLS_PRIVATE( have_private_key ) )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ! ctx->MBEDTLS_PRIVATE( have_privkey ) )
|
if( priv_ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(type)
|
||||||
|
!= MBEDTLS_LMS_SHA256_M32_H10 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(type) != MBEDTLS_LMS_SHA256_M32_H10 )
|
if( priv_ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(otstype)
|
||||||
|
!= MBEDTLS_LMOTS_SHA256_N32_W8 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(otstype) != MBEDTLS_LMOTS_SHA256_N32_W8 )
|
memcpy( &ctx->MBEDTLS_PRIVATE(params), &priv_ctx->MBEDTLS_PRIVATE(params),
|
||||||
{
|
sizeof(mbedtls_lmots_parameters_t) );
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
|
||||||
}
|
|
||||||
|
|
||||||
for( idx = 0; idx < MERKLE_TREE_LEAF_AM; idx++ )
|
ret = calculate_merkle_tree( priv_ctx, tree);
|
||||||
{
|
|
||||||
ret = mbedtls_lmots_gen_pubkey( &ctx->MBEDTLS_PRIVATE(priv_keys)[idx] );
|
|
||||||
if( ret )
|
|
||||||
{
|
|
||||||
return( ret );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = generate_merkle_tree( ctx, tree);
|
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
return( ret );
|
return( ret );
|
||||||
@ -643,83 +559,112 @@ int mbedtls_lms_gen_pubkey( mbedtls_lms_context *ctx )
|
|||||||
/* Root node is always at position 1, due to 1-based indexing */
|
/* Root node is always at position 1, due to 1-based indexing */
|
||||||
memcpy( ctx->MBEDTLS_PRIVATE(T_1_pub_key), &tree[1], MBEDTLS_LMOTS_N_HASH_LEN );
|
memcpy( ctx->MBEDTLS_PRIVATE(T_1_pub_key), &tree[1], MBEDTLS_LMOTS_N_HASH_LEN );
|
||||||
|
|
||||||
ctx->MBEDTLS_PRIVATE(have_pubkey) = 1;
|
ctx->MBEDTLS_PRIVATE(have_public_key) = 1;
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int mbedtls_lms_gen_privkey( mbedtls_lms_context *ctx,
|
|
||||||
int ( *f_rng)(void *, unsigned char *, size_t),
|
int mbedtls_lms_export_public_key( mbedtls_lms_public_t *ctx, unsigned char *key,
|
||||||
void* p_rng, unsigned char *seed,
|
size_t key_size, size_t *key_len )
|
||||||
size_t seed_len )
|
|
||||||
{
|
{
|
||||||
unsigned int idx;
|
if( key_size < MBEDTLS_LMS_PUBLIC_KEY_LEN ) {
|
||||||
|
return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ! ctx->MBEDTLS_PRIVATE(have_public_key) )
|
||||||
|
{
|
||||||
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned_int_to_network_bytes(
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(type),
|
||||||
|
MBEDTLS_LMS_TYPE_LEN, key + MBEDTLS_LMS_PUBLIC_KEY_TYPE_OFFSET );
|
||||||
|
unsigned_int_to_network_bytes(
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(otstype),
|
||||||
|
MBEDTLS_LMOTS_TYPE_LEN, key + MBEDTLS_LMS_PUBLIC_KEY_OTSTYPE_OFFSET );
|
||||||
|
memcpy( key + MBEDTLS_LMS_PUBLIC_KEY_I_KEY_ID_OFFSET,
|
||||||
|
ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(I_key_identifier),
|
||||||
|
MBEDTLS_LMOTS_I_KEY_ID_LEN );
|
||||||
|
memcpy( key + MBEDTLS_LMS_PUBLIC_KEY_ROOT_NODE_OFFSET,
|
||||||
|
ctx->MBEDTLS_PRIVATE(T_1_pub_key),
|
||||||
|
MBEDTLS_LMOTS_N_HASH_LEN );
|
||||||
|
|
||||||
|
if( key_len != NULL ) {
|
||||||
|
*key_len = MBEDTLS_LMS_PUBLIC_KEY_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int mbedtls_lms_sign( mbedtls_lms_private_t *ctx,
|
||||||
|
int (*f_rng)(void *, unsigned char *, size_t),
|
||||||
|
void* p_rng, 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;
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||||
|
|
||||||
if( ctx == NULL )
|
if( ! ctx->MBEDTLS_PRIVATE(have_private_key) )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(type) != MBEDTLS_LMS_SHA256_M32_H10 )
|
if( sig_size < MBEDTLS_LMS_SIG_LEN )
|
||||||
|
{
|
||||||
|
return( MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(type) != MBEDTLS_LMS_SHA256_M32_H10 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(otstype) != MBEDTLS_LMOTS_SHA256_N32_W8 )
|
if( ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(otstype)
|
||||||
|
!= MBEDTLS_LMOTS_SHA256_N32_W8 )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(have_privkey) )
|
if( ctx->MBEDTLS_PRIVATE(q_next_usable_key) >= MERKLE_TREE_LEAF_NODE_AM )
|
||||||
{
|
{
|
||||||
return( MBEDTLS_ERR_LMS_BAD_INPUT_DATA );
|
return( MBEDTLS_ERR_LMS_OUT_OF_PRIVATE_KEYS );
|
||||||
}
|
|
||||||
|
|
||||||
f_rng( p_rng, ctx->MBEDTLS_PRIVATE(I_key_identifier),
|
|
||||||
sizeof( ctx->MBEDTLS_PRIVATE(I_key_identifier) ) );
|
|
||||||
|
|
||||||
ctx->MBEDTLS_PRIVATE(priv_keys) = mbedtls_calloc( MERKLE_TREE_LEAF_AM,
|
|
||||||
sizeof( mbedtls_lmots_context));
|
|
||||||
if( ctx->MBEDTLS_PRIVATE(priv_keys) == NULL )
|
|
||||||
{
|
|
||||||
ret = MBEDTLS_ERR_LMS_ALLOC_FAILED;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
for( idx = 0; idx < MERKLE_TREE_LEAF_AM; idx++ )
|
|
||||||
{
|
|
||||||
mbedtls_lmots_init( &ctx->MBEDTLS_PRIVATE(priv_keys)[idx] );
|
|
||||||
ret = mbedtls_lmots_set_algorithm_type( &ctx->MBEDTLS_PRIVATE(priv_keys)[idx],
|
|
||||||
ctx->MBEDTLS_PRIVATE(otstype) );
|
|
||||||
if( ret)
|
|
||||||
{
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for( idx = 0; idx < MERKLE_TREE_LEAF_AM; idx++ )
|
q_leaf_identifier = ctx->MBEDTLS_PRIVATE(q_next_usable_key);
|
||||||
{
|
/* This new value must _always_ be written back to the disk before the
|
||||||
ret = mbedtls_lmots_gen_privkey( &ctx->MBEDTLS_PRIVATE(priv_keys)[idx],
|
* signature is returned.
|
||||||
ctx->MBEDTLS_PRIVATE(I_key_identifier),
|
*/
|
||||||
idx, seed, seed_len );
|
ctx->MBEDTLS_PRIVATE(q_next_usable_key) += 1;
|
||||||
if( ret)
|
|
||||||
{
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->MBEDTLS_PRIVATE(q_next_usable_key) = 0;
|
ret = mbedtls_lmots_sign( &ctx->MBEDTLS_PRIVATE(ots_private_keys)[q_leaf_identifier],
|
||||||
ctx->MBEDTLS_PRIVATE(have_privkey) = 1;
|
f_rng, p_rng, msg, msg_size,
|
||||||
|
sig + MBEDTLS_LMS_SIG_OTS_SIG_OFFSET,
|
||||||
out:
|
MBEDTLS_LMS_SIG_LEN, NULL );
|
||||||
if( ret )
|
if( ret )
|
||||||
{
|
{
|
||||||
mbedtls_free( ctx->MBEDTLS_PRIVATE(priv_keys) );
|
|
||||||
return( ret );
|
return( ret );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned_int_to_network_bytes( ctx->MBEDTLS_PRIVATE(params).MBEDTLS_PRIVATE(type),
|
||||||
|
MBEDTLS_LMS_TYPE_LEN, sig + MBEDTLS_LMS_SIG_TYPE_OFFSET );
|
||||||
|
unsigned_int_to_network_bytes( q_leaf_identifier, MBEDTLS_LMOTS_Q_LEAF_ID_LEN,
|
||||||
|
sig + MBEDTLS_LMS_SIG_Q_LEAF_ID_OFFSET);
|
||||||
|
|
||||||
|
ret = get_merkle_path( ctx, MERKLE_TREE_INTERNAL_NODE_AM + q_leaf_identifier,
|
||||||
|
( unsigned char( * )[MBEDTLS_LMS_M_NODE_BYTES] )( sig + MBEDTLS_LMS_SIG_PATH_OFFSET ) );
|
||||||
|
if( ret )
|
||||||
|
{
|
||||||
|
return( ret );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( sig_len != NULL ) {
|
||||||
|
*sig_len = MBEDTLS_LMS_SIG_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
/* BEGIN_CASE */
|
/* BEGIN_CASE */
|
||||||
void lmots_sign_verify_test ( data_t * msg )
|
void lmots_sign_verify_test ( data_t * msg )
|
||||||
{
|
{
|
||||||
mbedtls_lmots_context ctx;
|
mbedtls_lmots_public_t pub_ctx;
|
||||||
|
mbedtls_lmots_private_t priv_ctx;
|
||||||
unsigned char sig[MBEDTLS_LMOTS_SIG_LEN];
|
unsigned char sig[MBEDTLS_LMOTS_SIG_LEN];
|
||||||
mbedtls_entropy_context entropy_ctx;
|
mbedtls_entropy_context entropy_ctx;
|
||||||
mbedtls_ctr_drbg_context drbg_ctx;
|
mbedtls_ctr_drbg_context drbg_ctx;
|
||||||
@ -23,22 +24,25 @@ void lmots_sign_verify_test ( data_t * msg )
|
|||||||
|
|
||||||
mbedtls_entropy_init( &entropy_ctx );
|
mbedtls_entropy_init( &entropy_ctx );
|
||||||
mbedtls_ctr_drbg_init( &drbg_ctx );
|
mbedtls_ctr_drbg_init( &drbg_ctx );
|
||||||
mbedtls_lmots_init( &ctx );
|
mbedtls_lmots_init_public( &pub_ctx );
|
||||||
|
mbedtls_lmots_init_private( &priv_ctx );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_ctr_drbg_seed( &drbg_ctx, mbedtls_entropy_func,
|
TEST_ASSERT( mbedtls_ctr_drbg_seed( &drbg_ctx, mbedtls_entropy_func,
|
||||||
&entropy_ctx, (uint8_t*)"", 0 ) == 0 );
|
&entropy_ctx, (uint8_t*)"", 0 ) == 0 );
|
||||||
TEST_ASSERT( mbedtls_ctr_drbg_random( &drbg_ctx, seed, sizeof( seed ) ) == 0 );
|
TEST_ASSERT( mbedtls_ctr_drbg_random( &drbg_ctx, seed, sizeof( seed ) ) == 0 );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_lmots_set_algorithm_type(&ctx, MBEDTLS_LMOTS_SHA256_N32_W8) == 0 );
|
TEST_ASSERT( mbedtls_lmots_generate_private_key(&priv_ctx, MBEDTLS_LMOTS_SHA256_N32_W8,
|
||||||
TEST_ASSERT( mbedtls_lmots_gen_privkey(&ctx, (uint8_t[16]){0}, 0x12, seed, sizeof( seed ) ) == 0 );
|
(uint8_t[16]){0}, 0x12, seed, sizeof( seed ) ) == 0 );
|
||||||
TEST_ASSERT( mbedtls_lmots_gen_pubkey(&ctx) == 0 );
|
TEST_ASSERT( mbedtls_lmots_calculate_public_key(&pub_ctx, &priv_ctx) == 0 );
|
||||||
TEST_ASSERT( mbedtls_lmots_sign(&ctx, mbedtls_ctr_drbg_random, &drbg_ctx, msg->x, msg->len, sig ) == 0 );
|
TEST_ASSERT( mbedtls_lmots_sign(&priv_ctx, mbedtls_ctr_drbg_random, &drbg_ctx,
|
||||||
TEST_ASSERT( mbedtls_lmots_verify(&ctx, msg->x, msg->len, sig) == 0 );
|
msg->x, msg->len, sig, sizeof(sig), NULL ) == 0 );
|
||||||
|
TEST_ASSERT( mbedtls_lmots_verify(&pub_ctx, msg->x, msg->len, sig, sizeof(sig)) == 0 );
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mbedtls_entropy_free( &entropy_ctx );
|
mbedtls_entropy_free( &entropy_ctx );
|
||||||
mbedtls_ctr_drbg_free( &drbg_ctx );
|
mbedtls_ctr_drbg_free( &drbg_ctx );
|
||||||
mbedtls_lmots_free( &ctx );
|
mbedtls_lmots_free_public( &pub_ctx );
|
||||||
|
mbedtls_lmots_free_private( &priv_ctx );
|
||||||
}
|
}
|
||||||
/* END_CASE */
|
/* END_CASE */
|
||||||
|
|
||||||
@ -46,40 +50,40 @@ exit:
|
|||||||
void lmots_verify_test ( data_t * msg, data_t * sig, data_t * pub_key,
|
void lmots_verify_test ( data_t * msg, data_t * sig, data_t * pub_key,
|
||||||
int expected_rc )
|
int expected_rc )
|
||||||
{
|
{
|
||||||
mbedtls_lmots_context ctx;
|
mbedtls_lmots_public_t ctx;
|
||||||
|
|
||||||
mbedtls_lmots_init( &ctx );
|
mbedtls_lmots_init_public( &ctx );
|
||||||
|
|
||||||
mbedtls_lmots_import_pubkey( &ctx, pub_key->x );
|
mbedtls_lmots_import_public_key( &ctx, pub_key->x, pub_key->len );
|
||||||
|
|
||||||
TEST_ASSERT(mbedtls_lmots_verify( &ctx, msg->x, msg->len, sig->x ) == expected_rc );
|
TEST_ASSERT(mbedtls_lmots_verify( &ctx, msg->x, msg->len, sig->x, sig->len ) == expected_rc );
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mbedtls_lmots_free( &ctx );
|
mbedtls_lmots_free_public( &ctx );
|
||||||
}
|
}
|
||||||
/* END_CASE */
|
/* END_CASE */
|
||||||
|
|
||||||
/* BEGIN_CASE */
|
/* BEGIN_CASE */
|
||||||
void lmots_import_export_test ( data_t * pub_key )
|
void lmots_import_export_test ( data_t * pub_key )
|
||||||
{
|
{
|
||||||
mbedtls_lmots_context ctx;
|
mbedtls_lmots_public_t ctx;
|
||||||
uint8_t exported_pub_key[MBEDTLS_LMOTS_PUBKEY_LEN];
|
uint8_t exported_pub_key[MBEDTLS_LMOTS_PUBLIC_KEY_LEN];
|
||||||
|
|
||||||
mbedtls_lmots_init( &ctx );
|
mbedtls_lmots_init_public( &ctx );
|
||||||
TEST_ASSERT( mbedtls_lmots_import_pubkey( &ctx, pub_key->x ) == 0 );
|
TEST_ASSERT( mbedtls_lmots_import_public_key( &ctx, pub_key->x, pub_key->len ) == 0 );
|
||||||
TEST_ASSERT( mbedtls_lmots_export_pubkey( &ctx, exported_pub_key ) == 0 );
|
TEST_ASSERT( mbedtls_lmots_export_public_key( &ctx, exported_pub_key, sizeof( exported_pub_key ), NULL ) == 0 );
|
||||||
|
|
||||||
TEST_ASSERT( memcmp( pub_key->x, exported_pub_key, MBEDTLS_LMOTS_PUBKEY_LEN ) == 0 );
|
TEST_ASSERT( memcmp( pub_key->x, exported_pub_key, MBEDTLS_LMOTS_PUBLIC_KEY_LEN ) == 0 );
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mbedtls_lmots_free( &ctx );
|
mbedtls_lmots_free_public( &ctx );
|
||||||
}
|
}
|
||||||
/* END_CASE */
|
/* END_CASE */
|
||||||
|
|
||||||
/* BEGIN_CASE */
|
/* BEGIN_CASE */
|
||||||
void lmots_reuse_test ( data_t * msg )
|
void lmots_reuse_test ( data_t * msg )
|
||||||
{
|
{
|
||||||
mbedtls_lmots_context ctx;
|
mbedtls_lmots_private_t ctx;
|
||||||
unsigned char sig[MBEDTLS_LMOTS_SIG_LEN];
|
unsigned char sig[MBEDTLS_LMOTS_SIG_LEN];
|
||||||
mbedtls_entropy_context entropy_ctx;
|
mbedtls_entropy_context entropy_ctx;
|
||||||
mbedtls_ctr_drbg_context drbg_ctx;
|
mbedtls_ctr_drbg_context drbg_ctx;
|
||||||
@ -92,19 +96,21 @@ void lmots_reuse_test ( data_t * msg )
|
|||||||
|
|
||||||
mbedtls_ctr_drbg_random( &drbg_ctx, seed, sizeof( seed ) );
|
mbedtls_ctr_drbg_random( &drbg_ctx, seed, sizeof( seed ) );
|
||||||
|
|
||||||
mbedtls_lmots_init( &ctx );
|
mbedtls_lmots_init_private( &ctx );
|
||||||
TEST_ASSERT( mbedtls_lmots_set_algorithm_type( &ctx, MBEDTLS_LMOTS_SHA256_N32_W8 ) == 0 );
|
TEST_ASSERT( mbedtls_lmots_generate_private_key(&ctx, MBEDTLS_LMOTS_SHA256_N32_W8,
|
||||||
TEST_ASSERT( mbedtls_lmots_gen_privkey(&ctx, (uint8_t[16]){0}, 0x12, seed, sizeof( seed ) ) == 0 );
|
(uint8_t[16]){0}, 0x12, seed, sizeof( seed ) ) == 0 );
|
||||||
TEST_ASSERT( mbedtls_lmots_sign(&ctx, mbedtls_ctr_drbg_random, &drbg_ctx, msg->x, msg->len, sig ) == 0 );
|
TEST_ASSERT( mbedtls_lmots_sign(&ctx, mbedtls_ctr_drbg_random, &drbg_ctx,
|
||||||
|
msg->x, msg->len, sig, sizeof( sig ), NULL ) == 0 );
|
||||||
|
|
||||||
/* Running another sign operation should fail, since the key should now have
|
/* Running another sign operation should fail, since the key should now have
|
||||||
* been erased.
|
* been erased.
|
||||||
*/
|
*/
|
||||||
TEST_ASSERT( mbedtls_lmots_sign(&ctx, mbedtls_ctr_drbg_random, &drbg_ctx, msg->x, msg->len, sig ) != 0 );
|
TEST_ASSERT( mbedtls_lmots_sign(&ctx, mbedtls_ctr_drbg_random, &drbg_ctx,
|
||||||
|
msg->x, msg->len, sig, sizeof( sig ), NULL ) != 0 );
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mbedtls_entropy_free( &entropy_ctx );
|
mbedtls_entropy_free( &entropy_ctx );
|
||||||
mbedtls_ctr_drbg_free( &drbg_ctx );
|
mbedtls_ctr_drbg_free( &drbg_ctx );
|
||||||
mbedtls_lmots_free( &ctx );
|
mbedtls_lmots_free_private( &ctx );
|
||||||
}
|
}
|
||||||
/* END_CASE */
|
/* END_CASE */
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
/* BEGIN_CASE */
|
/* BEGIN_CASE */
|
||||||
void lms_sign_verify_test ( data_t * msg )
|
void lms_sign_verify_test ( data_t * msg )
|
||||||
{
|
{
|
||||||
mbedtls_lms_context ctx;
|
mbedtls_lms_public_t pub_ctx;
|
||||||
|
mbedtls_lms_private_t priv_ctx;
|
||||||
unsigned char sig[MBEDTLS_LMS_SIG_LEN];
|
unsigned char sig[MBEDTLS_LMS_SIG_LEN];
|
||||||
mbedtls_entropy_context entropy_ctx;
|
mbedtls_entropy_context entropy_ctx;
|
||||||
mbedtls_ctr_drbg_context drbg_ctx;
|
mbedtls_ctr_drbg_context drbg_ctx;
|
||||||
@ -22,29 +23,35 @@ void lms_sign_verify_test ( data_t * msg )
|
|||||||
|
|
||||||
mbedtls_entropy_init( &entropy_ctx );
|
mbedtls_entropy_init( &entropy_ctx );
|
||||||
mbedtls_ctr_drbg_init( &drbg_ctx );
|
mbedtls_ctr_drbg_init( &drbg_ctx );
|
||||||
mbedtls_lms_init( &ctx );
|
mbedtls_lms_init_public( &pub_ctx );
|
||||||
|
mbedtls_lms_init_private( &priv_ctx );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_ctr_drbg_seed( &drbg_ctx, mbedtls_entropy_func,
|
TEST_ASSERT( mbedtls_ctr_drbg_seed( &drbg_ctx, mbedtls_entropy_func,
|
||||||
&entropy_ctx, ( uint8_t* )"", 0 ) == 0 );
|
&entropy_ctx, ( uint8_t* )"", 0 ) == 0 );
|
||||||
TEST_ASSERT( mbedtls_ctr_drbg_random( &drbg_ctx, seed, sizeof( seed ) ) == 0 );
|
TEST_ASSERT( mbedtls_ctr_drbg_random( &drbg_ctx, seed, sizeof( seed ) ) == 0 );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_lms_set_algorithm_type( &ctx, MBEDTLS_LMS_SHA256_M32_H10, MBEDTLS_LMOTS_SHA256_N32_W8 ) == 0 );
|
|
||||||
|
|
||||||
/* Allocation failure isn't a test failure, since it likely just means there's not enough memory to run the test */
|
/* Allocation failure isn't a test failure, since it likely just means there's not enough memory to run the test */
|
||||||
rc = mbedtls_lms_gen_privkey( &ctx, mbedtls_ctr_drbg_random, &drbg_ctx, seed, sizeof( seed ) );
|
rc = mbedtls_lms_generate_private_key( &priv_ctx, MBEDTLS_LMS_SHA256_M32_H10,
|
||||||
|
MBEDTLS_LMOTS_SHA256_N32_W8,
|
||||||
|
mbedtls_ctr_drbg_random, &drbg_ctx, seed,
|
||||||
|
sizeof( seed ) );
|
||||||
TEST_ASSUME( rc != MBEDTLS_ERR_LMS_ALLOC_FAILED );
|
TEST_ASSUME( rc != MBEDTLS_ERR_LMS_ALLOC_FAILED );
|
||||||
TEST_ASSERT( rc == 0 );
|
TEST_ASSERT( rc == 0 );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_lms_gen_pubkey( &ctx) == 0 );
|
TEST_ASSERT( mbedtls_lms_calculate_public_key( &pub_ctx, &priv_ctx ) == 0 );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_lms_sign( &ctx, mbedtls_ctr_drbg_random, &drbg_ctx, msg->x, msg->len, sig ) == 0 );
|
TEST_ASSERT( mbedtls_lms_sign( &priv_ctx, mbedtls_ctr_drbg_random,
|
||||||
|
&drbg_ctx, msg->x, msg->len, sig,
|
||||||
|
sizeof( sig ), NULL ) == 0 );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_lms_verify( &ctx, msg->x, msg->len, sig) == 0 );
|
TEST_ASSERT( mbedtls_lms_verify( &pub_ctx, msg->x, msg->len, sig,
|
||||||
|
sizeof( sig ) ) == 0 );
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mbedtls_entropy_free( &entropy_ctx );
|
mbedtls_entropy_free( &entropy_ctx );
|
||||||
mbedtls_ctr_drbg_free( &drbg_ctx );
|
mbedtls_ctr_drbg_free( &drbg_ctx );
|
||||||
mbedtls_lms_free( &ctx );
|
mbedtls_lms_free_public( &pub_ctx );
|
||||||
|
mbedtls_lms_free_private( &priv_ctx );
|
||||||
}
|
}
|
||||||
/* END_CASE */
|
/* END_CASE */
|
||||||
|
|
||||||
@ -52,34 +59,35 @@ exit:
|
|||||||
void lms_verify_test ( data_t * msg, data_t * sig, data_t * pub_key,
|
void lms_verify_test ( data_t * msg, data_t * sig, data_t * pub_key,
|
||||||
int expected_rc )
|
int expected_rc )
|
||||||
{
|
{
|
||||||
mbedtls_lms_context ctx;
|
mbedtls_lms_public_t ctx;
|
||||||
|
|
||||||
mbedtls_lms_init( &ctx);
|
mbedtls_lms_init_public( &ctx);
|
||||||
|
|
||||||
mbedtls_lms_import_pubkey( &ctx, pub_key->x );
|
mbedtls_lms_import_public_key( &ctx, pub_key->x, pub_key->len );
|
||||||
|
|
||||||
TEST_ASSERT( mbedtls_lms_verify( &ctx, msg->x, msg->len, sig->x ) == expected_rc );
|
TEST_ASSERT( mbedtls_lms_verify( &ctx, msg->x, msg->len, sig->x, sig->len ) == expected_rc );
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mbedtls_lms_free( &ctx );
|
mbedtls_lms_free_public( &ctx );
|
||||||
}
|
}
|
||||||
/* END_CASE */
|
/* END_CASE */
|
||||||
|
|
||||||
/* BEGIN_CASE */
|
/* BEGIN_CASE */
|
||||||
void lms_import_export_test ( data_t * pub_key )
|
void lms_import_export_test ( data_t * pub_key )
|
||||||
{
|
{
|
||||||
mbedtls_lms_context ctx;
|
mbedtls_lms_public_t ctx;
|
||||||
uint8_t exported_pub_key[MBEDTLS_LMS_PUBKEY_LEN];
|
uint8_t exported_pub_key[MBEDTLS_LMS_PUBLIC_KEY_LEN];
|
||||||
|
|
||||||
mbedtls_lms_init(&ctx);
|
mbedtls_lms_init_public(&ctx);
|
||||||
TEST_ASSERT( mbedtls_lms_import_pubkey( &ctx, pub_key->x ) == 0 );
|
TEST_ASSERT( mbedtls_lms_import_public_key( &ctx, pub_key->x, pub_key->len ) == 0 );
|
||||||
TEST_ASSERT( mbedtls_lms_export_pubkey( &ctx, exported_pub_key) == 0 );
|
TEST_ASSERT( mbedtls_lms_export_public_key( &ctx, exported_pub_key,
|
||||||
|
sizeof(exported_pub_key), NULL ) == 0 );
|
||||||
|
|
||||||
ASSERT_COMPARE( pub_key->x, MBEDTLS_LMS_PUBKEY_LEN,
|
ASSERT_COMPARE( pub_key->x, MBEDTLS_LMS_PUBLIC_KEY_LEN,
|
||||||
exported_pub_key, MBEDTLS_LMS_PUBKEY_LEN );
|
exported_pub_key, MBEDTLS_LMS_PUBLIC_KEY_LEN );
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mbedtls_lms_free( &ctx );
|
mbedtls_lms_free_public( &ctx );
|
||||||
}
|
}
|
||||||
/* END_CASE */
|
/* END_CASE */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user