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
slh_dsa_key_hash_cleanup(SLH_DSA_KEY * key)23 static void slh_dsa_key_hash_cleanup(SLH_DSA_KEY *key)
24 {
25 OPENSSL_free(key->propq);
26 if (key->md_big != key->md)
27 EVP_MD_free(key->md_big);
28 key->md_big = NULL;
29 EVP_MD_free(key->md);
30 EVP_MAC_free(key->hmac);
31 key->md = NULL;
32 }
33
slh_dsa_key_hash_init(SLH_DSA_KEY * key)34 static int slh_dsa_key_hash_init(SLH_DSA_KEY *key)
35 {
36 int is_shake = key->params->is_shake;
37 int security_category = key->params->security_category;
38 const char *digest_alg = is_shake ? "SHAKE-256" : "SHA2-256";
39
40 key->md = EVP_MD_fetch(key->libctx, digest_alg, key->propq);
41 if (key->md == NULL)
42 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 if (is_shake == 0) {
48 if (security_category == 1) {
49 /* For category 1 SHA2-256 is used for all hash operations */
50 key->md_big = key->md;
51 } else {
52 /* Security categories 3 & 5 also need SHA-512 */
53 key->md_big = EVP_MD_fetch(key->libctx, "SHA2-512", key->propq);
54 if (key->md_big == NULL)
55 goto err;
56 }
57 key->hmac = EVP_MAC_fetch(key->libctx, "HMAC", key->propq);
58 if (key->hmac == NULL)
59 goto err;
60 }
61 key->adrs_func = ossl_slh_get_adrs_fn(is_shake == 0);
62 key->hash_func = ossl_slh_get_hash_fn(is_shake);
63 return 1;
64 err:
65 slh_dsa_key_hash_cleanup(key);
66 return 0;
67 }
68
slh_dsa_key_hash_dup(SLH_DSA_KEY * dst,const SLH_DSA_KEY * src)69 static void slh_dsa_key_hash_dup(SLH_DSA_KEY *dst, const SLH_DSA_KEY *src)
70 {
71 if (src->md_big != NULL && src->md_big != src->md)
72 EVP_MD_up_ref(src->md_big);
73 if (src->md != NULL)
74 EVP_MD_up_ref(src->md);
75 if (src->hmac != NULL)
76 EVP_MAC_up_ref(src->hmac);
77 }
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 */
ossl_slh_dsa_key_new(OSSL_LIB_CTX * libctx,const char * propq,const char * alg)87 SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *propq,
88 const char *alg)
89 {
90 SLH_DSA_KEY *ret;
91 const SLH_DSA_PARAMS *params = ossl_slh_dsa_params_get(alg);
92
93 if (params == NULL)
94 return NULL;
95
96 ret = OPENSSL_zalloc(sizeof(*ret));
97 if (ret != NULL) {
98 ret->libctx = libctx;
99 ret->params = params;
100 if (propq != NULL) {
101 ret->propq = OPENSSL_strdup(propq);
102 if (ret->propq == NULL)
103 goto err;
104 }
105 if (!slh_dsa_key_hash_init(ret))
106 goto err;
107 }
108 return ret;
109 err:
110 ossl_slh_dsa_key_free(ret);
111 return NULL;
112 }
113
114 /**
115 * @brief Destroy a SLH_DSA_KEY object
116 */
ossl_slh_dsa_key_free(SLH_DSA_KEY * key)117 void ossl_slh_dsa_key_free(SLH_DSA_KEY *key)
118 {
119 if (key == NULL)
120 return;
121
122 slh_dsa_key_hash_cleanup(key);
123 OPENSSL_cleanse(&key->priv, sizeof(key->priv) >> 1);
124 OPENSSL_free(key);
125 }
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 */
ossl_slh_dsa_key_dup(const SLH_DSA_KEY * src,int selection)135 SLH_DSA_KEY *ossl_slh_dsa_key_dup(const SLH_DSA_KEY *src, int selection)
136 {
137 SLH_DSA_KEY *ret = NULL;
138
139 if (src == NULL)
140 return NULL;
141
142 ret = OPENSSL_zalloc(sizeof(*ret));
143 if (ret != NULL) {
144 *ret = *src; /* this copies everything including the keydata in priv[] */
145 ret->propq = NULL;
146 ret->pub = NULL;
147 ret->has_priv = 0;
148 slh_dsa_key_hash_dup(ret, src);
149 if (src->propq != NULL) {
150 ret->propq = OPENSSL_strdup(src->propq);
151 if (ret->propq == NULL)
152 goto err;
153 }
154 if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
155 /* The public components are present if the private key is present */
156 if (src->pub != NULL)
157 ret->pub = SLH_DSA_PUB(ret);
158 if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
159 ret->has_priv = src->has_priv;
160 }
161 }
162 return ret;
163 err:
164 ossl_slh_dsa_key_free(ret);
165 return NULL;
166 }
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 */
ossl_slh_dsa_key_equal(const SLH_DSA_KEY * key1,const SLH_DSA_KEY * key2,int selection)178 int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2,
179 int selection)
180 {
181 int key_checked = 0;
182
183 /* The parameter sets must match - i.e. The same algorithm name */
184 if (key1->params != key2->params)
185 return 0;
186
187 if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
188 if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
189 if (key1->pub != NULL && key2->pub != NULL) {
190 if (memcmp(key1->pub, key2->pub, key1->params->pk_len) != 0)
191 return 0;
192 key_checked = 1;
193 }
194 }
195 if (!key_checked
196 && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
197 if (key1->has_priv && key2->has_priv) {
198 if (memcmp(key1->priv, key2->priv,
199 key1->params->pk_len) != 0)
200 return 0;
201 key_checked = 1;
202 }
203 }
204 return key_checked;
205 }
206 return 1;
207 }
208
ossl_slh_dsa_key_has(const SLH_DSA_KEY * key,int selection)209 int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection)
210 {
211 if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
212 if (key->pub == NULL)
213 return 0; /* No public key */
214 if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
215 && key->has_priv == 0)
216 return 0; /* No private key */
217 return 1;
218 }
219 return 0;
220 }
221
ossl_slh_dsa_key_pairwise_check(const SLH_DSA_KEY * key)222 int ossl_slh_dsa_key_pairwise_check(const SLH_DSA_KEY *key)
223 {
224 int ret;
225 SLH_DSA_HASH_CTX *ctx = NULL;
226
227 if (key->pub == NULL || key->has_priv == 0)
228 return 0;
229
230 ctx = ossl_slh_dsa_hash_ctx_new(key);
231 if (ctx == NULL)
232 return 0;
233 ret = slh_dsa_compute_pk_root(ctx, (SLH_DSA_KEY *)key, 1);
234 ossl_slh_dsa_hash_ctx_free(ctx);
235 return ret;
236 }
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 */
ossl_slh_dsa_key_fromdata(SLH_DSA_KEY * key,const OSSL_PARAM params[],int include_private)247 int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[],
248 int include_private)
249 {
250 size_t priv_len, key_len, data_len = 0;
251 const OSSL_PARAM *param_priv = NULL, *param_pub = NULL;
252 void *p;
253
254 if (key == NULL)
255 return 0;
256
257 /* The private key consists of 4 elements SK_SEED, SK_PRF, PK_SEED and PK_ROOT */
258 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 key_len = priv_len >> 1;
261
262 /* Private key is optional */
263 if (include_private) {
264 param_priv = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
265 if (param_priv != NULL) {
266 p = key->priv;
267 if (!OSSL_PARAM_get_octet_string(param_priv, &p, priv_len, &data_len))
268 return 0;
269 /* If the data read includes all 4 elements then we are finished */
270 if (data_len == priv_len) {
271 key->has_priv = 1;
272 key->pub = SLH_DSA_PUB(key);
273 return 1;
274 }
275 /* Otherwise it must be just SK_SEED + SK_PRF */
276 if (data_len != key_len)
277 goto err;
278 key->has_priv = 1;
279 }
280 }
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 p = SLH_DSA_PUB(key);
288 param_pub = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
289 if (param_pub == NULL
290 || !OSSL_PARAM_get_octet_string(param_pub, &p, key_len, &data_len)
291 || data_len != key_len)
292 goto err;
293 key->pub = p;
294 return 1;
295 err:
296 key->pub = NULL;
297 key->has_priv = 0;
298 OPENSSL_cleanse(key->priv, priv_len);
299 return 0;
300 }
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 */
slh_dsa_compute_pk_root(SLH_DSA_HASH_CTX * ctx,SLH_DSA_KEY * out,int validate)313 static int slh_dsa_compute_pk_root(SLH_DSA_HASH_CTX *ctx, SLH_DSA_KEY *out,
314 int validate)
315 {
316 const SLH_DSA_KEY *key = ctx->key;
317 SLH_ADRS_FUNC_DECLARE(key, adrsf);
318 SLH_ADRS_DECLARE(adrs);
319 const SLH_DSA_PARAMS *params = key->params;
320 size_t n = params->n;
321 uint8_t pk_root[SLH_DSA_MAX_N], *dst;
322
323 adrsf->zero(adrs);
324 adrsf->set_layer_address(adrs, params->d - 1);
325
326 dst = validate ? pk_root : SLH_DSA_PK_ROOT(out);
327
328 /* Generate the ROOT public key */
329 return ossl_slh_xmss_node(ctx, SLH_DSA_SK_SEED(key), 0, params->hm,
330 SLH_DSA_PK_SEED(key), adrs, dst, n)
331 && (validate == 0 || memcmp(dst, SLH_DSA_PK_ROOT(out), n) == 0);
332 }
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 */
ossl_slh_dsa_generate_key(SLH_DSA_HASH_CTX * ctx,SLH_DSA_KEY * out,OSSL_LIB_CTX * lib_ctx,const uint8_t * entropy,size_t entropy_len)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 {
352 size_t n = out->params->n;
353 size_t secret_key_len = 2 * n; /* The length of SK_SEED + SK_PRF */
354 size_t pk_seed_len = n; /* The length of PK_SEED */
355 size_t entropy_len_expected = secret_key_len + pk_seed_len;
356 uint8_t *priv = SLH_DSA_PRIV(out);
357 uint8_t *pub = SLH_DSA_PUB(out);
358
359 if (entropy != NULL && entropy_len != 0) {
360 if (entropy_len != entropy_len_expected)
361 goto err;
362 memcpy(priv, entropy, entropy_len_expected);
363 } else {
364 if (RAND_priv_bytes_ex(lib_ctx, priv, secret_key_len, 0) <= 0
365 || RAND_bytes_ex(lib_ctx, pub, pk_seed_len, 0) <= 0)
366 goto err;
367 }
368 if (!slh_dsa_compute_pk_root(ctx, out, 0))
369 goto err;
370 out->pub = pub;
371 out->has_priv = 1;
372 return 1;
373 err:
374 out->pub = NULL;
375 out->has_priv = 0;
376 OPENSSL_cleanse(priv, secret_key_len);
377 return 0;
378 }
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 */
ossl_slh_dsa_key_type_matches(const SLH_DSA_KEY * key,const char * alg)390 int ossl_slh_dsa_key_type_matches(const SLH_DSA_KEY *key, const char *alg)
391 {
392 return (OPENSSL_strcasecmp(key->params->alg, alg) == 0);
393 }
394
395 /* Returns the public key data or NULL if there is no public key */
ossl_slh_dsa_key_get_pub(const SLH_DSA_KEY * key)396 const uint8_t *ossl_slh_dsa_key_get_pub(const SLH_DSA_KEY *key)
397 {
398 return key->pub;
399 }
400
401 /* Returns the constant 2 * |n| which is the size of PK_SEED + PK_ROOT */
ossl_slh_dsa_key_get_pub_len(const SLH_DSA_KEY * key)402 size_t ossl_slh_dsa_key_get_pub_len(const SLH_DSA_KEY *key)
403 {
404 return 2 * key->params->n;
405 }
406
407 /* Returns the private key data or NULL if there is no private key */
ossl_slh_dsa_key_get_priv(const SLH_DSA_KEY * key)408 const uint8_t *ossl_slh_dsa_key_get_priv(const SLH_DSA_KEY *key)
409 {
410 return key->has_priv ? key->priv : NULL;
411 }
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 */
ossl_slh_dsa_key_get_priv_len(const SLH_DSA_KEY * key)418 size_t ossl_slh_dsa_key_get_priv_len(const SLH_DSA_KEY *key)
419 {
420 return 4 * key->params->n;
421 }
422
ossl_slh_dsa_key_get_n(const SLH_DSA_KEY * key)423 size_t ossl_slh_dsa_key_get_n(const SLH_DSA_KEY *key)
424 {
425 return key->params->n;
426 }
427
ossl_slh_dsa_key_get_sig_len(const SLH_DSA_KEY * key)428 size_t ossl_slh_dsa_key_get_sig_len(const SLH_DSA_KEY *key)
429 {
430 return key->params->sig_len;
431 }
ossl_slh_dsa_key_get_name(const SLH_DSA_KEY * key)432 const char *ossl_slh_dsa_key_get_name(const SLH_DSA_KEY *key)
433 {
434 return key->params->alg;
435 }
ossl_slh_dsa_key_get_type(const SLH_DSA_KEY * key)436 int ossl_slh_dsa_key_get_type(const SLH_DSA_KEY *key)
437 {
438 return key->params->type;
439 }
440
ossl_slh_dsa_set_priv(SLH_DSA_KEY * key,const uint8_t * priv,size_t priv_len)441 int ossl_slh_dsa_set_priv(SLH_DSA_KEY *key, const uint8_t *priv, size_t priv_len)
442 {
443 if (ossl_slh_dsa_key_get_priv_len(key) != priv_len)
444 return 0;
445 memcpy(key->priv, priv, priv_len);
446 key->has_priv = 1;
447 key->pub = SLH_DSA_PUB(key);
448 return 1;
449 }
450
ossl_slh_dsa_set_pub(SLH_DSA_KEY * key,const uint8_t * pub,size_t pub_len)451 int ossl_slh_dsa_set_pub(SLH_DSA_KEY *key, const uint8_t *pub, size_t pub_len)
452 {
453 if (ossl_slh_dsa_key_get_pub_len(key) != pub_len)
454 return 0;
455 key->pub = SLH_DSA_PUB(key);
456 memcpy(key->pub, pub, pub_len);
457 key->has_priv = 0;
458 return 1;
459 }
460
461 #ifndef FIPS_MODULE
ossl_slh_dsa_key_to_text(BIO * out,const SLH_DSA_KEY * key,int selection)462 int ossl_slh_dsa_key_to_text(BIO *out, const SLH_DSA_KEY *key, int selection)
463 {
464 const char *name;
465
466 if (out == NULL || key == NULL) {
467 ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
468 return 0;
469 }
470 name = ossl_slh_dsa_key_get_name(key);
471 if (ossl_slh_dsa_key_get_pub(key) == NULL) {
472 /* Regardless of the |selection|, there must be a public key */
473 ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
474 "no %s key material available", name);
475 return 0;
476 }
477
478 if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
479 if (ossl_slh_dsa_key_get_priv(key) == NULL) {
480 ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
481 "no %s key material available", name);
482 return 0;
483 }
484 if (BIO_printf(out, "%s Private-Key:\n", name) <= 0)
485 return 0;
486 if (!ossl_bio_print_labeled_buf(out, "priv:", ossl_slh_dsa_key_get_priv(key),
487 ossl_slh_dsa_key_get_priv_len(key)))
488 return 0;
489 } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
490 if (BIO_printf(out, "%s Public-Key:\n", name) <= 0)
491 return 0;
492 }
493
494 if (!ossl_bio_print_labeled_buf(out, "pub:", ossl_slh_dsa_key_get_pub(key),
495 ossl_slh_dsa_key_get_pub_len(key)))
496 return 0;
497
498 return 1;
499 }
500 #endif /* FIPS_MODULE */
501