Coverage Report

Created: 2026-10-28 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl41/crypto/slh_dsa/slh_dsa_key.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 <string.h>
11
#include <openssl/err.h>
12
#include <openssl/core_dispatch.h>
13
#include <openssl/core_names.h>
14
#include <openssl/params.h>
15
#include <openssl/rand.h>
16
#include <openssl/proverr.h>
17
#include "slh_dsa_local.h"
18
#include "slh_dsa_key.h"
19
#include "internal/encoder.h"
20
21
static int slh_dsa_compute_pk_root(SLH_DSA_HASH_CTX *ctx, SLH_DSA_KEY *out, int verify);
22
23
static void slh_dsa_key_hash_cleanup(SLH_DSA_KEY *key)
24
142k
{
25
142k
    OPENSSL_free(key->propq);
26
142k
    EVP_MD_free(key->md_sha512);
27
142k
    EVP_MD_free(key->md);
28
142k
    EVP_MAC_free(key->hmac);
29
142k
    key->md = NULL;
30
142k
    key->md_sha512 = NULL;
31
142k
}
32
33
static int slh_dsa_key_hash_init(SLH_DSA_KEY *key)
34
142k
{
35
142k
    int is_shake = key->params->is_shake;
36
142k
    int security_category = key->params->security_category;
37
142k
    const char *digest_alg = is_shake ? "SHAKE-256" : "SHA2-256";
38
39
142k
    key->md = EVP_MD_fetch(key->libctx, digest_alg, key->propq);
40
142k
    if (key->md == NULL)
41
0
        return 0;
42
    /*
43
     * SHA2 algorithm(s) require SHA256 + HMAC_SHA(X) & MGF1(SHAX)
44
     * SHAKE algorithm(s) use SHAKE for all functions.
45
     */
46
142k
    if (is_shake == 0) {
47
71.2k
        if (security_category != 1) {
48
            /* Security categories 3 & 5 also need SHA-512 */
49
47.7k
            key->md_sha512 = EVP_MD_fetch(key->libctx, "SHA2-512", key->propq);
50
47.7k
            if (key->md_sha512 == NULL)
51
0
                goto err;
52
47.7k
        }
53
71.2k
        key->hmac = EVP_MAC_fetch(key->libctx, "HMAC", key->propq);
54
71.2k
        if (key->hmac == NULL)
55
0
            goto err;
56
71.2k
    }
57
142k
    key->adrs_func = ossl_slh_get_adrs_fn(is_shake == 0);
58
142k
    key->hash_func = ossl_slh_get_hash_fn(is_shake, security_category);
59
142k
    return 1;
60
0
err:
61
0
    return 0;
62
142k
}
63
64
static void slh_dsa_key_hash_dup(SLH_DSA_KEY *dst, const SLH_DSA_KEY *src)
65
4
{
66
4
    if (src->md_sha512 != NULL)
67
0
        EVP_MD_up_ref(src->md_sha512);
68
4
    if (src->md != NULL)
69
4
        EVP_MD_up_ref(src->md);
70
4
    if (src->hmac != NULL)
71
2
        EVP_MAC_up_ref(src->hmac);
72
4
}
73
74
/**
75
 * @brief Return the libctx associated with a SLH_DSA_KEY object
76
 *
77
 * @param key A SLH_DSA_KEY to extract the libctx from.
78
 * @returns The new OSSL_LIB_CTX object on success, or NULL failure
79
 */
80
OSSL_LIB_CTX *ossl_slh_dsa_key_get0_libctx(const SLH_DSA_KEY *key)
81
0
{
82
0
    return key != NULL ? key->libctx : NULL;
83
0
}
84
85
/**
86
 * @brief Create a new SLH_DSA_KEY object
87
 *
88
 * @param libctx A OSSL_LIB_CTX object used for fetching algorithms.
89
 * @param propq The property query used for fetching algorithms
90
 * @param alg The algorithm name associated with the key type
91
 * @returns The new SLH_DSA_KEY object on success, or NULL on malloc failure
92
 */
93
SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *propq,
94
    const char *alg)
