Coverage Report

Created: 2026-09-12 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl36/crypto/slh_dsa/slh_hash.c
Line
Count
Source
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 "internal/deprecated.h" /* PKCS1_MGF1() */
11
12
#include <string.h>
13
#include <openssl/evp.h>
14
#include <openssl/core_names.h>
15
#include <openssl/rsa.h> /* PKCS1_MGF1() */
16
#include "slh_dsa_local.h"
17
#include "slh_dsa_key.h"
18
19
18.6M
#define MAX_DIGEST_SIZE 64 /* SHA-512 is used for security category 3 & 5 */
20
21
static OSSL_SLH_HASHFUNC_H_MSG slh_hmsg_sha2;
22
static OSSL_SLH_HASHFUNC_PRF slh_prf_sha2;
23
static OSSL_SLH_HASHFUNC_PRF_MSG slh_prf_msg_sha2;
24
static OSSL_SLH_HASHFUNC_F slh_f_sha2;
25
static OSSL_SLH_HASHFUNC_H slh_h_sha2;
26
static OSSL_SLH_HASHFUNC_T slh_t_sha2;
27
28
static OSSL_SLH_HASHFUNC_H_MSG slh_hmsg_shake;
29
static OSSL_SLH_HASHFUNC_PRF slh_prf_shake;
30
static OSSL_SLH_HASHFUNC_PRF_MSG slh_prf_msg_shake;
31
static OSSL_SLH_HASHFUNC_F slh_f_shake;
32
static OSSL_SLH_HASHFUNC_H slh_h_shake;
33
static OSSL_SLH_HASHFUNC_T slh_t_shake;
34
35
static ossl_inline int xof_digest_3(EVP_MD_CTX *ctx,
36
    const uint8_t *in1, size_t in1_len,
37
    const uint8_t *in2, size_t in2_len,
38
    const uint8_t *in3, size_t in3_len,
39
    uint8_t *out, size_t out_len)
40
264M
{
41
264M
    return (EVP_DigestInit_ex2(ctx, NULL, NULL) == 1
42
264M
        && EVP_DigestUpdate(ctx, in1, in1_len) == 1
43
264M
        && EVP_DigestUpdate(ctx, in2, in2_len) == 1
44
264M
        && EVP_DigestUpdate(ctx, in3, in3_len) == 1
45
264M
        && EVP_DigestFinalXOF(ctx, out, out_len) == 1);
46
264M
}
47
48
static ossl_inline int xof_digest_4(EVP_MD_CTX *ctx,
49
    const uint8_t *in1, size_t in1_len,
50
    const uint8_t *in2, size_t in2_len,
51
    const uint8_t *in3, size_t in3_len,
52
    const uint8_t *in4, size_t in4_len,
53
    uint8_t *out, size_t out_len)
54
19.3M
{
55
19.3M
    return (EVP_DigestInit_ex2(ctx, NULL, NULL) == 1
56
19.3M
        && EVP_DigestUpdate(ctx, in1, in1_len) == 1
57
19.3M
        && EVP_DigestUpdate(ctx, in2, in2_len) == 1
58
19.3M
        && EVP_DigestUpdate(ctx, in3, in3_len) == 1
59
19.3M
        && EVP_DigestUpdate(ctx, in4, in4_len) == 1
60
19.3M
        && EVP_DigestFinalXOF(ctx, out, out_len) == 1);
61
19.3M
}
62
63
/* See FIPS 205 Section 11.1 */
64
static int
65
slh_hmsg_shake(SLH_DSA_HASH_CTX *ctx, const uint8_t *r,
66
    const uint8_t *pk_seed, const uint8_t *pk_root,
67
    const uint8_t *msg, size_t msg_len,
68
    uint8_t *out, size_t out_len)
69
652
{
70
652
    const SLH_DSA_PARAMS *params = ctx->key->params;
71
652
    size_t m = params->m;
72
652
    size_t n = params->n;
73
74
652
    return xof_digest_4(ctx->md_ctx, r, n, pk_seed, n, pk_root, n,
75
652
        msg, msg_len, out, m);
76
652
}
77
78
static int
79
slh_prf_shake(SLH_DSA_HASH_CTX *ctx,
80
    const uint8_t *pk_seed, const uint8_t *sk_seed,
81
    const uint8_t *adrs, uint8_t *out, size_t out_len)
