xref: /freebsd/crypto/openssl/crypto/slh_dsa/slh_fors.c (revision e7be843b4a162e68651d3911f0357ed464915629)
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 <openssl/crypto.h>
12 #include "slh_dsa_local.h"
13 #include "slh_dsa_key.h"
14 
15 /* k = 14, 17, 22, 33, 35 (number of trees) */
16 #define SLH_MAX_K           35
17 /* a = 6, 8, 9, 12 or 14  - There are (2^a) merkle trees */
18 #define SLH_MAX_A           9
19 
20 #define SLH_MAX_K_TIMES_A      (SLH_MAX_A * SLH_MAX_K)
21 #define SLH_MAX_ROOTS          (SLH_MAX_K_TIMES_A * SLH_MAX_N)
22 
23 static void slh_base_2b(const uint8_t *in, uint32_t b, uint32_t *out, size_t out_len);
24 
25 /**
26  * @brief Generate FORS secret values
27  * See FIPS 205 Section 8.1 Algorithm 14.
28  *
29  * @param ctx Contains SLH_DSA algorithm functions and constants.
30  * @param sk_seed A private key seed of size |n|
31  * @param pk_seed A public key seed of size |n|
32  * @param adrs An ADRS object containing the layer address of zero, with the
33  *             tree address and key pair address set to the index of the WOTS+
34  *             key within the XMSS tree that signs the FORS key.
35  * @param id The index of the FORS secret value within the sets of FORS trees.
36  *               (which must be < 2^(hm - height)
37  * @param pk_out The generated FORS secret value of size |n|
38  * @param pk_out_len The maximum size of |pk_out|
39  * @returns 1 on success, or 0 on error.
40  */
slh_fors_sk_gen(SLH_DSA_HASH_CTX * ctx,const uint8_t * sk_seed,const uint8_t * pk_seed,uint8_t * adrs,uint32_t id,uint8_t * pk_out,size_t pk_out_len)41 static int slh_fors_sk_gen(SLH_DSA_HASH_CTX *ctx, const uint8_t *sk_seed,
42                            const uint8_t *pk_seed, uint8_t *adrs, uint32_t id,
43                            uint8_t *pk_out, size_t pk_out_len)
44 {
45     const SLH_DSA_KEY *key = ctx->key;
46     SLH_ADRS_DECLARE(sk_adrs);
47     SLH_ADRS_FUNC_DECLARE(key, adrsf);
48 
49     adrsf->copy(sk_adrs, adrs);
50     adrsf->set_type_and_clear(sk_adrs, SLH_ADRS_TYPE_FORS_PRF);
51     adrsf->copy_keypair_address(sk_adrs, adrs);
52     adrsf->set_tree_index(sk_adrs, id);
53     return key->hash_func->PRF(ctx, pk_seed, sk_seed, sk_adrs, pk_out, pk_out_len);
54 }
55 
56 /**
57  * @brief Computes the nodes of a Merkle tree.
58  * See FIPS 205 Section 8.2 Algorithm 18
59  *
60  * The leaf nodes are hashes of FORS secret values.
61  * Each parent node is a hash of its 2 children.
62  * Note this is a recursive function.
63  *
64  * @param ctx Contains SLH_DSA algorithm functions and constants.
65  * @param sk_seed A SLH_DSA private key seed of size |n|
66  * @param pk_seed A SLH_DSA public key seed of size |n|
67  * @param adrs The ADRS object must have a layer address of zero, and the
68  *             tree address set to the XMSS tree that signs the FORS key,
69  *             the type set to FORS_TREE, and the keypair address set to the
70  *             index of the WOTS+ key that signs the FORS key.
71  * @param node_id The target node index
72  * @param height The target node height
73  * @param node The returned hash for a node of size|n|
74  * @param node_len The maximum size of |node|
75  * @returns 1 on success, or 0 on error.
76  */
slh_fors_node(SLH_DSA_HASH_CTX * ctx,const uint8_t * sk_seed,const uint8_t * pk_seed,uint8_t * adrs,uint32_t node_id,uint32_t height,uint8_t * node,size_t node_len)77 static int slh_fors_node(SLH_DSA_HASH_CTX *ctx, const uint8_t *sk_seed,
78                          const uint8_t *pk_seed, uint8_t *adrs, uint32_t node_id,
79                          uint32_t height, uint8_t *node, size_t node_len)
80 {
81     int ret = 0;
82     const SLH_DSA_KEY *key = ctx->key;
83     uint8_t sk[SLH_MAX_N], lnode[SLH_MAX_N], rnode[SLH_MAX_N];
84     uint32_t n = key->params->n;
85 
86     SLH_ADRS_FUNC_DECLARE(key, adrsf);
87 
88     if (height == 0) {
89         /* Gets here for leaf nodes */
90         if (!slh_fors_sk_gen(ctx, sk_seed, pk_seed, adrs, node_id, sk, sizeof(sk)))
91             return 0;
92         adrsf->set_tree_height(adrs, 0);
93         adrsf->set_tree_index(adrs, node_id);
94         ret = key->hash_func->F(ctx, pk_seed, adrs, sk, n, node, node_len);
95         OPENSSL_cleanse(sk, n);
96         return ret;
97     } else {
98         if (!slh_fors_node(ctx, sk_seed, pk_seed, adrs, 2 * node_id, height - 1,
99                            lnode, sizeof(rnode))
100                 || !slh_fors_node(ctx, sk_seed, pk_seed, adrs, 2 * node_id + 1,
101                                   height - 1, rnode, sizeof(rnode)))
102             return 0;
103         adrsf->set_tree_height(adrs, height);
104         adrsf->set_tree_index(adrs, node_id);
105         if (!key->hash_func->H(ctx, pk_seed, adrs, lnode, rnode, node, node_len))
106             return 0;
107     }
108     return 1;
109 }
110 
111 /**
112  * @brief Generate an FORS signature
113  * See FIPS 205 Section 8.3 Algorithm 16
114  *
115  * A FORS signature has a size of (k * (1 + a) * n) bytes
116  * There are k trees, each of which have a private key value of size |n| followed
117  * by an authentication path of size |a| (where each path is size |n|)
118  *
119  * @param ctx Contains SLH_DSA algorithm functions and constants.
120  * @param md A message digest of size |(k * a + 7) / 8| bytes to sign
121  * @param sk_seed A private key seed of size |n|
122  * @param pk_seed A public key seed of size |n|
123  * @param adrs The ADRS object must have a layer address of zero, and the
124  *             tree address set to the XMSS tree that signs the FORS key,
125  *             the type set to FORS_TREE, and the keypair address set to the
126  *             index of the WOTS+ key that signs the FORS key.
127  * @param sig_wpkt A WPACKET object to write the generated XMSS signature to
128  * @param sig_len  The size of |sig| which is (2 * n + 3) * n + tree_height * n.
129  * @returns 1 on success, or 0 on error.
130  */
ossl_slh_fors_sign(SLH_DSA_HASH_CTX * ctx,const uint8_t * md,const uint8_t * sk_seed,const uint8_t * pk_seed,uint8_t * adrs,WPACKET * sig_wpkt)131 int ossl_slh_fors_sign(SLH_DSA_HASH_CTX *ctx, const uint8_t *md,
132                        const uint8_t *sk_seed, const uint8_t *pk_seed,
133                        uint8_t *adrs, WPACKET *sig_wpkt)
134 {
135     const SLH_DSA_KEY *key = ctx->key;
136     uint32_t tree_id, layer, s, tree_offset;
137     uint32_t ids[SLH_MAX_K];
138     const SLH_DSA_PARAMS *params = key->params;
139     uint32_t n = params->n;
140     uint32_t k = params->k; /* number of trees */
141     uint32_t a = params->a;
142     uint32_t two_power_a = (1 << a); /* this is t in FIPS 205 */
143     uint32_t tree_id_times_two_power_a = 0;
144     uint8_t out[SLH_MAX_N];
145 
146     /*
147      * Split md into k a-bit values e.g with k = 14, a = 12
148      * ids[0..13] = 12 bits each of md
149      */
150     slh_base_2b(md, a, ids, k);
151 
152     for (tree_id = 0; tree_id < k; ++tree_id) {
153         /* Get the tree[i] leaf id */
154         uint32_t node_id = ids[tree_id]; /* |id| = |a| bits */
155 
156         /*
157          * Give each of the k trees a unique range at each level.
158          * e.g. If we have 4096 leaf nodes (2^a = 2^12) for each tree
159          * tree i will use indexes from 4096 * i + (0..4095) for its bottom level.
160          * For the next level up from the bottom there would be 2048 nodes
161          * (so tree i uses indexes 2048 * i + (0...2047) for this level)
162          */
163         tree_offset = tree_id_times_two_power_a;
164 
165         if (!slh_fors_sk_gen(ctx, sk_seed, pk_seed, adrs,
166                              node_id + tree_id_times_two_power_a, out, sizeof(out))
167                 || !WPACKET_memcpy(sig_wpkt, out, n))
168             return 0;
169 
170         /*
171          * Traverse from the bottom of the tree (layer = 0)
172          * up to the root (layer = a - 1).
173          * NOTE: This is a really inefficient way of doing this, since at
174          * layer a - 1 it calculates most of the hashes of the entire tree as
175          * well as all the leaf nodes. So it is calculating nodes multiple times.
176          */
177         for (layer = 0; layer < a; ++layer) {
178             s = node_id ^ 1; /* XOR gets the index of the other child in a binary tree */
179             if (!slh_fors_node(ctx, sk_seed, pk_seed, adrs,
180                                s + tree_offset, layer, out, sizeof(out)))
181                 return 0;
182             node_id >>= 1; /* Get the parent node id */
183             tree_offset >>= 1; /* Each layer up has half as many nodes */
184             if (!WPACKET_memcpy(sig_wpkt, out, n))
185                 return 0;
186         }
187         tree_id_times_two_power_a += two_power_a;
188     }
189     return 1;
190 }
191 
192 /**
193  * @brief Compute a candidate FORS public key from a message and signature.
194  * See FIPS 205 Section 8.4 Algorithm 17.
195  *
196  * A FORS signature has a size of (k * (a + 1) * n) bytes
197  *
198  * @param ctx Contains SLH_DSA algorithm functions and constants.
199  * @param fors_sig_rpkt A PACKET object to read a FORS signature from
200  * @param md A message digest of size (k * a / 8) bytes
201  * @param pk_seed A public key seed of size |n|
202  * @param adrs The ADRS object must have a layer address of zero, and the
203  *             tree address set to the XMSS tree that signs the FORS key,
204  *             the type set to FORS_TREE, and the keypair address set to the
205  *             index of the WOTS+ key that signs the FORS key.
206  * @param pk_out The returned candidate FORS public key of size |n|
207  * @param pk_out_len The maximum size of |pk_out|
208  * @returns 1 on success, or 0 on error.
209  */
ossl_slh_fors_pk_from_sig(SLH_DSA_HASH_CTX * ctx,PACKET * fors_sig_rpkt,const uint8_t * md,const uint8_t * pk_seed,uint8_t * adrs,uint8_t * pk_out,size_t pk_out_len)210 int ossl_slh_fors_pk_from_sig(SLH_DSA_HASH_CTX *ctx, PACKET *fors_sig_rpkt,
211                               const uint8_t *md, const uint8_t *pk_seed,
212                               uint8_t *adrs, uint8_t *pk_out, size_t pk_out_len)
213 {
214     const SLH_DSA_KEY *key = ctx->key;
215     int ret = 0;
216     uint32_t i, j, aoff = 0;
217     uint32_t ids[SLH_MAX_K];
218     const SLH_DSA_PARAMS *params = key->params;
219     uint32_t a = params->a;
220     uint32_t k = params->k;
221     uint32_t n = params->n;
222     uint32_t two_power_a = (1 << a);
223     const uint8_t *sk, *authj; /* Pointers to |sig| buffer inside fors_sig_rpkt */
224     uint8_t roots[SLH_MAX_ROOTS];
225     size_t roots_len = 0; /* The size of |roots| */
226     uint8_t *node0, *node1; /* Pointers into roots[] */
227     WPACKET root_pkt, *wroot_pkt = &root_pkt; /* Points to |roots| buffer */
228 
229     SLH_ADRS_DECLARE(pk_adrs);
230     SLH_ADRS_FUNC_DECLARE(key, adrsf);
231     SLH_ADRS_FN_DECLARE(adrsf, set_tree_index);
232     SLH_ADRS_FN_DECLARE(adrsf, set_tree_height);
233     SLH_HASH_FUNC_DECLARE(key, hashf);
234     SLH_HASH_FN_DECLARE(hashf, F);
235     SLH_HASH_FN_DECLARE(hashf, H);
236 
237     if (!WPACKET_init_static_len(wroot_pkt, roots, sizeof(roots), 0))
238         return 0;
239 
240     /* Split md into k a-bit values e.g ids[0..k-1] = 12 bits each of md */
241     slh_base_2b(md, a, ids, k);
242 
243     /* Compute the roots of k Merkle trees */
244     for (i = 0; i < k; ++i) {
245         uint32_t id = ids[i];
246         uint32_t node_id = id + aoff;
247 
248         set_tree_height(adrs, 0);
249         set_tree_index(adrs, node_id);
250 
251         /* Regenerate the public key of the leaf */
252         if (!PACKET_get_bytes(fors_sig_rpkt, &sk, n)
253                 || !WPACKET_allocate_bytes(wroot_pkt, n, &node0)
254                 || !F(ctx, pk_seed, adrs, sk, n, node0, n))
255             goto err;
256 
257         /* This omits the copying of the nodes that the FIPS 205 code does */
258         node1 = node0;
259         for (j = 0; j < a; ++j) {
260             /* Get this layers other child public key */
261             if (!PACKET_get_bytes(fors_sig_rpkt, &authj, n))
262                 goto err;
263             /* Hash the children together to get the parent nodes public key */
264             set_tree_height(adrs, j + 1);
265             if ((id & 1) == 0) {
266                 node_id >>= 1;
267                 set_tree_index(adrs, node_id);
268                 if (!H(ctx, pk_seed, adrs, node0, authj, node1, n))
269                     goto err;
270             } else {
271                 node_id = (node_id - 1) >> 1;
272                 set_tree_index(adrs, node_id);
273                 if (!H(ctx, pk_seed, adrs, authj, node0, node1, n))
274                     goto err;
275             }
276             id >>= 1;
277         }
278         aoff += two_power_a;
279     }
280     if (!WPACKET_get_total_written(wroot_pkt, &roots_len))
281         goto err;
282 
283     /* The public key is the hash of all the roots of the k trees */
284     adrsf->copy(pk_adrs, adrs);
285     adrsf->set_type_and_clear(pk_adrs, SLH_ADRS_TYPE_FORS_ROOTS);
286     adrsf->copy_keypair_address(pk_adrs, adrs);
287     ret = hashf->T(ctx, pk_seed, pk_adrs, roots, roots_len, pk_out, pk_out_len);
288  err:
289     if (!WPACKET_finish(wroot_pkt))
290         ret = 0;
291     return ret;
292 }
293 
294 /**
295  * @brief Convert a byte string into a base 2^b representation
296  * See FIPS 205 Algorithm 4
297  *
298  * @param in An input byte stream with a size >= |outlen * b / 8|
299  * @param b The bit size to divide |in| into
300  *          This is one of 6, 8, 9, 12 or 14 for FORS.
301  * @param out The array of returned base 2^b integers that represents the first
302  *            |outlen|*|b| bits of |in|
303  * @param out_len The size of |out|
304  */
slh_base_2b(const uint8_t * in,uint32_t b,uint32_t * out,size_t out_len)305 static void slh_base_2b(const uint8_t *in, uint32_t b,
306                         uint32_t *out, size_t out_len)
307 {
308     size_t consumed = 0;
309     uint32_t bits = 0;
310     uint32_t total = 0;
311     uint32_t mask = (1 << b) - 1;
312 
313     for (consumed = 0; consumed < out_len; consumed++) {
314         while (bits < b) {
315             total <<= 8;
316             total += *in++;
317             bits += 8;
318         }
319         bits -= b;
320         *out++ = (total >> bits) & mask;
321     }
322 }
323