95
288k
{
96
288k
    SLH_DSA_KEY *ret;
97
288k
    const SLH_DSA_PARAMS *params = ossl_slh_dsa_params_get(alg);
98
99
288k
    if (params == NULL)
100
0
        return NULL;
101
102
288k
    ret = OPENSSL_zalloc(sizeof(*ret));
103
288k
    if (ret != NULL) {
104
288k
        ret->libctx = libctx;
105
288k
        ret->params = params;
106
288k
        if (propq != NULL) {
107
285k
            ret->propq = OPENSSL_strdup(propq);
108
285k
            if (ret->propq == NULL)
109
0
                goto err;
110
285k
        }
111
288k
        if (!slh_dsa_key_hash_init(ret))
112
0
            goto err;
113
288k
    }
114
288k
    return ret;
115
0
err:
116
0
    ossl_slh_dsa_key_free(ret);
117
0
    return NULL;
118
288k
}
119
120
/**
121
 * @brief Destroy a SLH_DSA_KEY object
122
 */
123
void ossl_slh_dsa_key_free(SLH_DSA_KEY *key)
124
2.24M
{
125
2.24M
    if (key == NULL)
126
1.95M
        return;
127
128
288k
    slh_dsa_key_hash_cleanup(key);
129
288k
    OPENSSL_cleanse(&key->priv, sizeof(key->priv) >> 1);
130
288k
    OPENSSL_free(key);
131
288k
}
132
133
/**
134
 * @brief Duplicate a key
135
 *
136
 * @param src A SLH_DSA_KEY object to copy
137
 * @param selection to select public and/or private components. Selecting the
138
 *                  private key will also select the public key
139
 * @returns The duplicated key, or NULL on failure.
140
 */
141
SLH_DSA_KEY *ossl_slh_dsa_key_dup(const SLH_DSA_KEY *src, int selection)
142
24
{
143
24
    SLH_DSA_KEY *ret = NULL;
144
145
24
    if (src == NULL)
146
0
        return NULL;
147
148
24
    ret = OPENSSL_zalloc(sizeof(*ret));
149
24
    if (ret != NULL) {
150
24
        *ret = *src; /* this copies everything including the keydata in priv[] */
151
24
        ret->propq = NULL;
152
24
        ret->pub = NULL;
153
24
        ret->has_priv = 0;
154
24
        slh_dsa_key_hash_dup(ret, src);
155
24
        if (src->propq != NULL) {
156
24
            ret->propq = OPENSSL_strdup(src->propq);
157
24
            if (ret->propq == NULL)
158
0
                goto err;
159
24
        }
160
24
        if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
161
            /* The public components are present if the private key is present */
162
24
            if (src->pub != NULL)
163
24
                ret->pub = SLH_DSA_PUB(ret);
164
24
            if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
165
24
                ret->has_priv = src->has_priv;
166
24
        }
167
24
    }
168
24
    return ret;
169
0
err:
170
0
    ossl_slh_dsa_key_free(ret);
171
0
    return NULL;
172
24
}
173
174
/**
175
 * @brief Are 2 keys equal?
176
 *
177
 * To be equal the keys must have the same key data and algorithm name.
178
 *
179
 * @param key1 A SLH_DSA_KEY object
180
 * @param key2 A SLH_DSA_KEY object
181
 * @param selection to select public and/or private component comparison.
182
 * @returns 1 if the keys are equal otherwise it returns 0.
183
 */
184
int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2,
185
    int selection)
186
723
{
187
723
    int key_checked = 0;
188
189
    /* The parameter sets must match - i.e. The same algorithm name */
190
723
    if (key1->params != key2->params)
191
0
        return 0;
192
193
723
    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
194
723
        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
195
723
            if (key1->pub != NULL && key2->pub != NULL) {
196
723
                if (memcmp(key1->pub, key2->pub, key1->params->pk_len) != 0)
197
161
                    return 0;
198
562
                key_checked = 1;
199
562
            }
200
723
        }
201
562
        if (!key_checked
202
0
            && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
203
0
            if (key1->has_priv && key2->has_priv) {
204
0
                if (CRYPTO_memcmp(key1->priv, key2->priv,
205
0
                        key1->params->pk_len)
206
0
                    != 0)
207
0
                    return 0;
208
0
                key_checked = 1;
209
0
            }
210
0
        }
211
562
        return key_checked;
212
562
    }
213
0
    return 1;
214
723
}
215
216
int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection)
217
1.75k
{
218
1.75k
    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
219
1.75k
        if (key->pub == NULL)
220
0
            return 0; /* No public key */
221
1.75k
        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
222
73
            && key->has_priv == 0)
223
0
            return 0; /* No private key */
224
1.75k
        return 1;
225
1.75k
    }
226
0
    return 0;
