mirror of https://github.com/openssl/openssl.git
175 lines
6.2 KiB
C
175 lines
6.2 KiB
C
/*
|
|
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
|
* this file except in compliance with the License. You can obtain a copy
|
|
* in the file LICENSE in the source distribution or at
|
|
* https://www.openssl.org/source/license.html
|
|
*/
|
|
|
|
#include <openssl/byteorder.h>
|
|
#include "crypto/lms_sig.h"
|
|
#include "crypto/lms_util.h"
|
|
#include "internal/common.h"
|
|
|
|
/*
|
|
* Constants used for obtaining unique inputs for different hashing operations
|
|
* e.g H(I || q || OSSL_LMS_D_LEAF || ... )
|
|
*/
|
|
const uint16_t OSSL_LMS_D_PBLC = 0x8080;
|
|
const uint16_t OSSL_LMS_D_MESG = 0x8181;
|
|
const uint16_t OSSL_LMS_D_LEAF = 0x8282;
|
|
const uint16_t OSSL_LMS_D_INTR = 0x8383;
|
|
|
|
/*
|
|
* @brief Compute the candidate LMS root value Tc
|
|
*
|
|
* @param paths An array of bytes representing the hash values associated with
|
|
* the path through the tree from the leaf associated with the
|
|
* LM-OTS signature to the root public key node.
|
|
* @param n The hash output size (The size of each path in |paths|)
|
|
* @param nodenum The leaf index node number. The root node had a value of 1
|
|
* Each subsequent level has nodes in the range 2^h...2^(h+1)-1
|
|
* @param ctx A EVP_MD_CTX object used for calculations
|
|
* @param ctxI A EVP_MD_CTX object containing an unfinalised H(I)
|
|
* @param Tc Contains H(I || u32str(node_num) || u16str(D_LEAF) || Kc) on input,
|
|
* and on output returns the calculated candidate public key.
|
|
* @returns 1 on success, or 0 otherwise.
|
|
*/
|
|
static
|
|
int lms_sig_compute_tc_from_path(const unsigned char *paths, uint32_t n,
|
|
uint32_t node_num,
|
|
EVP_MD_CTX *ctx, EVP_MD_CTX *ctxI,
|
|
unsigned char *Tc)
|
|
{
|
|
int ret = 0;
|
|
unsigned char qbuf[4];
|
|
unsigned char d_intr[sizeof(uint16_t)];
|
|
const unsigned char *path = paths;
|
|
|
|
OPENSSL_store_u16_be(d_intr, OSSL_LMS_D_INTR);
|
|
|
|
/*
|
|
* Calculate the public key Tc using the path
|
|
* The root hash is the hash of its 2 children's Hash values.
|
|
* A child hash for each level is passed in by paths, and we have
|
|
* a leaf value that can be used with the path to calculate the parent
|
|
* hash.
|
|
*/
|
|
while (node_num > 1) {
|
|
/* At each level the path contains either the left or right child */
|
|
int odd = node_num & 1;
|
|
|
|
node_num = node_num >> 1; /* get the parent node_num */
|
|
OPENSSL_store_u32_be(qbuf, node_num);
|
|
|
|
/*
|
|
* Calculate Tc as either
|
|
* Tc(parent) = H(I || node_q || 0x8383 || paths[i][n] || Tc(right) OR
|
|
* Tc(parent) = H(I || node_q || 0x8383 || Tc(left) || paths[i][n])
|
|
*/
|
|
if (!EVP_MD_CTX_copy_ex(ctx, ctxI)
|
|
|| !EVP_DigestUpdate(ctx, qbuf, sizeof(qbuf))
|
|
|| !EVP_DigestUpdate(ctx, d_intr, sizeof(d_intr)))
|
|
goto err;
|
|
|
|
if (odd) {
|
|
if (!EVP_DigestUpdate(ctx, path, n)
|
|
|| !EVP_DigestUpdate(ctx, Tc, n))
|
|
goto err;
|
|
} else {
|
|
if (!EVP_DigestUpdate(ctx, Tc, n)
|
|
|| !EVP_DigestUpdate(ctx, path, n))
|
|
goto err;
|
|
}
|
|
/*
|
|
* Tc = parent Hash, which is either the left or right child for the next
|
|
* level up (node_num determines if it is left or right).
|
|
*/
|
|
if (!EVP_DigestFinal_ex(ctx, Tc, NULL))
|
|
goto err;
|
|
path += n;
|
|
}
|
|
ret = 1;
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* @brief LMS signature verification.
|
|
* See RFC 8554 Section 5.4.2. Algorithm 6: Steps 3 & 4
|
|
*
|
|
* @param lms_sig Is a valid decoded LMS_SIG signature object.
|
|
* @param pub Is a valid LMS public key object.
|
|
* @param md Contains the fetched digest to be used for Hash operations
|
|
* @param msg A message to verify
|
|
* @param msglen The size of |msg|
|
|
* @returns 1 if the verification succeeded, or 0 otherwise.
|
|
*/
|
|
int ossl_lms_sig_verify(const LMS_SIG *lms_sig, const LMS_KEY *pub,
|
|
const EVP_MD *md,
|
|
const unsigned char *msg, size_t msglen)
|
|
{
|
|
int ret = 0;
|
|
EVP_MD_CTX *ctx = NULL, *ctxIq = NULL;
|
|
EVP_MD_CTX *ctxI;
|
|
unsigned char Kc[LMS_MAX_DIGEST_SIZE];
|
|
unsigned char Tc[LMS_MAX_DIGEST_SIZE];
|
|
unsigned char qbuf[4];
|
|
unsigned char d_leaf[sizeof(uint16_t)];
|
|
const LMS_PARAMS *lms_params = pub->lms_params;
|
|
uint32_t n = lms_params->n;
|
|
uint32_t node_num;
|
|
|
|
ctx = EVP_MD_CTX_create();
|
|
ctxIq = EVP_MD_CTX_create();
|
|
if (ctx == NULL || ctxIq == NULL)
|
|
goto err;
|
|
|
|
if (!lms_evp_md_ctx_init(ctxIq, md, lms_sig->params))
|
|
goto err;
|
|
/*
|
|
* Algorithm 6a: Step 3.
|
|
* Calculate a candidate public key |Kc| using the lmots_signature, message,
|
|
* and the identifiers I, q
|
|
*/
|
|
if (!ossl_lm_ots_compute_pubkey(ctx, ctxIq, &lms_sig->sig,
|
|
pub->ots_params, pub->Id,
|
|
lms_sig->q, msg, msglen, Kc))
|
|
goto err;
|
|
|
|
/*
|
|
* Algorithm 6a: Step 4
|
|
* Compute the candidate LMS root value Tc
|
|
*/
|
|
if (!ossl_assert(lms_sig->q < (uint32_t)(1 << lms_params->h)))
|
|
return 0;
|
|
node_num = (1 << lms_params->h) + lms_sig->q;
|
|
|
|
OPENSSL_store_u32_be(qbuf, node_num);
|
|
OPENSSL_store_u16_be(d_leaf, OSSL_LMS_D_LEAF);
|
|
ctxI = ctxIq;
|
|
/*
|
|
* Tc = H(I || u32str(node_num) || u16str(D_LEAF) || Kc)
|
|
*
|
|
* ctx is left initialised with the md from ossl_lm_ots_compute_pubkey,
|
|
* so there is no need to reinitialise it here.
|
|
*/
|
|
if (!EVP_DigestInit_ex2(ctx, NULL, NULL)
|
|
|| !EVP_DigestUpdate(ctx, pub->Id, LMS_SIZE_I)
|
|
|| !EVP_MD_CTX_copy_ex(ctxI, ctx)
|
|
|| !EVP_DigestUpdate(ctx, qbuf, sizeof(qbuf))
|
|
|| !EVP_DigestUpdate(ctx, d_leaf, sizeof(d_leaf))
|
|
|| !EVP_DigestUpdate(ctx, Kc, n)
|
|
|| !EVP_DigestFinal_ex(ctx, Tc, NULL)
|
|
|| !lms_sig_compute_tc_from_path(lms_sig->paths, n, node_num,
|
|
ctx, ctxI, Tc))
|
|
goto err;
|
|
/* Algorithm 6: Step 4 */
|
|
ret = (memcmp(pub->pub.K, Tc, n) == 0);
|
|
err:
|
|
EVP_MD_CTX_free(ctxIq);
|
|
EVP_MD_CTX_free(ctx);
|
|
return ret;
|
|
}
|