/* * 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 #include #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; }