mbedtls/library/lmots.c
Agathiyan Bragadeesh 10b6775aeb Add enum type casts in lmots.c and lms.c
The IAR compiler throws an error when trying to assign an int to an enum
so these casts have been added.

Signed-off-by: Agathiyan Bragadeesh <agathiyan.bragadeesh2@arm.com>
2023-07-17 15:14:42 +01:00

833 lines
29 KiB
C

/*
* The LM-OTS one-time public-key signature scheme
*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* The following sources were referenced in the design of this implementation
* of the LM-OTS algorithm:
*
* [1] IETF RFC8554
* D. McGrew, M. Curcio, S.Fluhrer
* https://datatracker.ietf.org/doc/html/rfc8554
*
* [2] NIST Special Publication 800-208
* David A. Cooper et. al.
* https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf
*/
#include "common.h"
#if defined(MBEDTLS_LMS_C)
#include <string.h>
#include "lmots.h"
#include "mbedtls/lms.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/error.h"
#include "mbedtls/psa_util.h"
#include "psa/crypto.h"
/* Define a local translating function to save code size by not using too many
* arguments in each translating place. */
static int local_err_translation(psa_status_t status)
{
return psa_status_to_mbedtls(status, psa_to_lms_errors,
ARRAY_LENGTH(psa_to_lms_errors),
psa_generic_status_to_mbedtls);
}
#define PSA_TO_MBEDTLS_ERR(status) local_err_translation(status)
#define PUBLIC_KEY_TYPE_OFFSET (0)
#define PUBLIC_KEY_I_KEY_ID_OFFSET (PUBLIC_KEY_TYPE_OFFSET + \
MBEDTLS_LMOTS_TYPE_LEN)
#define PUBLIC_KEY_Q_LEAF_ID_OFFSET (PUBLIC_KEY_I_KEY_ID_OFFSET + \
MBEDTLS_LMOTS_I_KEY_ID_LEN)
#define PUBLIC_KEY_KEY_HASH_OFFSET (PUBLIC_KEY_Q_LEAF_ID_OFFSET + \
MBEDTLS_LMOTS_Q_LEAF_ID_LEN)
/* We only support parameter sets that use 8-bit digits, as it does not require
* translation logic between digits and bytes */
#define W_WINTERNITZ_PARAMETER (8u)
#define CHECKSUM_LEN (2)
#define I_DIGIT_IDX_LEN (2)
#define J_HASH_IDX_LEN (1)
#define D_CONST_LEN (2)
#define DIGIT_MAX_VALUE ((1u << W_WINTERNITZ_PARAMETER) - 1u)
#define D_CONST_LEN (2)
static const unsigned char D_PUBLIC_CONSTANT_BYTES[D_CONST_LEN] = { 0x80, 0x80 };
static const unsigned char D_MESSAGE_CONSTANT_BYTES[D_CONST_LEN] = { 0x81, 0x81 };
#if defined(MBEDTLS_TEST_HOOKS)
int (*mbedtls_lmots_sign_private_key_invalidated_hook)(unsigned char *) = NULL;
#endif /* defined(MBEDTLS_TEST_HOOKS) */
void mbedtls_lms_unsigned_int_to_network_bytes(unsigned int val, size_t len,
unsigned char *bytes)
{
size_t idx;
for (idx = 0; idx < len; idx++) {
bytes[idx] = (val >> ((len - 1 - idx) * 8)) & 0xFF;
}
}
unsigned int mbedtls_lms_network_bytes_to_unsigned_int(size_t len,
const unsigned char *bytes)
{
size_t idx;
unsigned int val = 0;
for (idx = 0; idx < len; idx++) {
val |= ((unsigned int) bytes[idx]) << (8 * (len - 1 - idx));
}
return val;
}
/* Calculate the checksum digits that are appended to the end of the LMOTS digit
* string. See NIST SP800-208 section 3.1 or RFC8554 Algorithm 2 for details of
* the checksum algorithm.
*
* params The LMOTS parameter set, I and q values which
* describe the key being used.
*
* digest The digit string to create the digest from. As
* this does not contain a checksum, it is the same
* size as a hash output.
*/
static unsigned short lmots_checksum_calculate(const mbedtls_lmots_parameters_t *params,
const unsigned char *digest)
{
size_t idx;
unsigned sum = 0;
for (idx = 0; idx < MBEDTLS_LMOTS_N_HASH_LEN(params->type); idx++) {
sum += DIGIT_MAX_VALUE - digest[idx];
}
return sum;
}
/* Create the string of digest digits (in the base determined by the Winternitz
* parameter with the checksum appended to the end (Q || cksm(Q)). See NIST
* SP800-208 section 3.1 or RFC8554 Algorithm 3 step 5 (also used in Algorithm
* 4b step 3) for details.
*
* params The LMOTS parameter set, I and q values which
* describe the key being used.
*
* msg The message that will be hashed to create the
* digest.
*
* msg_size The size of the message.
*
* C_random_value The random value that will be combined with the
* message digest. This is always the same size as a
* hash output for whichever hash algorithm is
* determined by the parameter set.
*
* output An output containing the digit string (+
* checksum) of length P digits (in the case of
* MBEDTLS_LMOTS_SHA256_N32_W8, this means it is of
* size P bytes).
*/
static int create_digit_array_with_checksum(const mbedtls_lmots_parameters_t *params,
const unsigned char *msg,
size_t msg_len,
const unsigned char *C_random_value,
unsigned char *out)
{
psa_hash_operation_t op = PSA_HASH_OPERATION_INIT;
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t output_hash_len;
unsigned short checksum;
status = psa_hash_setup(&op, PSA_ALG_SHA_256);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, params->I_key_identifier,
MBEDTLS_LMOTS_I_KEY_ID_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, params->q_leaf_identifier,
MBEDTLS_LMOTS_Q_LEAF_ID_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, D_MESSAGE_CONSTANT_BYTES, D_CONST_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, C_random_value,
MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(params->type));
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, msg, msg_len);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_finish(&op, out,
MBEDTLS_LMOTS_N_HASH_LEN(params->type),
&output_hash_len);
if (status != PSA_SUCCESS) {
goto exit;
}
checksum = lmots_checksum_calculate(params, out);
mbedtls_lms_unsigned_int_to_network_bytes(checksum, CHECKSUM_LEN,
out + MBEDTLS_LMOTS_N_HASH_LEN(params->type));
exit:
psa_hash_abort(&op);
return PSA_TO_MBEDTLS_ERR(status);
}
/* Hash each element of the string of digits (+ checksum), producing a hash
* output for each element. This is used in several places (by varying the
* hash_idx_min/max_values) in order to calculate a public key from a private
* key (RFC8554 Algorithm 1 step 4), in order to sign a message (RFC8554
* Algorithm 3 step 5), and to calculate a public key candidate from a
* signature and message (RFC8554 Algorithm 4b step 3).
*
* params The LMOTS parameter set, I and q values which
* describe the key being used.
*
* x_digit_array The array of digits (of size P, 34 in the case of
* MBEDTLS_LMOTS_SHA256_N32_W8).
*
* hash_idx_min_values An array of the starting values of the j iterator
* for each of the members of the digit array. If
* this value in NULL, then all iterators will start
* at 0.
*
* hash_idx_max_values An array of the upper bound values of the j
* iterator for each of the members of the digit
* array. If this value in NULL, then iterator is
* bounded to be less than 2^w - 1 (255 in the case
* of MBEDTLS_LMOTS_SHA256_N32_W8)
*
* output An array containing a hash output for each member
* of the digit string P. In the case of
* MBEDTLS_LMOTS_SHA256_N32_W8, this is of size 32 *
* 34.
*/
static int hash_digit_array(const mbedtls_lmots_parameters_t *params,
const unsigned char *x_digit_array,
const unsigned char *hash_idx_min_values,
const unsigned char *hash_idx_max_values,
unsigned char *output)
{
unsigned int i_digit_idx;
unsigned char i_digit_idx_bytes[I_DIGIT_IDX_LEN];
unsigned int j_hash_idx;
unsigned char j_hash_idx_bytes[J_HASH_IDX_LEN];
unsigned int j_hash_idx_min;
unsigned int j_hash_idx_max;
psa_hash_operation_t op = PSA_HASH_OPERATION_INIT;
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t output_hash_len;
unsigned char tmp_hash[MBEDTLS_LMOTS_N_HASH_LEN_MAX];
for (i_digit_idx = 0;
i_digit_idx < MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(params->type);
i_digit_idx++) {
memcpy(tmp_hash,
&x_digit_array[i_digit_idx * MBEDTLS_LMOTS_N_HASH_LEN(params->type)],
MBEDTLS_LMOTS_N_HASH_LEN(params->type));
j_hash_idx_min = hash_idx_min_values != NULL ?
hash_idx_min_values[i_digit_idx] : 0;
j_hash_idx_max = hash_idx_max_values != NULL ?
hash_idx_max_values[i_digit_idx] : DIGIT_MAX_VALUE;
for (j_hash_idx = j_hash_idx_min;
j_hash_idx < j_hash_idx_max;
j_hash_idx++) {
status = psa_hash_setup(&op, PSA_ALG_SHA_256);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op,
params->I_key_identifier,
MBEDTLS_LMOTS_I_KEY_ID_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op,
params->q_leaf_identifier,
MBEDTLS_LMOTS_Q_LEAF_ID_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
mbedtls_lms_unsigned_int_to_network_bytes(i_digit_idx,
I_DIGIT_IDX_LEN,
i_digit_idx_bytes);
status = psa_hash_update(&op, i_digit_idx_bytes, I_DIGIT_IDX_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
mbedtls_lms_unsigned_int_to_network_bytes(j_hash_idx,
J_HASH_IDX_LEN,
j_hash_idx_bytes);
status = psa_hash_update(&op, j_hash_idx_bytes, J_HASH_IDX_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, tmp_hash,
MBEDTLS_LMOTS_N_HASH_LEN(params->type));
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_finish(&op, tmp_hash, sizeof(tmp_hash),
&output_hash_len);
if (status != PSA_SUCCESS) {
goto exit;
}
psa_hash_abort(&op);
}
memcpy(&output[i_digit_idx * MBEDTLS_LMOTS_N_HASH_LEN(params->type)],
tmp_hash, MBEDTLS_LMOTS_N_HASH_LEN(params->type));
}
exit:
psa_hash_abort(&op);
mbedtls_platform_zeroize(tmp_hash, sizeof(tmp_hash));
return PSA_TO_MBEDTLS_ERR(status);
}
/* Combine the hashes of the digit array into a public key. This is used in
* in order to calculate a public key from a private key (RFC8554 Algorithm 1
* step 4), and to calculate a public key candidate from a signature and message
* (RFC8554 Algorithm 4b step 3).
*
* params The LMOTS parameter set, I and q values which describe
* the key being used.
* y_hashed_digits The array of hashes, one hash for each digit of the
* symbol array (which is of size P, 34 in the case of
* MBEDTLS_LMOTS_SHA256_N32_W8)
*
* pub_key The output public key (or candidate public key in
* case this is being run as part of signature
* verification), in the form of a hash output.
*/
static int public_key_from_hashed_digit_array(const mbedtls_lmots_parameters_t *params,
const unsigned char *y_hashed_digits,
unsigned char *pub_key)
{
psa_hash_operation_t op = PSA_HASH_OPERATION_INIT;
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t output_hash_len;
status = psa_hash_setup(&op, PSA_ALG_SHA_256);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op,
params->I_key_identifier,
MBEDTLS_LMOTS_I_KEY_ID_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, params->q_leaf_identifier,
MBEDTLS_LMOTS_Q_LEAF_ID_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, D_PUBLIC_CONSTANT_BYTES, D_CONST_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, y_hashed_digits,
MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(params->type) *
MBEDTLS_LMOTS_N_HASH_LEN(params->type));
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_finish(&op, pub_key,
MBEDTLS_LMOTS_N_HASH_LEN(params->type),
&output_hash_len);
if (status != PSA_SUCCESS) {
exit:
psa_hash_abort(&op);
}
return PSA_TO_MBEDTLS_ERR(status);
}
#if !defined(MBEDTLS_DEPRECATED_REMOVED)
int mbedtls_lms_error_from_psa(psa_status_t status)
{
switch (status) {
case PSA_SUCCESS:
return 0;
case PSA_ERROR_HARDWARE_FAILURE:
return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
case PSA_ERROR_NOT_SUPPORTED:
return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
case PSA_ERROR_BUFFER_TOO_SMALL:
return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL;
case PSA_ERROR_INVALID_ARGUMENT:
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
default:
return MBEDTLS_ERR_ERROR_GENERIC_ERROR;
}
}
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
void mbedtls_lmots_public_init(mbedtls_lmots_public_t *ctx)
{
memset(ctx, 0, sizeof(*ctx));
}
void mbedtls_lmots_public_free(mbedtls_lmots_public_t *ctx)
{
mbedtls_platform_zeroize(ctx, sizeof(*ctx));
}
int mbedtls_lmots_import_public_key(mbedtls_lmots_public_t *ctx,
const unsigned char *key, size_t key_len)
{
if (key_len < MBEDTLS_LMOTS_SIG_TYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
ctx->params.type =
(mbedtls_lmots_algorithm_type_t) mbedtls_lms_network_bytes_to_unsigned_int(
MBEDTLS_LMOTS_TYPE_LEN,
key +
MBEDTLS_LMOTS_SIG_TYPE_OFFSET);
if (key_len != MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type)) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
memcpy(ctx->params.I_key_identifier,
key + PUBLIC_KEY_I_KEY_ID_OFFSET,
MBEDTLS_LMOTS_I_KEY_ID_LEN);
memcpy(ctx->params.q_leaf_identifier,
key + PUBLIC_KEY_Q_LEAF_ID_OFFSET,
MBEDTLS_LMOTS_Q_LEAF_ID_LEN);
memcpy(ctx->public_key,
key + PUBLIC_KEY_KEY_HASH_OFFSET,
MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type));
ctx->have_public_key = 1;
return 0;
}
int mbedtls_lmots_export_public_key(const mbedtls_lmots_public_t *ctx,
unsigned char *key, size_t key_size,
size_t *key_len)
{
if (key_size < MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type)) {
return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL;
}
if (!ctx->have_public_key) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
mbedtls_lms_unsigned_int_to_network_bytes(ctx->params.type,
MBEDTLS_LMOTS_TYPE_LEN,
key + MBEDTLS_LMOTS_SIG_TYPE_OFFSET);
memcpy(key + PUBLIC_KEY_I_KEY_ID_OFFSET,
ctx->params.I_key_identifier,
MBEDTLS_LMOTS_I_KEY_ID_LEN);
memcpy(key + PUBLIC_KEY_Q_LEAF_ID_OFFSET,
ctx->params.q_leaf_identifier,
MBEDTLS_LMOTS_Q_LEAF_ID_LEN);
memcpy(key + PUBLIC_KEY_KEY_HASH_OFFSET, ctx->public_key,
MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type));
if (key_len != NULL) {
*key_len = MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type);
}
return 0;
}
int mbedtls_lmots_calculate_public_key_candidate(const mbedtls_lmots_parameters_t *params,
const unsigned char *msg,
size_t msg_size,
const unsigned char *sig,
size_t sig_size,
unsigned char *out,
size_t out_size,
size_t *out_len)
{
unsigned char tmp_digit_array[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX];
unsigned char y_hashed_digits[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX];
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
if (msg == NULL && msg_size != 0) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
if (sig_size != MBEDTLS_LMOTS_SIG_LEN(params->type) ||
out_size < MBEDTLS_LMOTS_N_HASH_LEN(params->type)) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
ret = create_digit_array_with_checksum(params, msg, msg_size,
sig + MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET,
tmp_digit_array);
if (ret) {
return ret;
}
ret = hash_digit_array(params,
sig + MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(params->type),
tmp_digit_array, NULL, (unsigned char *) y_hashed_digits);
if (ret) {
return ret;
}
ret = public_key_from_hashed_digit_array(params,
(unsigned char *) y_hashed_digits,
out);
if (ret) {
return ret;
}
if (out_len != NULL) {
*out_len = MBEDTLS_LMOTS_N_HASH_LEN(params->type);
}
return 0;
}
int mbedtls_lmots_verify(const mbedtls_lmots_public_t *ctx,
const unsigned char *msg, size_t msg_size,
const unsigned char *sig, size_t sig_size)
{
unsigned char Kc_public_key_candidate[MBEDTLS_LMOTS_N_HASH_LEN_MAX];
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
if (msg == NULL && msg_size != 0) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
if (!ctx->have_public_key) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
if (ctx->params.type != MBEDTLS_LMOTS_SHA256_N32_W8) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
if (sig_size < MBEDTLS_LMOTS_SIG_TYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN) {
return MBEDTLS_ERR_LMS_VERIFY_FAILED;
}
if (mbedtls_lms_network_bytes_to_unsigned_int(MBEDTLS_LMOTS_TYPE_LEN,
sig + MBEDTLS_LMOTS_SIG_TYPE_OFFSET) !=
MBEDTLS_LMOTS_SHA256_N32_W8) {
return MBEDTLS_ERR_LMS_VERIFY_FAILED;
}
ret = mbedtls_lmots_calculate_public_key_candidate(&ctx->params,
msg, msg_size, sig, sig_size,
Kc_public_key_candidate,
MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type),
NULL);
if (ret) {
return MBEDTLS_ERR_LMS_VERIFY_FAILED;
}
if (memcmp(&Kc_public_key_candidate, ctx->public_key,
sizeof(ctx->public_key))) {
return MBEDTLS_ERR_LMS_VERIFY_FAILED;
}
return 0;
}
#if defined(MBEDTLS_LMS_PRIVATE)
void mbedtls_lmots_private_init(mbedtls_lmots_private_t *ctx)
{
memset(ctx, 0, sizeof(*ctx));
}
void mbedtls_lmots_private_free(mbedtls_lmots_private_t *ctx)
{
mbedtls_platform_zeroize(ctx,
sizeof(*ctx));
}
int mbedtls_lmots_generate_private_key(mbedtls_lmots_private_t *ctx,
mbedtls_lmots_algorithm_type_t type,
const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN],
uint32_t q_leaf_identifier,
const unsigned char *seed,
size_t seed_size)
{
psa_hash_operation_t op = PSA_HASH_OPERATION_INIT;
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t output_hash_len;
unsigned int i_digit_idx;
unsigned char i_digit_idx_bytes[2];
unsigned char const_bytes[1];
if (ctx->have_private_key) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
if (type != MBEDTLS_LMOTS_SHA256_N32_W8) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
ctx->params.type = type;
memcpy(ctx->params.I_key_identifier,
I_key_identifier,
sizeof(ctx->params.I_key_identifier));
mbedtls_lms_unsigned_int_to_network_bytes(q_leaf_identifier,
MBEDTLS_LMOTS_Q_LEAF_ID_LEN,
ctx->params.q_leaf_identifier);
mbedtls_lms_unsigned_int_to_network_bytes(0xFF, sizeof(const_bytes),
const_bytes);
for (i_digit_idx = 0;
i_digit_idx < MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(ctx->params.type);
i_digit_idx++) {
status = psa_hash_setup(&op, PSA_ALG_SHA_256);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op,
ctx->params.I_key_identifier,
sizeof(ctx->params.I_key_identifier));
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op,
ctx->params.q_leaf_identifier,
MBEDTLS_LMOTS_Q_LEAF_ID_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
mbedtls_lms_unsigned_int_to_network_bytes(i_digit_idx, I_DIGIT_IDX_LEN,
i_digit_idx_bytes);
status = psa_hash_update(&op, i_digit_idx_bytes, I_DIGIT_IDX_LEN);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, const_bytes, sizeof(const_bytes));
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_update(&op, seed, seed_size);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_hash_finish(&op,
ctx->private_key[i_digit_idx],
MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type),
&output_hash_len);
if (status != PSA_SUCCESS) {
goto exit;
}
psa_hash_abort(&op);
}
ctx->have_private_key = 1;
exit:
psa_hash_abort(&op);
return PSA_TO_MBEDTLS_ERR(status);
}
int mbedtls_lmots_calculate_public_key(mbedtls_lmots_public_t *ctx,
const mbedtls_lmots_private_t *priv_ctx)
{
unsigned char y_hashed_digits[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX];
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
/* Check that a private key is loaded */
if (!priv_ctx->have_private_key) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
ret = hash_digit_array(&priv_ctx->params,
(unsigned char *) priv_ctx->private_key, NULL,
NULL, (unsigned char *) y_hashed_digits);
if (ret) {
goto exit;
}
ret = public_key_from_hashed_digit_array(&priv_ctx->params,
(unsigned char *) y_hashed_digits,
ctx->public_key);
if (ret) {
goto exit;
}
memcpy(&ctx->params, &priv_ctx->params,
sizeof(ctx->params));
ctx->have_public_key = 1;
exit:
mbedtls_platform_zeroize(y_hashed_digits, sizeof(y_hashed_digits));
return ret;
}
int mbedtls_lmots_sign(mbedtls_lmots_private_t *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng, const unsigned char *msg, size_t msg_size,
unsigned char *sig, size_t sig_size, size_t *sig_len)
{
unsigned char tmp_digit_array[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX];
/* Create a temporary buffer to prepare the signature in. This allows us to
* finish creating a signature (ensuring the process doesn't fail), and then
* erase the private key **before** writing any data into the sig parameter
* buffer. If data were directly written into the sig buffer, it might leak
* a partial signature on failure, which effectively compromises the private
* key.
*/
unsigned char tmp_sig[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX];
unsigned char tmp_c_random[MBEDTLS_LMOTS_N_HASH_LEN_MAX];
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
if (msg == NULL && msg_size != 0) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
if (sig_size < MBEDTLS_LMOTS_SIG_LEN(ctx->params.type)) {
return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL;
}
/* Check that a private key is loaded */
if (!ctx->have_private_key) {
return MBEDTLS_ERR_LMS_BAD_INPUT_DATA;
}
ret = f_rng(p_rng, tmp_c_random,
MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type));
if (ret) {
return ret;
}
ret = create_digit_array_with_checksum(&ctx->params,
msg, msg_size,
tmp_c_random,
tmp_digit_array);
if (ret) {
goto exit;
}
ret = hash_digit_array(&ctx->params, (unsigned char *) ctx->private_key,
NULL, tmp_digit_array, (unsigned char *) tmp_sig);
if (ret) {
goto exit;
}
mbedtls_lms_unsigned_int_to_network_bytes(ctx->params.type,
MBEDTLS_LMOTS_TYPE_LEN,
sig + MBEDTLS_LMOTS_SIG_TYPE_OFFSET);
/* Test hook to check if sig is being written to before we invalidate the
* private key.
*/
#if defined(MBEDTLS_TEST_HOOKS)
if (mbedtls_lmots_sign_private_key_invalidated_hook != NULL) {
ret = (*mbedtls_lmots_sign_private_key_invalidated_hook)(sig);
if (ret != 0) {
return ret;
}
}
#endif /* defined(MBEDTLS_TEST_HOOKS) */
/* We've got a valid signature now, so it's time to make sure the private
* key can't be reused.
*/
ctx->have_private_key = 0;
mbedtls_platform_zeroize(ctx->private_key,
sizeof(ctx->private_key));
memcpy(sig + MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET, tmp_c_random,
MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(ctx->params.type));
memcpy(sig + MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(ctx->params.type), tmp_sig,
MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(ctx->params.type)
* MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type));
if (sig_len != NULL) {
*sig_len = MBEDTLS_LMOTS_SIG_LEN(ctx->params.type);
}
ret = 0;
exit:
mbedtls_platform_zeroize(tmp_digit_array, sizeof(tmp_digit_array));
mbedtls_platform_zeroize(tmp_sig, sizeof(tmp_sig));
return ret;
}
#endif /* defined(MBEDTLS_LMS_PRIVATE) */
#endif /* defined(MBEDTLS_LMS_C) */