/src/openssl/crypto/slh_dsa/slh_hypertree.c
Line | Count | Source |
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 | | /** |
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 | | */ |
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 | 0 | { |
37 | 0 | int ret = 0; |
38 | 0 | const SLH_DSA_KEY *key = ctx->key; |
39 | 0 | SLH_ADRS_FUNC_DECLARE(key, adrsf); |
40 | 0 | SLH_ADRS_DECLARE(adrs); |
41 | 0 | uint8_t root[SLH_MAX_N]; |
42 | 0 | uint32_t layer, mask; |
43 | 0 | const SLH_DSA_PARAMS *params = key->params; |
44 | 0 | uint32_t n = params->n; |
45 | 0 | uint32_t d = params->d; |
46 | 0 | uint32_t hm = params->hm; |
47 | 0 | uint8_t *psig; |
48 | 0 | PACKET rpkt, *xmss_sig_rpkt = &rpkt; |
49 | |
|
50 | 0 | mask = (1 << hm) - 1; /* A mod 2^h = A & ((2^h - 1))) */ |
51 | |
|
52 | 0 | 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 | 0 | memcpy(root, msg, n); |
67 | |
|
68 | 0 | for (layer = 0; layer < d; ++layer) { |
69 | | /* type = SLH_ADRS_TYPE_WOTS_HASH */ |
70 | 0 | adrsf->set_layer_address(adrs, layer); |
71 | 0 | adrsf->set_tree_address(adrs, tree_id); |
72 | 0 | psig = WPACKET_get_curr(sig_wpkt); |
73 | 0 | if (!ossl_slh_xmss_sign(ctx, root, sk_seed, leaf_id, pk_seed, adrs, |
74 | 0 | sig_wpkt)) |
75 | 0 | 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 | 0 | if (layer < d - 1) { |
82 | 0 | if (!PACKET_buf_init(xmss_sig_rpkt, psig, |
83 | 0 | WPACKET_get_curr(sig_wpkt) - psig)) |
84 | 0 | goto err; |
85 | 0 | if (!ossl_slh_xmss_pk_from_sig(ctx, leaf_id, xmss_sig_rpkt, root, |
86 | 0 | pk_seed, adrs, root, sizeof(root))) |
87 | 0 | goto err; |
88 | 0 | leaf_id = tree_id & mask; |
89 | 0 | tree_id >>= hm; |
90 | 0 | } |
91 | 0 | } |
92 | 0 | ret = 1; |
93 | 0 | err: |
94 | 0 | OPENSSL_cleanse(root, sizeof(root)); |
95 | 0 | return ret; |
96 | 0 | } |
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 | | */ |
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 | 0 | { |
116 | 0 | int ret = 0; |
117 | 0 | const SLH_DSA_KEY *key = ctx->key; |
118 | 0 | SLH_ADRS_FUNC_DECLARE(key, adrsf); |
119 | 0 | SLH_ADRS_DECLARE(adrs); |
120 | 0 | uint8_t node[SLH_MAX_N]; |
121 | 0 | const SLH_DSA_PARAMS *params = key->params; |
122 | 0 | uint32_t tree_height = params->hm; |
123 | 0 | uint32_t n = params->n; |
124 | 0 | uint32_t d = params->d; |
125 | 0 | uint32_t mask = (1 << tree_height) - 1; |
126 | 0 | uint32_t layer; |
127 | |
|
128 | 0 | adrsf->zero(adrs); |
129 | 0 | memcpy(node, msg, n); |
130 | |
|
131 | 0 | for (layer = 0; layer < d; ++layer) { |
132 | 0 | adrsf->set_layer_address(adrs, layer); |
133 | 0 | adrsf->set_tree_address(adrs, tree_id); |
134 | 0 | if (!ossl_slh_xmss_pk_from_sig(ctx, leaf_id, sig_pkt, node, |
135 | 0 | pk_seed, adrs, node, sizeof(node))) |
136 | 0 | goto err; |
137 | 0 | leaf_id = tree_id & mask; |
138 | 0 | tree_id >>= tree_height; |
139 | 0 | } |
140 | 0 | ret = (memcmp(node, pk_root, n) == 0); |
141 | 0 | err: |
142 | 0 | OPENSSL_cleanse(node, sizeof(node)); |
143 | 0 | return ret; |
144 | 0 | } |