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