82
48.2M
{
83
48.2M
    const SLH_DSA_PARAMS *params = ctx->key->params;
84
48.2M
    size_t n = params->n;
85
86
48.2M
    return xof_digest_3(ctx->md_ctx, pk_seed, n, adrs, SLH_ADRS_SIZE,
87
48.2M
        sk_seed, n, out, n);
88
48.2M
}
89
90
static int
91
slh_prf_msg_shake(SLH_DSA_HASH_CTX *ctx, const uint8_t *sk_prf,
92
    const uint8_t *opt_rand, const uint8_t *msg, size_t msg_len,
93
    WPACKET *pkt)
94
209
{
95
209
    int ret;
96
209
    unsigned char out[SLH_MAX_N];
97
209
    const SLH_DSA_PARAMS *params = ctx->key->params;
98
209
    size_t n = params->n;
99
100
209
    ret = xof_digest_3(ctx->md_ctx, sk_prf, n, opt_rand, n, msg, msg_len, out, n)
101
209
        && WPACKET_memcpy(pkt, out, n);
102
209
    OPENSSL_cleanse(out, sizeof(out));
103
209
    return ret;
104
209
}
105
106
static int
107
slh_f_shake(SLH_DSA_HASH_CTX *ctx, const uint8_t *pk_seed, const uint8_t *adrs,
108
    const uint8_t *m1, size_t m1_len, uint8_t *out, size_t out_len)
109
248M
{
110
248M
    const SLH_DSA_PARAMS *params = ctx->key->params;
111
248M
    size_t n = params->n;
112
113
248M
    return xof_digest_3(ctx->md_ctx, pk_seed, n, adrs, SLH_ADRS_SIZE, m1, m1_len, out, n);
114
248M
}
115
116
static int
117
slh_h_shake(SLH_DSA_HASH_CTX *ctx, const uint8_t *pk_seed, const uint8_t *adrs,
118
    const uint8_t *m1, const uint8_t *m2, uint8_t *out, size_t out_len)
119
34.6M
{
120
34.6M
    const SLH_DSA_PARAMS *params = ctx->key->params;
121
34.6M
    size_t n = params->n;
122
123
34.6M
    return xof_digest_4(ctx->md_ctx, pk_seed, n, adrs, SLH_ADRS_SIZE, m1, n, m2, n, out, n);
124
34.6M
}
125
126
static int
127
slh_t_shake(SLH_DSA_HASH_CTX *ctx, const uint8_t *pk_seed, const uint8_t *adrs,
128
    const uint8_t *ml, size_t ml_len, uint8_t *out, size_t out_len)
129
273k
{
130
273k
    const SLH_DSA_PARAMS *params = ctx->key->params;
131
273k
    size_t n = params->n;
132
133
273k
    return xof_digest_3(ctx->md_ctx, pk_seed, n, adrs, SLH_ADRS_SIZE, ml, ml_len, out, n);
134
273k
}
135
136
static ossl_inline int
137
digest_4(EVP_MD_CTX *ctx,
138
    const uint8_t *in1, size_t in1_len, const uint8_t *in2, size_t in2_len,
139
    const uint8_t *in3, size_t in3_len, const uint8_t *in4, size_t in4_len,
140
    uint8_t *out)
141
363M
{
142
363M
    return (EVP_DigestInit_ex2(ctx, NULL, NULL) == 1
143
363M
        && EVP_DigestUpdate(ctx, in1, in1_len) == 1
144
363M
        && EVP_DigestUpdate(ctx, in2, in2_len) == 1
145
363M
        && EVP_DigestUpdate(ctx, in3, in3_len) == 1
146
363M
        && EVP_DigestUpdate(ctx, in4, in4_len) == 1
147
363M
        && EVP_DigestFinal_ex(ctx, out, NULL) == 1);
148
363M
}
149
150
/* FIPS 205 Section 11.2.1 and 11.2.2 */
151
152
static int
153
slh_hmsg_sha2(SLH_DSA_HASH_CTX *hctx, const uint8_t *r, const uint8_t *pk_seed,
154
    const uint8_t *pk_root, const uint8_t *msg, size_t msg_len,
155
    uint8_t *out, size_t out_len)
