Coverage Report

Created: 2025-08-28 07:07

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