1 /*
2 * Copyright 2024-2025 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 "slh_dsa_local.h"
12 #include "slh_dsa_key.h"
13
14 /**
15 * @brief Compute the root Public key of a XMSS tree.
16 * See FIPS 205 Section 6.1 Algorithm 9.
17 * This is a recursive function that starts at an leaf index, that calculates
18 * the hash of each parent using 2 child nodes.
19 *
20 * @param ctx Contains SLH_DSA algorithm functions and constants.
21 * @param sk_seed A SLH-DSA private key seed of size |n|
22 * @param nodeid The index of the target node being computed
23 * (which must be < 2^(hm - height)
24 * @param h The height within the tree of the node being computed.
25 * (which must be <= hm) (hm is one of 3, 4, 8 or 9)
26 * At height=0 There are 2^hm leaf nodes,
27 * and the root node is at height = hm)
28 * @param pk_seed A SLH-DSA public key seed of size |n|
29 * @param adrs An ADRS object containing the layer address and tree address set
30 * to the XMSS tree within which the XMSS tree is being computed.
31 * @param pk_out The generated public key of size |n|
32 * @param pk_out_len The maximum size of |pk_out|
33 * @returns 1 on success, or 0 on error.
34 */
ossl_slh_xmss_node(SLH_DSA_HASH_CTX * ctx,const uint8_t * sk_seed,uint32_t node_id,uint32_t h,const uint8_t * pk_seed,uint8_t * adrs,uint8_t * pk_out,size_t pk_out_len)35 int ossl_slh_xmss_node(SLH_DSA_HASH_CTX *ctx, const uint8_t *sk_seed,
36 uint32_t node_id, uint32_t h,
37 const uint8_t *pk_seed, uint8_t *adrs,
38 uint8_t *pk_out, size_t pk_out_len)
39 {
40 const SLH_DSA_KEY *key = ctx->key;
41 SLH_ADRS_FUNC_DECLARE(key, adrsf);
42
43 if (h == 0) {
44 /* For leaf nodes generate the public key */
45 adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH);
46 adrsf->set_keypair_address(adrs, node_id);
47 if (!ossl_slh_wots_pk_gen(ctx, sk_seed, pk_seed, adrs,
48 pk_out, pk_out_len))
49 return 0;
50 } else {
51 uint8_t lnode[SLH_MAX_N], rnode[SLH_MAX_N];
52
53 if (!ossl_slh_xmss_node(ctx, sk_seed, 2 * node_id, h - 1, pk_seed, adrs,
54 lnode, sizeof(lnode))
55 || !ossl_slh_xmss_node(ctx, sk_seed, 2 * node_id + 1, h - 1,
56 pk_seed, adrs, rnode, sizeof(rnode)))
57 return 0;
58 adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_TREE);
59 adrsf->set_tree_height(adrs, h);
60 adrsf->set_tree_index(adrs, node_id);
61 if (!key->hash_func->H(ctx, pk_seed, adrs, lnode, rnode, pk_out, pk_out_len))
62 return 0;
63 }
64 return 1;
65 }
66
67 /**
68 * @brief Generate an XMSS signature using a message and key.
69 * See FIPS 205 Section 6.2 Algorithm 10
70 *
71 * The generated signature consists of:
72 * - A WOTS+ signature of size (2 * n + 3) * n
73 * - An array of authentication paths of size (XMSS tree_height) * n.
74 *
75 * @param ctx Contains SLH_DSA algorithm functions and constants.
76 * @param msg A message of size |n| bytes to sign
77 * @param sk_seed A private key seed of size |n|
78 * @param node_id The index of a WOTS+ key within the XMSS tree to use for signing.
79 * @param pk_seed A public key seed f size |n|
80 * @param adrs An ADRS object containing the layer address and tree address set
81 * to the XMSS key being used to sign the message.
82 * @param sig_wpkt A WPACKET object to write the generated XMSS signature to.
83 * @returns 1 on success, or 0 on error.
84 */
ossl_slh_xmss_sign(SLH_DSA_HASH_CTX * ctx,const uint8_t * msg,const uint8_t * sk_seed,uint32_t node_id,const uint8_t * pk_seed,uint8_t * adrs,WPACKET * sig_wpkt)85 int ossl_slh_xmss_sign(SLH_DSA_HASH_CTX *ctx, const uint8_t *msg,
86 const uint8_t *sk_seed, uint32_t node_id,
87 const uint8_t *pk_seed, uint8_t *adrs, WPACKET *sig_wpkt)
88 {
89 const SLH_DSA_KEY *key = ctx->key;
90 SLH_ADRS_FUNC_DECLARE(key, adrsf);
91 SLH_ADRS_DECLARE(tmp_adrs);
92 size_t n = key->params->n;
93 uint32_t h, hm = key->params->hm;
94 uint32_t id = node_id;
95 uint8_t *auth_path; /* Pointer to a buffer offset inside |sig_wpkt| */
96 size_t auth_path_len = n;
97
98 /*
99 * This code reverses the order of the FIPS 205 code so that it does the
100 * sign first. This simplifies the WPACKET writing.
101 */
102 adrsf->copy(tmp_adrs, adrs);
103 adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH);
104 adrsf->set_keypair_address(adrs, node_id);
105 if (!ossl_slh_wots_sign(ctx, msg, sk_seed, pk_seed, adrs, sig_wpkt))
106 return 0;
107
108 adrsf->copy(adrs, tmp_adrs);
109 for (h = 0; h < hm; ++h) {
110 if (!WPACKET_allocate_bytes(sig_wpkt, auth_path_len, &auth_path)
111 || !ossl_slh_xmss_node(ctx, sk_seed, id ^ 1, h, pk_seed, adrs,
112 auth_path, auth_path_len))
113 return 0;
114 id >>= 1;
115 }
116 return 1;
117 }
118
119 /**
120 * @brief Compute a candidate XMSS public key from a message and XMSS signature
121 * See FIPS 205 Section 6.3 Algorithm 11
122 *
123 * * The signature consists of:
124 * - A WOTS+ signature of size (2 * n + 3) * n
125 * - An array of authentication paths of size (XMSS tree height) * n.
126 *
127 * @param ctx Contains SLH_DSA algorithm functions and constants.
128 * @param node_id Must be set to the |node_id| used in xmss_sign().
129 * @param sig_rpkt A Packet to read a XMSS signature from.
130 * @param msg A message of size |n| bytes
131 * @param sk_seed A private key seed of size |n|
132 * @param pk_seed A public key seed of size |n|
133 * @param adrs An ADRS object containing a layer address and tree address of an
134 * XMSS key used for signing the message.
135 * @param pk_out The returned candidate XMSS public key of size |n|.
136 * @param pk_out_len The maximum size of |pk_out|.
137 * @returns 1 on success, or 0 on error.
138 */
ossl_slh_xmss_pk_from_sig(SLH_DSA_HASH_CTX * ctx,uint32_t node_id,PACKET * sig_rpkt,const uint8_t * msg,const uint8_t * pk_seed,uint8_t * adrs,uint8_t * pk_out,size_t pk_out_len)139 int ossl_slh_xmss_pk_from_sig(SLH_DSA_HASH_CTX *ctx, uint32_t node_id,
140 PACKET *sig_rpkt, const uint8_t *msg,
141 const uint8_t *pk_seed, uint8_t *adrs,
142 uint8_t *pk_out, size_t pk_out_len)
143 {
144 const SLH_DSA_KEY *key = ctx->key;
145 SLH_HASH_FUNC_DECLARE(key, hashf);
146 SLH_ADRS_FUNC_DECLARE(key, adrsf);
147 SLH_HASH_FN_DECLARE(hashf, H);
148 SLH_ADRS_FN_DECLARE(adrsf, set_tree_index);
149 SLH_ADRS_FN_DECLARE(adrsf, set_tree_height);
150 uint32_t k;
151 size_t n = key->params->n;
152 uint32_t hm = key->params->hm;
153 uint8_t *node = pk_out;
154 const uint8_t *auth_path; /* Pointer to buffer offset in |pkt_sig| */
155
156 adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH);
157 adrsf->set_keypair_address(adrs, node_id);
158 if (!ossl_slh_wots_pk_from_sig(ctx, sig_rpkt, msg, pk_seed, adrs,
159 node, pk_out_len))
160 return 0;
161
162 adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_TREE);
163
164 for (k = 0; k < hm; ++k) {
165 if (!PACKET_get_bytes(sig_rpkt, &auth_path, n))
166 return 0;
167 set_tree_height(adrs, k + 1);
168 if ((node_id & 1) == 0) { /* even */
169 node_id >>= 1;
170 set_tree_index(adrs, node_id);
171 if (!H(ctx, pk_seed, adrs, node, auth_path, node, pk_out_len))
172 return 0;
173 } else { /* odd */
174 node_id = (node_id - 1) >> 1;
175 set_tree_index(adrs, node_id);
176 if (!H(ctx, pk_seed, adrs, auth_path, node, node, pk_out_len))
177 return 0;
178 }
179 }
180 return 1;
181 }
182