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