227
1.75k
}
228
229
int ossl_slh_dsa_key_pairwise_check(const SLH_DSA_KEY *key)
230
24
{
231
24
    int ret;
232
24
    SLH_DSA_HASH_CTX *ctx = NULL;
233
234
24
    if (key->pub == NULL || key->has_priv == 0)
235
0
        return 0;
236
237
24
    ctx = ossl_slh_dsa_hash_ctx_new(key);
238
24
    if (ctx == NULL)
239
0
        return 0;
240
24
    ret = slh_dsa_compute_pk_root(ctx, (SLH_DSA_KEY *)key, 1);
241
24
    ossl_slh_dsa_hash_ctx_free(ctx);
242
24
    return ret;
243
24
}
244
245
void ossl_slh_dsa_key_reset(SLH_DSA_KEY *key)
246
101
{
247
101
    key->pub = NULL;
248
101
    if (key->has_priv) {
249
56
        key->has_priv = 0;
250
56
        OPENSSL_cleanse(key->priv, sizeof(key->priv));
251
56
    }
252
101
}
253
254
/**
255
 * @brief Load a SLH_DSA key from raw data.
256
 *
257
 * @param key An SLH_DSA key to load into
258
 * @param params An array of parameters containing key data.
259
 * @param include_private Set to 1 to optionally include the private key data
260
 *                        if it exists.
261
 * @returns 1 on success, or 0 on failure.
262
 */
263
int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM *param_pub,
264
    const OSSL_PARAM *param_priv,
265
    int include_private)
266
727
{
267
727
    size_t priv_len, key_len, data_len = 0;
268
727
    void *p;
269
270
727
    if (key == NULL)
271
0
        return 0;
272
273
    /* The private key consists of 4 elements SK_SEED, SK_PRF, PK_SEED and PK_ROOT */
274
727
    priv_len = ossl_slh_dsa_key_get_priv_len(key);
275
    /* The size of either SK_SEED + SK_PRF OR PK_SEED + PK_ROOT */
276
727
    key_len = priv_len >> 1;
277
278
    /* Private key is optional */
279
727
    if (include_private) {
280
727
        if (param_priv != NULL) {
281
727
            p = key->priv;
282
727
            if (!OSSL_PARAM_get_octet_string(param_priv, &p, priv_len, &data_len))
283
59
                return 0;
284
            /* If the data read includes all 4 elements then we are finished */
285
668
            if (data_len == priv_len) {
286
538
                key->has_priv = 1;
287
538
                key->pub = SLH_DSA_PUB(key);
288
538
                return 1;
289
538
            }
290
            /* Otherwise it must be just SK_SEED + SK_PRF */
291
130
            if (data_len != key_len)
292
45
                goto err;
293
85
            key->has_priv = 1;
294
85
        }
295
727
    }
296
    /*
297
     * In the case where the passed in private key does not contain the public key
298
     * there MUST be a separate public key, since the private key cannot exist
299
     * without the public key elements. NOTE that this does not accept half of
300
     * the public key, (Keygen must be used for this case currently).
301
     */
302
85
    p = SLH_DSA_PUB(key);
303
85
    if (param_pub == NULL
304
85
        || !OSSL_PARAM_get_octet_string(param_pub, &p, key_len, &data_len)
305
48
        || data_len != key_len)
306
56
        goto err;
307
29
    key->pub = p;
308
29
    return 1;
309
101
err:
310
    /*
311
     * A private key of unexpected length may have been copied into |priv|
312
     * before |has_priv| was set, in which case the reset below would not
313
     * erase it, so cleanse unconditionally.
314
     */
315
101
    OPENSSL_cleanse(key->priv, sizeof(key->priv));
316
101
    ossl_slh_dsa_key_reset(key);
317
101
    return 0;
318
85
}
319
320
/**
321
 * Generate the public key root from private key (seed and prf) and public key seed.
322
 * See FIPS 205 Section 9.1 Algorithm 18
323
 *
324
 * @param ctx Contains SLH_DSA algorithm functions and constants.
325
 * @param out An SLH_DSA key containing the private key (seed and prf) and public key seed.
326
 *            The public root key is written to this key.
327
 * @param validate If set to 1 the computed public key is not written to the key,
328
 *                 but will be compared to the existing value.
329
 * @returns 1 if the root key is generated or compared successfully, or 0 on error.
330
 */
331
static int slh_dsa_compute_pk_root(SLH_DSA_HASH_CTX *ctx, SLH_DSA_KEY *out,
332
    int validate)
