Files
MAX_CARLINK_A270S/MXC_A27-PCB4.5-270T/lib/wolfssl/wolfcrypt/src/wc_lms.c
2025-01-21 16:49:37 +08:00

1146 lines
34 KiB
C

/* wc_lms.c
*
* Copyright (C) 2006-2024 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/logging.h>
#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS)
#include <wolfssl/wolfcrypt/wc_lms.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
/* Calculate u. Appendix B. Works for w of 1, 2, 4, or 8.
*
* @param [in] w Winternitz width.
*/
#define LMS_U(w) \
(8 * WC_SHA256_DIGEST_SIZE / (w))
/* Calculate u. Appendix B. Works for w of 1, 2, 4, or 8.
*
* @param [in] w Winternitz width.
* @param [in] wb Winternitz width length in bits.
*/
#define LMS_V(w, wb) \
(2 + (8 - (wb)) / (w))
/* Calculate ls. Appendix B. Works for w of 1, 2, 4, or 8.
*
* @param [in] w Winternitz width.
* @param [in] wb Winternitz width length in bits.
*/
#define LMS_LS(w, wb) \
(16 - LMS_V(w, wb) * (w))
/* Calculate p. Appendix B. Works for w of 1, 2, 4, or 8.
*
* @param [in] w Winternitz width.
* @param [in] wb Winternitz width length in bits.
*/
#define LMS_P(w, wb) \
(LMS_U(w) + LMS_V(w, wb))
/* Calculate signature length.
*
* @param [in] l Number of levels.
* @param [in] h Height of the trees.
* @param [in] p Number of n-byte string elements in signature for a tree.
*/
#define LMS_PARAMS_SIG_LEN(l, h, p) \
(4 + (l) * (4 + 4 + 4 + WC_SHA256_DIGEST_SIZE * (1 + (p) + (h))) + \
((l) - 1) * LMS_PUBKEY_LEN)
#ifndef WOLFSSL_WC_LMS_SMALL
/* Root levels and leaf cache bits. */
#define LMS_PARAMS_CACHE(h) \
(((h) < LMS_ROOT_LEVELS) ? (h) : LMS_ROOT_LEVELS), \
(((h) < LMS_CACHE_BITS ) ? (h) : LMS_CACHE_BITS )
#else
/* Root levels and leaf cache bits aren't in structure. */
#define LMS_PARAMS_CACHE(h) /* null expansion */
#endif
/* Define parameters entry for LMS.
*
* @param [in] l Number of levels.
* @param [in] h Height of the trees.
* @param [in] w Winternitz width.
* @param [in] wb Winternitz width length in bits.
* @param [in] t LMS type.
* @param [in] t2 LM-OTS type.
*/
#define LMS_PARAMS(l, h, w, wb, t, t2) \
{ l, h, w, LMS_LS(w, wb), LMS_P(w, wb), t, t2, \
LMS_PARAMS_SIG_LEN(l, h, LMS_P(w, wb)), LMS_PARAMS_CACHE(h) }
/* Initialize the working state for LMS operations.
*
* @param [in, out] state LMS state.
* @param [in] params LMS parameters.
*/
static int wc_lmskey_state_init(LmsState* state, const LmsParams* params)
{
int ret;
/* Zero out every field. */
XMEMSET(state, 0, sizeof(LmsState));
/* Keep a reference to the parameters for use in operations. */
state->params = params;
/* Initialize the two hash algorithms. */
ret = wc_InitSha256(&state->hash);
if (ret == 0) {
ret = wc_InitSha256(&state->hash_k);
if (ret != 0) {
wc_Sha256Free(&state->hash);
}
}
return ret;
}
/* Free the working state for LMS operations.
*
* @param [in] state LMS state.
*/
static void wc_lmskey_state_free(LmsState* state)
{
wc_Sha256Free(&state->hash_k);
wc_Sha256Free(&state->hash);
}
/* Supported LMS parameters. */
static const wc_LmsParamsMap wc_lms_map[] = {
#if LMS_MAX_HEIGHT >= 15
{ WC_LMS_PARM_NONE , "LMS_NONE" ,
LMS_PARAMS(1, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L1_H15_W2, "LMS/HSS L1_H15_W2",
LMS_PARAMS(1, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L1_H15_W4, "LMS/HSS L1_H15_W4",
LMS_PARAMS(1, 15, 4, 2, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W4) },
#endif
#if LMS_MAX_LEVELS >= 2
#if LMS_MAX_HEIGHT >= 10
{ WC_LMS_PARM_L2_H10_W2, "LMS/HSS L2_H10_W2",
LMS_PARAMS(2, 10, 2, 1, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L2_H10_W4, "LMS/HSS L2_H10_W4",
LMS_PARAMS(2, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L2_H10_W8, "LMS/HSS L2_H10_W8",
LMS_PARAMS(2, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
#endif
#endif
#if LMS_MAX_LEVELS >= 3
{ WC_LMS_PARM_L3_H5_W2 , "LMS/HSS L3_H5_W2" ,
LMS_PARAMS(3, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L3_H5_W4 , "LMS/HSS L3_H5_W4" ,
LMS_PARAMS(3, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L3_H5_W8 , "LMS/HSS L3_H5_W8" ,
LMS_PARAMS(3, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
#if LMS_MAX_HEIGHT >= 10
{ WC_LMS_PARM_L3_H10_W4, "LMS/HSS L3_H10_W4",
LMS_PARAMS(3, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
#endif
#endif
#if LMS_MAX_LEVELS >= 4
{ WC_LMS_PARM_L4_H5_W8 , "LMS/HSS L4_H5_W8" ,
LMS_PARAMS(4, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
#endif
/* For when user sets L, H, W explicitly. */
{ WC_LMS_PARM_L1_H5_W1 , "LMS/HSS_L1_H5_W1" ,
LMS_PARAMS(1, 5, 1, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W1) },
{ WC_LMS_PARM_L1_H5_W2 , "LMS/HSS_L1_H5_W2" ,
LMS_PARAMS(1, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L1_H5_W4 , "LMS/HSS_L1_H5_W4" ,
LMS_PARAMS(1, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L1_H5_W8 , "LMS/HSS_L1_H5_W8" ,
LMS_PARAMS(1, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
#if LMS_MAX_HEIGHT >= 10
{ WC_LMS_PARM_L1_H10_W2 , "LMS/HSS_L1_H10_W2",
LMS_PARAMS(1, 10, 2, 1, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L1_H10_W4 , "LMS/HSS_L1_H10_W4",
LMS_PARAMS(1, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L1_H10_W8 , "LMS/HSS_L1_H10_W8",
LMS_PARAMS(1, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
#endif
#if LMS_MAX_HEIGHT >= 15
{ WC_LMS_PARM_L1_H15_W8 , "LMS/HSS L1_H15_W8",
LMS_PARAMS(1, 15, 8, 3, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W8) },
#endif
#if LMS_MAX_HEIGHT >= 20
{ WC_LMS_PARM_L1_H20_W2 , "LMS/HSS_L1_H20_W2",
LMS_PARAMS(1, 20, 2, 1, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L1_H20_W4 , "LMS/HSS_L1_H20_W4",
LMS_PARAMS(1, 20, 4, 2, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L1_H20_W8 , "LMS/HSS_L1_H20_W8",
LMS_PARAMS(1, 20, 8, 3, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W8) },
#endif
#if LMS_MAX_LEVELS >= 2
{ WC_LMS_PARM_L2_H5_W2 , "LMS/HSS_L2_H5_W2" ,
LMS_PARAMS(2, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L2_H5_W4 , "LMS/HSS_L2_H5_W4" ,
LMS_PARAMS(2, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L2_H5_W8 , "LMS/HSS_L2_H5_W8" ,
LMS_PARAMS(2, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
#if LMS_MAX_HEIGHT >= 15
{ WC_LMS_PARM_L2_H15_W2 , "LMS/HSS_L2_H15_W2",
LMS_PARAMS(2, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L2_H15_W4 , "LMS/HSS_L2_H15_W4",
LMS_PARAMS(2, 15, 4, 2, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L2_H15_W8 , "LMS/HSS_L2_H15_W8",
LMS_PARAMS(2, 15, 8, 3, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W8) },
#endif
#if LMS_MAX_HEIGHT >= 20
{ WC_LMS_PARM_L2_H20_W2 , "LMS/HSS_L2_H20_W2",
LMS_PARAMS(2, 20, 2, 1, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L2_H20_W4 , "LMS/HSS_L2_H20_W4",
LMS_PARAMS(2, 20, 4, 2, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L2_H20_W8 , "LMS/HSS_L2_H20_W8",
LMS_PARAMS(2, 20, 8, 3, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W8) },
#endif
#endif
#if LMS_MAX_LEVELS >= 3
#if LMS_MAX_HEIGHT >= 10
{ WC_LMS_PARM_L3_H10_W8 , "LMS/HSS L3_H10_W8",
LMS_PARAMS(3, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
#endif
#endif
#if LMS_MAX_LEVELS >= 4
{ WC_LMS_PARM_L4_H5_W2 , "LMS/HSS L4_H5_W2" ,
LMS_PARAMS(4, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
{ WC_LMS_PARM_L4_H5_W4 , "LMS/HSS L4_H5_W4" ,
LMS_PARAMS(4, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
#if LMS_MAX_HEIGHT >= 10
{ WC_LMS_PARM_L4_H10_W4 , "LMS/HSS L4_H10_W4",
LMS_PARAMS(4, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
{ WC_LMS_PARM_L4_H10_W8 , "LMS/HSS L4_H10_W8",
LMS_PARAMS(4, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
#endif
#endif
};
/* Number of parameter sets supported. */
#define WC_LMS_MAP_LEN ((int)(sizeof(wc_lms_map) / sizeof(*wc_lms_map)))
/* Initialize LMS key.
*
* Call this before setting the params of an LMS key.
*
* @param [out] key LMS key to initialize.
* @param [in] heap Heap hint.
* @param [in] devId Device identifier.
* Use INVALID_DEVID when not using a device.
* @return 0 on success.
* @return BAD_FUNC_ARG when key is NULL.
*/
int wc_LmsKey_Init(LmsKey* key, void* heap, int devId)
{
int ret = 0;
(void)heap;
(void)devId;
/* Validate parameters. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
/* Zeroize the key data. */
ForceZero(key, sizeof(LmsKey));
#ifndef WOLFSSL_LMS_VERIFY_ONLY
/* Initialize other fields. */
key->write_private_key = NULL;
key->read_private_key = NULL;
key->context = NULL;
key->heap = heap;
#endif
#ifdef WOLF_CRYPTO_CB
key->devId = devId;
#endif
/* Start in initialized state. */
key->state = WC_LMS_STATE_INITED;
}
return ret;
}
/* Get the string representation of the LMS parameter set.
*
* @param [in] lmsParm LMS parameter set identifier.
* @return String representing LMS parameter set on success.
* @return NULL when parameter set not supported.
*/
const char* wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm)
{
const char* str = NULL;
int i;
/* Search through table for matching numeric identifier. */
for (i = 0; i < WC_LMS_MAP_LEN; i++) {
if (lmsParm == wc_lms_map[i].id) {
/* Get string corresponding to numeric identifier. */
str = wc_lms_map[i].str;
break;
}
}
/* Return the string or NULL. */
return str;
}
/* Set the wc_LmsParm of an LMS key.
*
* Use this if you wish to set a key with a predefined parameter set,
* such as WC_LMS_PARM_L2_H10_W8.
*
* Key must be inited before calling this.
*
* @param [in, out] key LMS key to set parameters on.
* @param [in] lmsParm Identifier of parameters.
* @return 0 on success.
* @return BAD_FUNC_ARG when key is NULL.
* @return BAD_FUNC_ARG when parameters not supported.
*/
int wc_LmsKey_SetLmsParm(LmsKey* key, enum wc_LmsParm lmsParm)
{
int ret = 0;
/* Validate parameters. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
/* Check state is valid. */
if ((ret == 0) && (key->state != WC_LMS_STATE_INITED)) {
WOLFSSL_MSG("error: LmsKey needs init");
ret = BAD_STATE_E;
}
if (ret == 0) {
int i;
ret = BAD_FUNC_ARG;
/* Search through table for matching numeric identifier. */
for (i = 0; i < WC_LMS_MAP_LEN; i++) {
if (lmsParm == wc_lms_map[i].id) {
/* Set the parameters into the key. */
key->params = &wc_lms_map[i].params;
ret = 0;
break;
}
}
}
if (ret == 0) {
/* Move the state to params set.
* Key is ready for MakeKey or Reload. */
key->state = WC_LMS_STATE_PARMSET;
}
return ret;
}
/* Set the parameters of an LMS key.
*
* Use this if you wish to set specific parameters not found in the
* wc_LmsParm predefined sets. See comments in lms.h for allowed
* parameters.
*
* Key must be inited before calling this.
*
* @param [in, out] key LMS key to set parameters on.
* @param [in] levels Number of tree levels.
* @param [in] height Height of each tree.
* @param [in] winternitz Width or Winternitz coefficient.
* @return 0 on success.
* @return BAD_FUNC_ARG when key is NULL.
* @return BAD_FUNC_ARG when parameters not supported.
* */
int wc_LmsKey_SetParameters(LmsKey* key, int levels, int height,
int winternitz)
{
int ret = 0;
/* Validate parameters. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
/* Check state is valid. */
if ((ret == 0) && (key->state != WC_LMS_STATE_INITED)) {
WOLFSSL_MSG("error: LmsKey needs init");
ret = BAD_STATE_E;
}
if (ret == 0) {
int i;
ret = BAD_FUNC_ARG;
/* Search through table for matching levels, height and width. */
for (i = 0; i < WC_LMS_MAP_LEN; i++) {
if ((levels == wc_lms_map[i].params.levels) &&
(height == wc_lms_map[i].params.height) &&
(winternitz == wc_lms_map[i].params.width)) {
/* Set the parameters into the key. */
key->params = &wc_lms_map[i].params;
ret = 0;
break;
}
}
}
if (ret == 0) {
/* Move the state to params set.
* Key is ready for MakeKey or Reload. */
key->state = WC_LMS_STATE_PARMSET;
}
return ret;
}
/* Get the parameters of an LMS key.
*
* Key must be inited and parameters set before calling this.
*
* @param [in] key LMS key.
* @param [out] levels Number of levels of trees.
* @param [out] height Height of the trees.
* @param [out] winternitz Winternitz width.
* Returns 0 on success.
* */
int wc_LmsKey_GetParameters(const LmsKey* key, int* levels, int* height,
int* winternitz)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (levels == NULL) || (height == NULL) ||
(winternitz == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Validate the parameters are available. */
if ((ret == 0) && (key->params == NULL)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
/* Set the levels, height and Winternitz width from parameters. */
*levels = key->params->levels;
*height = key->params->height;
*winternitz = key->params->width;
}
return ret;
}
/* Frees the LMS key from memory.
*
* This does not affect the private key saved to non-volatile storage.
*
* @param [in, out] key LMS key to free.
*/
void wc_LmsKey_Free(LmsKey* key)
{
if (key != NULL) {
#ifndef WOLFSSL_LMS_VERIFY_ONLY
if (key->priv_data != NULL) {
const LmsParams* params = key->params;
ForceZero(key->priv_data, LMS_PRIV_DATA_LEN(params->levels,
params->height, params->p, params->rootLevels,
params->cacheBits));
XFREE(key->priv_data, key->heap, DYNAMIC_TYPE_LMS);
}
#endif
ForceZero(key, sizeof(LmsKey));
key->state = WC_LMS_STATE_FREED;
}
}
#ifndef WOLFSSL_LMS_VERIFY_ONLY
/* Set the write private key callback to the LMS key structure.
*
* The callback must be able to write/update the private key to
* non-volatile storage.
*
* @param [in, out] key LMS key.
* @param [in] write_cb Callback function that stores private key.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or write_cb is NULL.
* @return BAD_STATE_E when key state is invalid.
*/
int wc_LmsKey_SetWriteCb(LmsKey* key, wc_lms_write_private_key_cb write_cb)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (write_cb == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Changing the write callback of an already working key is forbidden. */
if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) {
WOLFSSL_MSG("error: wc_LmsKey_SetWriteCb: key in use");
ret = BAD_STATE_E;
}
if (ret == 0) {
/* Set the callback into the key. */
key->write_private_key = write_cb;
}
return ret;
}
/* Set the read private key callback to the LMS key structure.
*
* The callback must be able to read the private key from
* non-volatile storage.
*
* @param [in, out] key LMS key.
* @param [in] read_cb Callback function that loads private key.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or read_cb is NULL.
* @return BAD_STATE_E when key state is invalid.
* */
int wc_LmsKey_SetReadCb(LmsKey* key, wc_lms_read_private_key_cb read_cb)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (read_cb == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Changing the read callback of an already working key is forbidden. */
if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) {
WOLFSSL_MSG("error: wc_LmsKey_SetReadCb: key in use");
ret = BAD_STATE_E;
}
if (ret == 0) {
/* Set the callback into the key. */
key->read_private_key = read_cb;
}
return ret;
}
/* Sets the context to be used by write and read callbacks.
*
* E.g. this could be a filename if the callbacks write/read to file.
*
* @param [in, out] key LMS key.
* @param [in] context Pointer to data for read/write callbacks.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or context is NULL.
* @return BAD_STATE_E when key state is invalid.
* */
int wc_LmsKey_SetContext(LmsKey* key, void* context)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (context == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Setting context of an already working key is forbidden. */
if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) {
WOLFSSL_MSG("error: wc_LmsKey_SetContext: key in use");
ret = BAD_STATE_E;
}
if (ret == 0) {
/* Set the callback context into the key. */
key->context = context;
}
return ret;
}
/* Make the LMS private/public key pair. The key must have its parameters
* set before calling this.
*
* Write/read callbacks, and context data, must be set prior.
* Key must have parameters set.
*
* @param [in, out] key LMS key.
* @param [in] rng Random number generator.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or rng is NULL.
* @return BAD_STATE_E when key is in an invalid state.
* @return BAD_FUNC_ARG when write callback or callback context not set.
* @return BAD_STATE_E when no more signatures can be created.
*/
int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (rng == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Check state. */
if ((ret == 0) && (key->state != WC_LMS_STATE_PARMSET)) {
WOLFSSL_MSG("error: LmsKey not ready for generation");
ret = BAD_STATE_E;
}
/* Check write callback set. */
if ((ret == 0) && (key->write_private_key == NULL)) {
WOLFSSL_MSG("error: LmsKey write callback is not set");
ret = BAD_FUNC_ARG;
}
/* Check callback context set. */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: LmsKey context is not set");
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (key->priv_data == NULL)) {
const LmsParams* params = key->params;
/* Allocate memory for the private key data. */
key->priv_data = XMALLOC(LMS_PRIV_DATA_LEN(params->levels,
params->height, params->p, params->rootLevels, params->cacheBits),
key->heap, DYNAMIC_TYPE_LMS);
/* Check pointer is valid. */
if (key->priv_data == NULL) {
ret = MEMORY_E;
}
}
if (ret == 0) {
#ifdef WOLFSSL_SMALL_STACK
LmsState* state;
#else
LmsState state[1];
#endif
#ifdef WOLFSSL_SMALL_STACK
/* Allocate memory for working state. */
state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (state == NULL) {
ret = MEMORY_E;
}
if (ret == 0)
#endif
{
/* Initialize working state for use. */
ret = wc_lmskey_state_init(state, key->params);
if (ret == 0) {
/* Make the HSS key. */
ret = wc_hss_make_key(state, rng, key->priv_raw, &key->priv,
key->priv_data, key->pub);
wc_lmskey_state_free(state);
}
ForceZero(state, sizeof(LmsState));
#ifdef WOLFSSL_SMALL_STACK
XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
}
}
if (ret == 0) {
/* Write private key to storage. */
int rv = key->write_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN,
key->context);
if (rv != WC_LMS_RC_SAVED_TO_NV_MEMORY) {
ret = IO_FAILED_E;
}
}
/* This should not happen, but check whether signatures can be created. */
if ((ret == 0) && (wc_LmsKey_SigsLeft(key) == 0)) {
WOLFSSL_MSG("error: generated LMS key signatures exhausted");
key->state = WC_LMS_STATE_NOSIGS;
ret = BAD_STATE_E;
}
if (ret == 0) {
/* Update state. */
key->state = WC_LMS_STATE_OK;
}
return ret;
}
/* Reload a key that has been prepared with the appropriate params and
* data. Use this if you wish to resume signing with an existing key.
*
* Write/read callbacks, and context data, must be set prior.
* Key must have parameters set.
*
* @param [in, out] key LMS key.
*
* Returns 0 on success. */
int wc_LmsKey_Reload(LmsKey* key)
{
int ret = 0;
/* Validate parameter. */
if (key == NULL) {
ret = BAD_FUNC_ARG;
}
/* Check state. */
if ((ret == 0) && (key->state != WC_LMS_STATE_PARMSET)) {
WOLFSSL_MSG("error: LmsKey not ready for reload");
ret = BAD_STATE_E;
}
/* Check read callback present. */
if ((ret == 0) && (key->read_private_key == NULL)) {
WOLFSSL_MSG("error: LmsKey read callback is not set");
ret = BAD_FUNC_ARG;
}
/* Check context for callback set */
if ((ret == 0) && (key->context == NULL)) {
WOLFSSL_MSG("error: LmsKey context is not set");
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (key->priv_data == NULL)) {
const LmsParams* params = key->params;
/* Allocate memory for the private key data. */
key->priv_data = XMALLOC(LMS_PRIV_DATA_LEN(params->levels,
params->height, params->p, params->rootLevels, params->cacheBits),
key->heap, DYNAMIC_TYPE_LMS);
/* Check pointer is valid. */
if (key->priv_data == NULL) {
ret = MEMORY_E;
}
}
if (ret == 0) {
/* Load private key. */
int rv = key->read_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN,
key->context);
if (rv != WC_LMS_RC_READ_TO_MEMORY) {
ret = IO_FAILED_E;
}
}
/* Double check the key actually has signatures left. */
if ((ret == 0) && (wc_LmsKey_SigsLeft(key) == 0)) {
WOLFSSL_MSG("error: reloaded LMS key signatures exhausted");
key->state = WC_LMS_STATE_NOSIGS;
ret = BAD_STATE_E;
}
if (ret == 0) {
#ifdef WOLFSSL_SMALL_STACK
LmsState* state;
#else
LmsState state[1];
#endif
#ifdef WOLFSSL_SMALL_STACK
/* Allocate memory for working state. */
state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (state == NULL) {
ret = MEMORY_E;
}
if (ret == 0)
#endif
{
/* Initialize working state for use. */
ret = wc_lmskey_state_init(state, key->params);
if (ret == 0) {
/* Reload the key ready for signing. */
ret = wc_hss_reload_key(state, key->priv_raw, &key->priv,
key->priv_data, NULL);
}
ForceZero(state, sizeof(LmsState));
#ifdef WOLFSSL_SMALL_STACK
XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
}
}
if (ret == 0) {
/* Update state. */
key->state = WC_LMS_STATE_OK;
}
return ret;
}
/* Get the private key length based on parameter set of key.
*
* @param [in] key LMS key.
* @param [out] len Length of private key.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or len is NULL or parameters not set.
*/
int wc_LmsKey_GetPrivLen(const LmsKey* key, word32* len)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
/* Return private key length from parameter set. */
*len = HSS_PRIVATE_KEY_LEN;
}
return ret;
}
/* Sign a message.
*
* @param [in, out] key LMS key to sign with.
* @param [out] sig Signature data. Buffer must be big enough to hold
* signature data.
* @param [out] sigSz Length of signature data.
* @param [in] msg Message to sign.
* @param [in] msgSz Length of message in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when key, sig, sigSz or msg is NULL.
* @return BAD_FUNC_ARG when msgSz is not greater than 0.
*/
int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg,
int msgSz)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (sig == NULL) || (sigSz == NULL) || (msg == NULL)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (msgSz <= 0)) {
ret = BAD_FUNC_ARG;
}
/* Check state. */
if ((ret == 0) && (key->state == WC_LMS_STATE_NOSIGS)) {
WOLFSSL_MSG("error: LMS signatures exhausted");
ret = BAD_STATE_E;
}
if ((ret == 0) && (key->state != WC_LMS_STATE_OK)) {
/* The key had an error the last time it was used, and we
* can't guarantee its state. */
WOLFSSL_MSG("error: can't sign, LMS key not in good state");
ret = BAD_STATE_E;
}
if (ret == 0) {
#ifdef WOLFSSL_SMALL_STACK
LmsState* state;
#else
LmsState state[1];
#endif
#ifdef WOLFSSL_SMALL_STACK
/* Allocate memory for working state. */
state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (state == NULL) {
ret = MEMORY_E;
}
if (ret == 0)
#endif
{
/* Initialize working state for use. */
ret = wc_lmskey_state_init(state, key->params);
if (ret == 0) {
/* Sign message. */
ret = wc_hss_sign(state, key->priv_raw, &key->priv,
key->priv_data, msg, msgSz, sig);
wc_lmskey_state_free(state);
}
ForceZero(state, sizeof(LmsState));
#ifdef WOLFSSL_SMALL_STACK
XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
}
}
if (ret == 0) {
*sigSz = (word32)key->params->sig_len;
}
if (ret == 0) {
/* Write private key to storage. */
int rv = key->write_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN,
key->context);
if (rv != WC_LMS_RC_SAVED_TO_NV_MEMORY) {
ret = IO_FAILED_E;
}
}
return ret;
}
/* Returns whether signatures can be created with key.
*
* @param [in] key LMS key.
*
* @return 1 if there are signatures remaining.
* @return 0 if available signatures are exhausted.
*/
int wc_LmsKey_SigsLeft(LmsKey* key)
{
int ret = 0;
/* NULL keys have no signatures remaining. */
if (key != NULL) {
ret = wc_hss_sigsleft(key->params, key->priv_raw);
}
return ret;
}
#endif /* ifndef WOLFSSL_LMS_VERIFY_ONLY*/
/* Get the public key length based on parameter set of key.
*
* @param [in] key LMS key.
* @param [out] len Length of public key.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or len is NULL or parameters not set.
*/
int wc_LmsKey_GetPubLen(const LmsKey* key, word32* len)
{
int ret = 0;
/* Validate parameters */
if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
*len = HSS_PUBLIC_KEY_LEN;
}
return ret;
}
/* Export a generated public key and parameter set from one LmsKey
* to another. Use this to prepare a signature verification LmsKey
* that is pub only.
*
* Though the public key is all that is used to verify signatures,
* the parameter set is needed to calculate the signature length
* before hand.
*
* @param [out] keyDst LMS key to copy into.
* @param [in] keySrc LMS key to copy.
* @return 0 on success.
* @return BAD_FUNC_ARG when keyDst or keySrc is NULL.
*/
int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc)
{
int ret = 0;
if ((keyDst == NULL) || (keySrc == NULL)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
ForceZero(keyDst, sizeof(LmsKey));
keyDst->params = keySrc->params;
XMEMCPY(keyDst->pub, keySrc->pub, sizeof(keySrc->pub));
/* Mark this key as verify only, to prevent misuse. */
keyDst->state = WC_LMS_STATE_VERIFYONLY;
}
return ret;
}
/* Exports the raw LMS public key buffer from key to out buffer.
* The out buffer should be large enough to hold the public key, and
* outLen should indicate the size of the buffer.
*
* Call wc_LmsKey_GetPubLen beforehand to determine pubLen.
*
* @param [in] key LMS key.
* @param [out] out Buffer to hold encoded public key.
* @param [in, out] outLen On in, length of out in bytes.
* On out, the length of the public key in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when key, out or outLen is NULL.
* @return BUFFER_E when outLen is too small to hold encoded public key.
*/
int wc_LmsKey_ExportPubRaw(const LmsKey* key, byte* out, word32* outLen)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (out == NULL) || (outLen == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Check size of out is sufficient. */
if ((ret == 0) && (*outLen < HSS_PUBLIC_KEY_LEN)) {
ret = BUFFER_E;
}
if (ret == 0) {
/* Return encoded public key. */
XMEMCPY(out, key->pub, HSS_PUBLIC_KEY_LEN);
*outLen = HSS_PUBLIC_KEY_LEN;
}
return ret;
}
/* Imports a raw public key buffer from in array to LmsKey key.
*
* The LMS parameters must be set first with wc_LmsKey_SetLmsParm or
* wc_LmsKey_SetParameters, and inLen must match the length returned
* by wc_LmsKey_GetPubLen.
*
* Call wc_LmsKey_GetPubLen beforehand to determine pubLen.
*
* @param [in, out] key LMS key to put public key in.
* @param [in] in Buffer holding encoded public key.
* @param [in] inLen Length of encoded public key in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or in is NULL.
* @return BUFFER_E when inLen does not match public key length by parameters.
*/
int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (in == NULL)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (inLen != HSS_PUBLIC_KEY_LEN)) {
/* Something inconsistent. Parameters weren't set, or input
* pub key is wrong.*/
return BUFFER_E;
}
if (ret == 0) {
XMEMCPY(key->pub, in, inLen);
key->state = WC_LMS_STATE_VERIFYONLY;
}
return ret;
}
/* Given a levels, height, winternitz parameter set, determine
* the signature length.
*
* Call this before wc_LmsKey_Sign so you know the length of
* the required signature buffer.
*
* @param [in] key LMS key.
* @param [out] len Length of a signature in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when key or len is NULL.
*/
int wc_LmsKey_GetSigLen(const LmsKey* key, word32* len)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (len == NULL)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
*len = key->params->sig_len;
}
return ret;
}
/* Verify the signature of the message with public key.
*
* @param [in] key LMS key.
* @param [in] sig Signature to verify.
* @param [in] sigSz Size of signature in bytes.
* @param [in] msg Message to verify.
* @param [in] msgSz Length of the message in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when a key, sig or msg is NULL.
* @return SIG_VERIFY_E when signature did not verify message.
* @return BAD_STATE_E when wrong state for operation.
* @return BUFFER_E when sigSz is invalid for parameters.
*/
int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz,
const byte* msg, int msgSz)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (sig == NULL) || (msg == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Check state. */
if ((ret == 0) && (key->state != WC_LMS_STATE_OK) &&
(key->state != WC_LMS_STATE_VERIFYONLY)) {
/* LMS key not ready for verification. Param str must be
* set first, and Reload() called. */
WOLFSSL_MSG("error: LMS key not ready for verification");
ret = BAD_STATE_E;
}
/* Check signature length. */
if ((ret == 0) && (sigSz != key->params->sig_len)) {
ret = BUFFER_E;
}
if (ret == 0) {
#ifdef WOLFSSL_SMALL_STACK
LmsState* state;
#else
LmsState state[1];
#endif
#ifdef WOLFSSL_SMALL_STACK
/* Allocate memory for working state. */
state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (state == NULL) {
ret = MEMORY_E;
}
if (ret == 0)
#endif
{
/* Initialize working state for use. */
ret = wc_lmskey_state_init(state, key->params);
if (ret == 0) {
/* Verify signature of message with public key. */
ret = wc_hss_verify(state, key->pub, msg, msgSz, sig);
wc_lmskey_state_free(state);
}
ForceZero(state, sizeof(LmsState));
#ifdef WOLFSSL_SMALL_STACK
XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
}
}
return ret;
}
#endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS */