1 /*
2 * Copyright 2024-2026 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include <string.h>
11 #include <openssl/crypto.h>
12 #include "slh_dsa_local.h"
13 #include "slh_dsa_key.h"
14
15 /**
16 * @brief Generate a Hypertree Signature
17 * See FIPS 205 Section 7.1 Algorithm 12
18 *
19 * This writes |d| XMSS signatures i.e. ((|h| + |d| * |len|) * |n|)
20 * where the first signature uses the XMSS key at the lowest layer, and the last
21 * signature uses the XMSS key at the top layer.
22 *
23 * @param ctx Contains SLH_DSA algorithm functions and constants.
24 * @param msg A message of size |n|.
25 * @param sk_seed The private key seed of size |n|
26 * @param pk_seed The public key seed of size |n|
27 * @param tree_id Index of the XMSS tree that will sign the message
28 * @param leaf_id Index of the WOTS+ key within the XMSS tree that will sign the message
29 * @param sig_wpkt A WPACKET object to write the Hypertree Signature to.
30 * @returns 1 on success, or 0 on error.
31 */
ossl_slh_ht_sign(SLH_DSA_HASH_CTX * ctx,const uint8_t * msg,const uint8_t * sk_seed,const uint8_t * pk_seed,uint64_t tree_id,uint32_t leaf_id,WPACKET * sig_wpkt)32 int ossl_slh_ht_sign(SLH_DSA_HASH_CTX *ctx,
33 const uint8_t *msg, const uint8_t *sk_seed,
34 const uint8_t *pk_seed,
35 uint64_t tree_id, uint32_t leaf_id, WPACKET *sig_wpkt)
36 {
37 int ret = 0;
38 const SLH_DSA_KEY *key = ctx->key;
39 SLH_ADRS_FUNC_DECLARE(key, adrsf);
40 SLH_ADRS_DECLARE(adrs);
41 uint8_t root[SLH_MAX_N];
42 uint32_t layer, mask;
43 const SLH_DSA_PARAMS *params = key->params;
44 uint32_t n = params->n;
45 uint32_t d = params->d;
46 uint32_t hm = params->hm;
47 uint8_t *psig;
48 PACKET rpkt, *xmss_sig_rpkt = &rpkt;
49
50 mask = (1 << hm) - 1; /* A mod 2^h = A & ((2^h - 1))) */
51
52 adrsf->zero(adrs);
53 /*
54 * For each XMSS tree there is a current leaf node that is used for signing.
55 * The first iteration of the loop signs the input message using the bottom
56 * tree. Subsequent passes use the parent trees leaf node to sign the current
57 * trees public key.
58 * Each node in an XMSS tree has a sibling (except for the root node),
59 * so starting at the leaf node it traverses up the tree calculating
60 * hashes for all the siblings in the path to the root node,
61 * which are then stored in the XMSS signature. The verify then just needs
62 * the hash of the leaf node which is can then combine with the signature
63 * path hashes to work all the way up to the root node to calculate the
64 * public key.
65 */
66 memcpy(root, msg, n);
67
68 for (layer = 0; layer < d; ++layer) {
69 /* type = SLH_ADRS_TYPE_WOTS_HASH */
70 adrsf->set_layer_address(adrs, layer);
71 adrsf->set_tree_address(adrs, tree_id);
72 psig = WPACKET_get_curr(sig_wpkt);
73 if (!ossl_slh_xmss_sign(ctx, root, sk_seed, leaf_id, pk_seed, adrs,
74 sig_wpkt))
75 goto err;
76 /*
77 * On the last loop it skips getting the public key since it is not needed
78 * to calculate another signature. If this was called it should equal
79 * the PK_ROOT (i.e. the public key of the top level tree).
80 */
81 if (layer < d - 1) {
82 if (!PACKET_buf_init(xmss_sig_rpkt, psig,
83 WPACKET_get_curr(sig_wpkt) - psig))
84 goto err;
85 if (!ossl_slh_xmss_pk_from_sig(ctx, leaf_id, xmss_sig_rpkt, root,
86 pk_seed, adrs, root, sizeof(root)))
87 goto err;
88 leaf_id = tree_id & mask;
89 tree_id >>= hm;
90 }
91 }
92 ret = 1;
93 err:
94 OPENSSL_cleanse(root, sizeof(root));
95 return ret;
96 }
97
98 /**
99 * @brief Verify a Hypertree Signature
100 * See FIPS 205 Section 7.2 Algorithm 13
101 *
102 * @param ctx Contains SLH_DSA algorithm functions and constants.
103 * @param msg A message of size |n| bytes
104 * @param sig A HT signature of size (|h| + |d| * |len|) * |n| bytes
105 * @param pk_seed SLH_DSA public key seed of size |n|
106 * @param tree_id Index of the XMSS tree that signed the message
107 * @param leaf_id Index of the WOTS+ key within the XMSS tree that signed the message
108 * @param pk_root The known Hypertree public key of size |n|
109 *
110 * @returns 1 if the computed XMSS public key matches pk_root, or 0 otherwise.
111 */
ossl_slh_ht_verify(SLH_DSA_HASH_CTX * ctx,const uint8_t * msg,PACKET * sig_pkt,const uint8_t * pk_seed,uint64_t tree_id,uint32_t leaf_id,const uint8_t * pk_root)112 int ossl_slh_ht_verify(SLH_DSA_HASH_CTX *ctx, const uint8_t *msg, PACKET *sig_pkt,
113 const uint8_t *pk_seed, uint64_t tree_id, uint32_t leaf_id,
114 const uint8_t *pk_root)
115 {
116 int ret = 0;
117 const SLH_DSA_KEY *key = ctx->key;
118 SLH_ADRS_FUNC_DECLARE(key, adrsf);
119 SLH_ADRS_DECLARE(adrs);
120 uint8_t node[SLH_MAX_N];
121 const SLH_DSA_PARAMS *params = key->params;
122 uint32_t tree_height = params->hm;
123 uint32_t n = params->n;
124 uint32_t d = params->d;
125 uint32_t mask = (1 << tree_height) - 1;
126 uint32_t layer;
127
128 adrsf->zero(adrs);
129 memcpy(node, msg, n);
130
131 for (layer = 0; layer < d; ++layer) {
132 adrsf->set_layer_address(adrs, layer);
133 adrsf->set_tree_address(adrs, tree_id);
134 if (!ossl_slh_xmss_pk_from_sig(ctx, leaf_id, sig_pkt, node,
135 pk_seed, adrs, node, sizeof(node)))
136 goto err;
137 leaf_id = tree_id & mask;
138 tree_id >>= tree_height;
139 }
140 ret = (memcmp(node, pk_root, n) == 0);
141 err:
142 OPENSSL_cleanse(node, sizeof(node));
143 return ret;
144 }
145