333
1.97k
{
334
1.97k
    const SLH_DSA_KEY *key = ctx->key;
335
1.97k
    SLH_ADRS_FUNC_DECLARE(key, adrsf);
336
1.97k
    SLH_ADRS_DECLARE(adrs);
337
1.97k
    const SLH_DSA_PARAMS *params = key->params;
338
1.97k
    size_t n = params->n;
339
1.97k
    uint8_t pk_root[SLH_DSA_MAX_N], *dst;
340
341
1.97k
    adrsf->zero(adrs);
342
1.97k
    adrsf->set_layer_address(adrs, params->d - 1);
343
344
1.97k
    dst = validate ? pk_root : SLH_DSA_PK_ROOT(out);
345
346
    /* Generate the ROOT public key */
347
1.97k
    return ossl_slh_xmss_node(ctx, SLH_DSA_SK_SEED(key), 0, params->hm,
348
1.97k
               SLH_DSA_PK_SEED(key), adrs, dst, n)
349
1.97k
        && (validate == 0 || memcmp(dst, SLH_DSA_PK_ROOT(out), n) == 0);
350
1.97k
}
351
352
/**
353
 * @brief Generate a SLH_DSA keypair. The private key seed and prf as well as the
354
 * public key seed are generated using an approved DRBG's. The public key root is
355
 * calculated using these generated values.
356
 * See FIPS 205 Section 10.1 Algorithm 21
357
 *
358
 * @param ctx Contains SLH_DSA algorithm functions and constants
359
 * @param out An SLH_DSA key to write key pair data to.
360
 * @param lib_ctx A library context for fetching RAND algorithms
361
 * @param entropy Optional entropy to use instead of using a DRBG.
362
 *        Required for ACVP testing. It may be NULL.
363
 * @param entropy_len the size of |entropy|. If set it must be at least 3 * |n|.
364
 * @returns 1 if the key is generated or 0 otherwise.
365
 */
366
int ossl_slh_dsa_generate_key(SLH_DSA_HASH_CTX *ctx, SLH_DSA_KEY *out,
367
    OSSL_LIB_CTX *lib_ctx,
368
    const uint8_t *entropy, size_t entropy_len)
369
934
{
370
934
    size_t n = out->params->n;
371
934
    size_t secret_key_len = 2 * n; /* The length of SK_SEED + SK_PRF */
372
934
    size_t pk_seed_len = n; /* The length of PK_SEED */
373
934
    size_t entropy_len_expected = secret_key_len + pk_seed_len;
374
934
    uint8_t *priv = SLH_DSA_PRIV(out);
375
934
    uint8_t *pub = SLH_DSA_PUB(out);
376
377
934
    if (entropy != NULL && entropy_len != 0) {
378
0
        if (entropy_len != entropy_len_expected)
379
0
            goto err;
380
0
        memcpy(priv, entropy, entropy_len_expected);
381
934
    } else {
382
934
        if (RAND_priv_bytes_ex(lib_ctx, priv, secret_key_len, 0) <= 0
383
934
            || RAND_bytes_ex(lib_ctx, pub, pk_seed_len, 0) <= 0)
384
0
            goto err;
385
934
    }
386
387
934
    if (!ossl_slh_dsa_hash_ctx_prehash_pk_seed(ctx, pub, pk_seed_len)
388
934
        || !slh_dsa_compute_pk_root(ctx, out, 0))
389
0
        goto err;
390
934
    out->pub = pub;
391
934
    out->has_priv = 1;
392
934
    return 1;
393
0
err:
394
0
    out->pub = NULL;
395
0
    out->has_priv = 0;
396
0
    OPENSSL_cleanse(priv, secret_key_len);
397
0
    return 0;
398
934
}
399
400
/**
401
 * @brief This is used when a SLH key is used for an operation.
402
 * This checks that the algorithm is the same (i.e. uses the same parameters)
403
 *
404
 * @param ctx Contains SLH_DSA algorithm functions and constants to be used for
405
 *            an operation.
406
 * @param key A SLH_DSA key to use for an operation.
407
 *
408
 * @returns 1 if the algorithm matches, or 0 otherwise.
409
 */
