Coverage Report

Created: 2025-08-28 07:07

/src/openssl35/crypto/slh_dsa/slh_hypertree.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 Generate a Hypertree Signature
16
 * See FIPS 205 Section 7.1 Algorithm 12
17
 *
18
 * This writes |d| XMSS signatures i.e. ((|h| + |d| * |len|) * |n|)
19
 * where the first signature uses the XMSS key at the lowest layer, and the last
20
 * signature uses the XMSS key at the top layer.
21
 *
22
 * @param ctx Contains SLH_DSA algorithm functions and constants.
23
 * @param msg A message of size |n|.
24
 * @param sk_seed The private key seed of size |n|
25
 * @param pk_seed The public key seed of size |n|
26
 * @param tree_id Index of the XMSS tree that will sign the message
27
 * @param leaf_id Index of the WOTS+ key within the XMSS tree that will sign the message
28
 * @param sig_wpkt A WPACKET object to write the Hypertree Signature to.
29
 * @returns 1 on success, or 0 on error.
30
 */
31
int ossl_slh_ht_sign(SLH_DSA_HASH_CTX *ctx,
32
                     const uint8_t *msg, const uint8_t *sk_seed,
33
                     const uint8_t *pk_seed,
34
                     uint64_t tree_id, uint32_t leaf_id, WPACKET *sig_wpkt)
35
569
{
36
569
    const SLH_DSA_KEY *key = ctx->key;
37
569
    SLH_ADRS_FUNC_DECLARE(key, adrsf);
38
569
    SLH_ADRS_DECLARE(adrs);
39
569
    uint8_t root[SLH_MAX_N];
40
569
    uint32_t layer, mask;
41
569
    const SLH_DSA_PARAMS *params = key->params;
42
569
    uint32_t n = params->n;
43
569
    uint32_t d = params->d;
44
569
    uint32_t hm = params->hm;
45
569
    uint8_t *psig;
46
569
    PACKET rpkt, *xmss_sig_rpkt = &rpkt;
47
48
569
    mask = (1 << hm) - 1; /* A mod 2^h = A & ((2^h - 1))) */
49
50
569
    adrsf->zero(adrs);
51
    /*
52
     * For each XMSS tree there is a current leaf node that is used for signing.
53
     * The first iteration of the loop signs the input message using the bottom
54
     * tree. Subsequent passes use the parent trees leaf node to sign the current
55
     * trees public key.
56
     * Each node in an XMSS tree has a sibling (except for the root node),
57
     * so starting at the leaf node it traverses up the tree calculating
58
     * hashes for all the siblings in the path to the root node,
59
     * which are then stored in the XMSS signature. The verify then just needs
60
     * the hash of the leaf node which is can then combine with the signature
61
     * path hashes to work all the way up to the root node to calculate the
62
     * public key.
63
     */
64
569
    memcpy(root, msg, n);
65
66
9.66k
    for (layer = 0; layer < d; ++layer) {
67
        /* type = SLH_ADRS_TYPE_WOTS_HASH */
68
9.09k
        adrsf->set_layer_address(adrs, layer);
69
9.09k
        adrsf->set_tree_address(adrs, tree_id);
70
9.09k
        psig = WPACKET_get_curr(sig_wpkt);
71
9.09k
        if (!ossl_slh_xmss_sign(ctx, root, sk_seed, leaf_id, pk_seed, adrs,
72
9.09k
                                sig_wpkt))
73
0
            return 0;
74
        /*
75
         * On the last loop it skips getting the public key since it is not needed
76
         * to calculate another signature. If this was called it should equal
77
         * the PK_ROOT (i.e. the public key of the top level tree).
78
         */
79
9.09k
        if (layer < d - 1) {
80
8.52k
            if (!PACKET_buf_init(xmss_sig_rpkt, psig,
81
8.52k
                                 WPACKET_get_curr(sig_wpkt) - psig))
82
0
                return 0;
83
8.52k
            if (!ossl_slh_xmss_pk_from_sig(ctx, leaf_id, xmss_sig_rpkt, root,
84
8.52k
                                           pk_seed, adrs, root, sizeof(root)))
85
0
                return 0;
86
8.52k
            leaf_id = tree_id & mask;
87
8.52k
            tree_id >>= hm;
88
8.52k
        }
89
9.09k
    }
90
569
    return 1;
91
569
}
92
93
/**
94
 * @brief Verify a Hypertree Signature
95
 * See FIPS 205 Section 7.2 Algorithm 13
96
 *
97
 * @param ctx Contains SLH_DSA algorithm functions and constants.
98
 * @param msg A message of size |n| bytes
99
 * @param sig A HT signature of size (|h| + |d| * |len|) * |n| bytes
100
 * @param pk_seed SLH_DSA public key seed of size |n|
101
 * @param tree_id Index of the XMSS tree that signed the message
102
 * @param leaf_id Index of the WOTS+ key within the XMSS tree that signed the message
103
 * @param pk_root The known Hypertree public key of size |n|
104
 *
105
 * @returns 1 if the computed XMSS public key matches pk_root, or 0 otherwise.
106
 */
107
int ossl_slh_ht_verify(SLH_DSA_HASH_CTX *ctx, const uint8_t *msg, PACKET *sig_pkt,
108
                       const uint8_t *pk_seed, uint64_t tree_id, uint32_t leaf_id,
109
                       const uint8_t *pk_root)
110
569
{
111
569
    const SLH_DSA_KEY *key = ctx->key;
112
569
    SLH_ADRS_FUNC_DECLARE(key, adrsf);
113
569
    SLH_ADRS_DECLARE(adrs);
114
569
    uint8_t node[SLH_MAX_N];
115
569
    const SLH_DSA_PARAMS *params = key->params;
116
569
    uint32_t tree_height = params->hm;
117
569
    uint32_t n = params->n;
118
569
    uint32_t d = params->d;
119
569
    uint32_t mask = (1 << tree_height) - 1;
120
569
    uint32_t layer;
121
122
569
    adrsf->zero(adrs);
123
569
    memcpy(node, msg, n);
124
125
9.66k
    for (layer = 0; layer < d; ++layer) {
126
9.09k
        adrsf->set_layer_address(adrs, layer);
127
9.09k
        adrsf->set_tree_address(adrs, tree_id);
128
9.09k
        if (!ossl_slh_xmss_pk_from_sig(ctx, leaf_id, sig_pkt, node,
129
9.09k
                                       pk_seed, adrs, node, sizeof(node)))
130
0
            return 0;
131
9.09k
        leaf_id = tree_id & mask;
132
9.09k
        tree_id >>= tree_height;
133
9.09k
    }
134
569
    return (memcmp(node, pk_root, n) == 0);
135
569
}