/src/openssl/crypto/slh_dsa/slh_xmss.c
Line | Count | Source (jump to first uncovered line) |
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 | | */ |
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 | 0 | { |
40 | 0 | const SLH_DSA_KEY *key = ctx->key; |
41 | 0 | SLH_ADRS_FUNC_DECLARE(key, adrsf); |
42 | |
|
43 | 0 | if (h == 0) { |
44 | | /* For leaf nodes generate the public key */ |
45 | 0 | adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH); |
46 | 0 | adrsf->set_keypair_address(adrs, node_id); |
47 | 0 | if (!ossl_slh_wots_pk_gen(ctx, sk_seed, pk_seed, adrs, |
48 | 0 | pk_out, pk_out_len)) |
49 | 0 | return 0; |
50 | 0 | } else { |
51 | 0 | uint8_t lnode[SLH_MAX_N], rnode[SLH_MAX_N]; |
52 | |
|
53 | 0 | if (!ossl_slh_xmss_node(ctx, sk_seed, 2 * node_id, h - 1, pk_seed, adrs, |
54 | 0 | lnode, sizeof(lnode)) |
55 | 0 | || !ossl_slh_xmss_node(ctx, sk_seed, 2 * node_id + 1, h - 1, |
56 | 0 | pk_seed, adrs, rnode, sizeof(rnode))) |
57 | 0 | return 0; |
58 | 0 | adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_TREE); |
59 | 0 | adrsf->set_tree_height(adrs, h); |
60 | 0 | adrsf->set_tree_index(adrs, node_id); |
61 | 0 | if (!key->hash_func->H(ctx, pk_seed, adrs, lnode, rnode, pk_out, pk_out_len)) |
62 | 0 | return 0; |
63 | 0 | } |
64 | 0 | return 1; |
65 | 0 | } |
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 | | */ |
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 | 0 | { |
89 | 0 | const SLH_DSA_KEY *key = ctx->key; |
90 | 0 | SLH_ADRS_FUNC_DECLARE(key, adrsf); |
91 | 0 | SLH_ADRS_DECLARE(tmp_adrs); |
92 | 0 | size_t n = key->params->n; |
93 | 0 | uint32_t h, hm = key->params->hm; |
94 | 0 | uint32_t id = node_id; |
95 | 0 | uint8_t *auth_path; /* Pointer to a buffer offset inside |sig_wpkt| */ |
96 | 0 | 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 | 0 | adrsf->copy(tmp_adrs, adrs); |
103 | 0 | adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH); |
104 | 0 | adrsf->set_keypair_address(adrs, node_id); |
105 | 0 | if (!ossl_slh_wots_sign(ctx, msg, sk_seed, pk_seed, adrs, sig_wpkt)) |
106 | 0 | return 0; |
107 | | |
108 | 0 | adrsf->copy(adrs, tmp_adrs); |
109 | 0 | for (h = 0; h < hm; ++h) { |
110 | 0 | if (!WPACKET_allocate_bytes(sig_wpkt, auth_path_len, &auth_path) |
111 | 0 | || !ossl_slh_xmss_node(ctx, sk_seed, id ^ 1, h, pk_seed, adrs, |
112 | 0 | auth_path, auth_path_len)) |
113 | 0 | return 0; |
114 | 0 | id >>= 1; |
115 | 0 | } |
116 | 0 | return 1; |
117 | 0 | } |
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 | | */ |
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 | 0 | { |
144 | 0 | const SLH_DSA_KEY *key = ctx->key; |
145 | 0 | SLH_HASH_FUNC_DECLARE(key, hashf); |
146 | 0 | SLH_ADRS_FUNC_DECLARE(key, adrsf); |
147 | 0 | SLH_HASH_FN_DECLARE(hashf, H); |
148 | 0 | SLH_ADRS_FN_DECLARE(adrsf, set_tree_index); |
149 | 0 | SLH_ADRS_FN_DECLARE(adrsf, set_tree_height); |
150 | 0 | uint32_t k; |
151 | 0 | size_t n = key->params->n; |
152 | 0 | uint32_t hm = key->params->hm; |
153 | 0 | uint8_t *node = pk_out; |
154 | 0 | const uint8_t *auth_path; /* Pointer to buffer offset in |pkt_sig| */ |
155 | |
|
156 | 0 | adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH); |
157 | 0 | adrsf->set_keypair_address(adrs, node_id); |
158 | 0 | if (!ossl_slh_wots_pk_from_sig(ctx, sig_rpkt, msg, pk_seed, adrs, |
159 | 0 | node, pk_out_len)) |
160 | 0 | return 0; |
161 | | |
162 | 0 | adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_TREE); |
163 | |
|
164 | 0 | for (k = 0; k < hm; ++k) { |
165 | 0 | if (!PACKET_get_bytes(sig_rpkt, &auth_path, n)) |
166 | 0 | return 0; |
167 | 0 | set_tree_height(adrs, k + 1); |
168 | 0 | if ((node_id & 1) == 0) { /* even */ |
169 | 0 | node_id >>= 1; |
170 | 0 | set_tree_index(adrs, node_id); |
171 | 0 | if (!H(ctx, pk_seed, adrs, node, auth_path, node, pk_out_len)) |
172 | 0 | return 0; |
173 | 0 | } else { /* odd */ |
174 | 0 | node_id = (node_id - 1) >> 1; |
175 | 0 | set_tree_index(adrs, node_id); |
176 | 0 | if (!H(ctx, pk_seed, adrs, auth_path, node, node, pk_out_len)) |
177 | 0 | return 0; |
178 | 0 | } |
179 | 0 | } |
180 | 0 | return 1; |
181 | 0 | } |