410
int ossl_slh_dsa_key_type_matches(const SLH_DSA_KEY *key, const char *alg)
411
2.26k
{
412
2.26k
    return (OPENSSL_strcasecmp(key->params->alg, alg) == 0);
413
2.26k
}
414
415
/* Returns the public key data or NULL if there is no public key */
416
const uint8_t *ossl_slh_dsa_key_get_pub(const SLH_DSA_KEY *key)
417
4.01k
{
418
4.01k
    return key->pub;
419
4.01k
}
420
421
/* Returns the constant 2 * |n| which is the size of PK_SEED + PK_ROOT */
422
size_t ossl_slh_dsa_key_get_pub_len(const SLH_DSA_KEY *key)
423
288k
{
424
288k
    return 2 * key->params->n;
425
288k
}
426
427
/* Returns the private key data or NULL if there is no private key */
428
const uint8_t *ossl_slh_dsa_key_get_priv(const SLH_DSA_KEY *key)
429
3.96k
{
430
3.96k
    return key->has_priv ? key->priv : NULL;
431
3.96k
}
432
433
/*
434
 * Returns the constant 4 * |n| which is the size of both
435
 * the private and public key components.
436
 * SK_SEED + SK_ROOT + PK_SEED + PK_ROOT
437
 */
438
size_t ossl_slh_dsa_key_get_priv_len(const SLH_DSA_KEY *key)
439
1.43k
{
440
1.43k
    return 4 * key->params->n;
441
1.43k
}
442
443
size_t ossl_slh_dsa_key_get_n(const SLH_DSA_KEY *key)
444
3.62k
{
445
3.62k
    return key->params->n;
446
3.62k
}
447
448
int ossl_slh_dsa_key_get_security_category(const SLH_DSA_KEY *key)
449
2.06k
{
450
2.06k
    return key->params->security_category;
451
2.06k
}
452
453
size_t ossl_slh_dsa_key_get_sig_len(const SLH_DSA_KEY *key)
454
2.73k
{
455
2.73k
    return key->params->sig_len;
456
2.73k
}
457
const char *ossl_slh_dsa_key_get_name(const SLH_DSA_KEY *key)
458
4.23k
{
459
4.23k
    return key->params->alg;
460
4.23k
}
461
int ossl_slh_dsa_key_get_type(const SLH_DSA_KEY *key)
462
2.26k
{
463
2.26k
    return key->params->type;
464
2.26k
}
465
466
int ossl_slh_dsa_set_priv(SLH_DSA_KEY *key, const uint8_t *priv, size_t priv_len)
467
93
{
468
93
    if (ossl_slh_dsa_key_get_priv_len(key) != priv_len)
469
44
        return 0;
470
49
    memcpy(key->priv, priv, priv_len);
471
49
    key->has_priv = 1;
472
49
    key->pub = SLH_DSA_PUB(key);
473
49
    return 1;
474
93
}
475
476
int ossl_slh_dsa_set_pub(SLH_DSA_KEY *key, const uint8_t *pub, size_t pub_len)
477
158
{
478
158
    if (ossl_slh_dsa_key_get_pub_len(key) != pub_len)
479
16
        return 0;
480
142
    key->pub = SLH_DSA_PUB(key);
481
142
    memcpy(key->pub, pub, pub_len);
482
142
    key->has_priv = 0;
483
142
    return 1;
484
158
}
485
486
#ifndef FIPS_MODULE
487
int ossl_slh_dsa_key_to_text(BIO *out, const SLH_DSA_KEY *key, int selection)
488
97
{
489
97
    const char *name;
490
491
97
    if (out == NULL || key == NULL) {
492
0
        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
493
0
        return 0;
494
0
    }
495
97
    name = ossl_slh_dsa_key_get_name(key);
496
97
    if (ossl_slh_dsa_key_get_pub(key) == NULL) {
497
        /* Regardless of the |selection|, there must be a public key */
498
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
499
0
            "no %s key material available", name);
500
0
        return 0;
501
0
    }
502
503
97
    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
504
49
        if (ossl_slh_dsa_key_get_priv(key) == NULL) {
505
0
            ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
506
0
                "no %s key material available", name);
507
0
            return 0;
508
0
        }
509
49
        if (BIO_printf(out, "%s Private-Key:\n", name) <= 0)
510
0
            return 0;
511
49
        if (!ossl_bio_print_labeled_buf(out, "priv:", ossl_slh_dsa_key_get_priv(key),
512
49
                ossl_slh_dsa_key_get_priv_len(key)))
513
0
            return 0;
514
49
    } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
515
24
        if (BIO_printf(out, "%s Public-Key:\n", name) <= 0)
516
0
            return 0;
517
24
    }
518
519
97
    if (!ossl_bio_print_labeled_buf(out, "pub:", ossl_slh_dsa_key_get_pub(key),
520
97
            ossl_slh_dsa_key_get_pub_len(key)))
521
0
        return 0;
522
523
97
    return 1;
524
97
}
525
#endif /* FIPS_MODULE */