xref: /freebsd/crypto/openssl/crypto/slh_dsa/slh_xmss.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 "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