156
848
{
157
848
    int ret;
158
848
    const SLH_DSA_PARAMS *params = hctx->key->params;
159
848
    size_t m = params->m;
160
848
    size_t n = params->n;
161
848
    uint8_t seed[2 * SLH_MAX_N + MAX_DIGEST_SIZE];
162
848
    int sz = EVP_MD_get_size(hctx->key->md_big);
163
848
    size_t seed_len = (size_t)sz + 2 * n;
164
165
848
    if (sz <= 0)
166
0
        return 0;
167
168
848
    memcpy(seed, r, n);
169
848
    memcpy(seed + n, pk_seed, n);
170
848
    ret = digest_4(hctx->md_big_ctx, r, n, pk_seed, n, pk_root, n, msg, msg_len,
171
848
              seed + 2 * n)
172
848
        && (PKCS1_MGF1(out, (long)m, seed, (long)seed_len, hctx->key->md_big) == 0);
173
848
    OPENSSL_cleanse(seed, sizeof(seed));
174
848
    return ret;
175
848
}
176
177
static int
178
slh_prf_msg_sha2(SLH_DSA_HASH_CTX *hctx,
179
    const uint8_t *sk_prf, const uint8_t *opt_rand,
180
    const uint8_t *msg, size_t msg_len, WPACKET *pkt)
181
424
{
182
424
    int ret;
183
424
    const SLH_DSA_KEY *key = hctx->key;
184
424
    EVP_MAC_CTX *mctx = hctx->hmac_ctx;
185
424
    const SLH_DSA_PARAMS *prms = key->params;
186
424
    size_t n = prms->n;
187
424
    uint8_t mac[MAX_DIGEST_SIZE] = { 0 };
188
424
    OSSL_PARAM *p = NULL;
189
424
    OSSL_PARAM params[3];
190
191
    /*
192
     * Due to the way HMAC works, it is not possible to do this code early
193
     * in hmac_ctx_new() since it requires a key in order to set the digest.
194
     * So we do a lazy update here on the first call.
195
     */
196
424
    if (hctx->hmac_digest_used == 0) {
197
424
        p = params;
198
        /* The underlying digest to be used */
199
424
        *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
200
424
            (char *)EVP_MD_get0_name(key->md_big), 0);
201
424
        if (key->propq != NULL)
202
0
            *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_PROPERTIES,
203
0
                (char *)key->propq, 0);
204
424
        *p = OSSL_PARAM_construct_end();
205
424
        p = params;
206
424
        hctx->hmac_digest_used = 1;
207
424
    }
208
209
424
    ret = EVP_MAC_init(mctx, sk_prf, n, p) == 1
210
424
        && EVP_MAC_update(mctx, opt_rand, n) == 1
211
424
        && EVP_MAC_update(mctx, msg, msg_len) == 1
212
424
        && EVP_MAC_final(mctx, mac, NULL, sizeof(mac)) == 1
213
424
        && WPACKET_memcpy(pkt, mac, n); /* Truncate output to n bytes */
214
424
    OPENSSL_cleanse(mac, sizeof(mac));
215
424
    return ret;
216
424
}
217
218
/*
219
 * The |digest| scratch storage in the hash context is used in place of a
220
 * local stack buffer, and is erased when the hash context is freed
221
 * (FIPS 205 section 3.1).  On the PRF path it holds a derived chain secret.
222
 */
223
static ossl_inline int
224
do_hash(SLH_DSA_HASH_CTX *hctx, EVP_MD_CTX *ctx, size_t n,
225
    const uint8_t *pk_seed, const uint8_t *adrs,
226
    const uint8_t *m, size_t m_len, size_t b, uint8_t *out, size_t out_len)
