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