openssl/crypto/lms/lm_ots_verify.c

151 lines
5.1 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/evp.h>
#include <openssl/byteorder.h>
#include "crypto/lms_sig.h"
#include "crypto/lms_util.h"
static int lm_ots_compute_pubkey_final(EVP_MD_CTX *ctx, EVP_MD_CTX *ctxIq,
const LM_OTS_SIG *sig, unsigned char *Kc);
/**
* @brief OTS Signature verification.
*
* See RFC 8554 Section 4.6 Signature Verification
* Algorithm 4b: Compute a Public Key Candidate |Kc| from a signature, message
* and public key parameters.
*
* @param ctx A temporary working EVP_MD_CTX object.
* @param ctxIq A EVP_MD_CTX object that has been initialised with a digest,
* that returns a non finalized value of H(I || q)
* @param sig An LM_OTS_SIG object that contains C and y
* @param pub The public key LM_OTS_PARAMS
* @param Id A 16 byte identifier (I) associated with a LMS tree
* @param q The leaf index of the LMS tree.
* @param msg A message to verify
* @param msglen The size of |msg|
* @param Kc The computed public key candidate. It is assumed the size is n.
* @returns 1 on success, or 0 otherwise.
*/
int ossl_lm_ots_compute_pubkey(EVP_MD_CTX *ctx, EVP_MD_CTX *ctxIq,
const LM_OTS_SIG *sig, const LM_OTS_PARAMS *pub,
const unsigned char *Id, uint32_t q,
const unsigned char *msg, size_t msglen,
unsigned char *Kc)
{
unsigned char qbuf[LMS_SIZE_q];
unsigned char d_mesg[sizeof(uint16_t)];
if (sig->params != pub)
return 0;
OPENSSL_store_u32_be(qbuf, q);
OPENSSL_store_u16_be(d_mesg, OSSL_LMS_D_MESG);
return (EVP_DigestUpdate(ctxIq, Id, LMS_SIZE_I)
&& EVP_DigestUpdate(ctxIq, qbuf, sizeof(qbuf))
&& EVP_MD_CTX_copy_ex(ctx, ctxIq)
/* Q = H(I || u32str(q) || u16str(D_MESG) || C || msg) */
&& EVP_DigestUpdate(ctx, d_mesg, sizeof(d_mesg))
&& EVP_DigestUpdate(ctx, sig->C, sig->params->n)
&& EVP_DigestUpdate(ctx, msg, msglen)
&& lm_ots_compute_pubkey_final(ctx, ctxIq, sig, Kc));
}
/**
* @brief simple function to increment a big-endian 16 bit counter by 1.
* It assumes checking for overflow is not required.
*/
static ossl_inline void INC16(unsigned char *tag)
{
if (++(tag[1]) == 0)
++tag[0];
}
/*
* @brief OTS signature verification final phase
* See RFC 8554 Section 4.3 Signature Verification
* Algorithm 4b - Part 3
* Step 3 (Finalizes Q) and 4
*
* @param ctx A EVP_MD_CTX object that contains a non finalized value of
* Q = H(I || u32str(q) || u16str(D_MESG) || C || msg)
* This ctx is reused for other calculations.
* @param ctxIq A EVP_MD_CTX object that contains a non finalized value of H(I || q).
* @param sig An object containing LM_OTS signature data.
* @param Kc The computed public key. It is assumed the size is n.
* @returns 1 on success, or 0 otherwise.
*/
static int lm_ots_compute_pubkey_final(EVP_MD_CTX *ctx, EVP_MD_CTX *ctxIq,
const LM_OTS_SIG *sig, unsigned char *Kc)
{
int ret = 0, i;
EVP_MD_CTX *ctxKc = NULL;
unsigned char tag[2 + 1], *tag2 = &tag[2];
unsigned char Q[LMS_MAX_DIGEST_SIZE + LMS_SIZE_QSUM], *Qsum;
unsigned char z[LMS_MAX_DIGEST_SIZE];
unsigned char d_pblc[sizeof(uint16_t)];
uint16_t sum;
const LM_OTS_PARAMS *params = sig->params;
int n = params->n;
int p = params->p;
uint8_t j, w = params->w, end = (1 << w) - 1;
int a;
unsigned char *y;
if (!EVP_DigestFinal_ex(ctx, Q, NULL)
|| (ctxKc = EVP_MD_CTX_create()) == NULL)
return 0;
sum = ossl_lm_ots_params_checksum(params, Q);
Qsum = Q + n;
/* Q || Cksm(Q) */
OPENSSL_store_u16_be(Qsum, sum);
OPENSSL_store_u16_be(d_pblc, OSSL_LMS_D_PBLC);
if (!(EVP_MD_CTX_copy_ex(ctxKc, ctxIq))
|| !EVP_DigestUpdate(ctxKc, d_pblc, sizeof(d_pblc)))
goto err;
y = sig->y;
tag[0] = 0;
tag[1] = 0;
/*
* Depending on the lm_ots_type (see lm_ots_params[])
* The outer loop |p| ranges from 26...265 iterations and
* the inner loop |end| is in the range 0...(2^w)-1
*/
for (i = 0; i < p; ++i) {
a = lms_ots_coef(Q, i, w);
memcpy(z, y, n);
y += n;
for (j = a; j < end; ++j) {
*tag2 = (j & 0xFF);
if (!(EVP_MD_CTX_copy_ex(ctx, ctxIq))
|| !EVP_DigestUpdate(ctx, tag, sizeof(tag))
|| !EVP_DigestUpdate(ctx, z, n)
|| !EVP_DigestFinal_ex(ctx, z, NULL))
goto err;
}
INC16(tag);
if (!EVP_DigestUpdate(ctxKc, z, n))
goto err;
}
/* Kc = H(I || u32str(q) || u16str(D_PBLC) || z[0] || ... || z[p-1]) */
if (!EVP_DigestFinal(ctxKc, Kc, NULL))
goto err;
ret = 1;
err:
EVP_MD_CTX_free(ctxKc);
return ret;
}