227
363M
{
228
363M
    int ret;
229
363M
    uint8_t zeros[128] = { 0 };
230
363M
    uint8_t *digest = hctx->scratch;
231
232
363M
    ret = digest_4(ctx, pk_seed, n, zeros, b - n, adrs, SLH_ADRSC_SIZE,
233
363M
        m, m_len, digest);
234
    /* Truncated returned value is n = 16 bytes */
235
363M
    memcpy(out, digest, n);
236
363M
    return ret;
237
363M
}
238
239
static int
240
slh_prf_sha2(SLH_DSA_HASH_CTX *hctx, const uint8_t *pk_seed,
241
    const uint8_t *sk_seed, const uint8_t *adrs,
242
    uint8_t *out, size_t out_len)
243
37.2M
{
244
37.2M
    size_t n = hctx->key->params->n;
245
246
37.2M
    return do_hash(hctx, hctx->md_ctx, n, pk_seed, adrs, sk_seed, n,
247
37.2M
        OSSL_SLH_DSA_SHA2_NUM_ZEROS_H_AND_T_BOUND1, out, out_len);
248
37.2M
}
249
250
static int
251
slh_f_sha2(SLH_DSA_HASH_CTX *hctx, const uint8_t *pk_seed, const uint8_t *adrs,
252
    const uint8_t *m1, size_t m1_len, uint8_t *out, size_t out_len)
253
307M
{
254
307M
    return do_hash(hctx, hctx->md_ctx, hctx->key->params->n, pk_seed, adrs,
255
307M
        m1, m1_len, OSSL_SLH_DSA_SHA2_NUM_ZEROS_H_AND_T_BOUND1, out, out_len);
256
307M
}
257
258
static int
259
slh_h_sha2(SLH_DSA_HASH_CTX *hctx, const uint8_t *pk_seed, const uint8_t *adrs,
260
    const uint8_t *m1, const uint8_t *m2, uint8_t *out, size_t out_len)
261
18.6M
{
262
    /* The concatenated children go in the scratch after the digest */
263
18.6M
    uint8_t *m = hctx->scratch + MAX_DIGEST_SIZE;
264
18.6M
    const SLH_DSA_PARAMS *prms = hctx->key->params;
265
18.6M
    size_t n = prms->n;
266
267
18.6M
    memcpy(m, m1, n);
268
18.6M
    memcpy(m + n, m2, n);
269
18.6M
    return do_hash(hctx, hctx->md_big_ctx, n, pk_seed, adrs, m, 2 * n,
270
18.6M
        prms->sha2_h_and_t_bound, out, out_len);
271
18.6M
}
272
273
static int
274
slh_t_sha2(SLH_DSA_HASH_CTX *hctx, const uint8_t *pk_seed, const uint8_t *adrs,
275
    const uint8_t *ml, size_t ml_len, uint8_t *out, size_t out_len)
276
441k
{
277
441k
    const SLH_DSA_PARAMS *prms = hctx->key->params;
278
279
441k
    return do_hash(hctx, hctx->md_big_ctx, prms->n, pk_seed, adrs, ml, ml_len,
280
441k
        prms->sha2_h_and_t_bound, out, out_len);
281
441k
}
282
283
const SLH_HASH_FUNC *ossl_slh_get_hash_fn(int is_shake)
284
142k
{
285
142k
    static const SLH_HASH_FUNC methods[] = {
286
142k
        { slh_hmsg_shake,
287
142k
            slh_prf_shake,
288
142k
            slh_prf_msg_shake,
289
142k
            slh_f_shake,
290
142k
            slh_h_shake,
291
142k
            slh_t_shake },
292
142k
        { slh_hmsg_sha2,
293
142k
            slh_prf_sha2,
294
142k
            slh_prf_msg_sha2,
295
142k
            slh_f_sha2,
296
142k
            slh_h_sha2,
297
142k
            slh_t_sha2 }
298
142k
    };
299
142k
    return &methods[is_shake ? 0 : 1];
300
142k
}