xref: /freebsd/crypto/openssl/crypto/slh_dsa/slh_dsa_key.c (revision 88b8b7f0c4e9948667a2279e78e975a784049cba)
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 Return the libctx associated with a SLH_DSA_KEY object
81  *
82  * @param key A SLH_DSA_KEY to extract the libctx from.
83  * @returns The new OSSL_LIB_CTX object on success, or NULL failure
84  */
ossl_slh_dsa_key_get0_libctx(const SLH_DSA_KEY * key)85 OSSL_LIB_CTX *ossl_slh_dsa_key_get0_libctx(const SLH_DSA_KEY *key)
86 {
87     return key != NULL ? key->libctx : NULL;
88 }
89 
90 /**
91  * @brief Create a new SLH_DSA_KEY object
92  *
93  * @param libctx A OSSL_LIB_CTX object used for fetching algorithms.
94  * @param propq The property query used for fetching algorithms
95  * @param alg The algorithm name associated with the key type
96  * @returns The new SLH_DSA_KEY object on success, or NULL on malloc failure
97  */
ossl_slh_dsa_key_new(OSSL_LIB_CTX * libctx,const char * propq,const char * alg)98 SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *propq,
99                                   const char *alg)
100 {
101     SLH_DSA_KEY *ret;
102     const SLH_DSA_PARAMS *params = ossl_slh_dsa_params_get(alg);
103 
104     if (params == NULL)
105         return NULL;
106 
107     ret = OPENSSL_zalloc(sizeof(*ret));
108     if (ret != NULL) {
109         ret->libctx = libctx;
110         ret->params = params;
111         if (propq != NULL) {
112             ret->propq = OPENSSL_strdup(propq);
113             if (ret->propq == NULL)
114                 goto err;
115         }
116         if (!slh_dsa_key_hash_init(ret))
117             goto err;
118     }
119     return ret;
120  err:
121     ossl_slh_dsa_key_free(ret);
122     return NULL;
123 }
124 
125 /**
126  * @brief Destroy a SLH_DSA_KEY object
127  */
ossl_slh_dsa_key_free(SLH_DSA_KEY * key)128 void ossl_slh_dsa_key_free(SLH_DSA_KEY *key)
129 {
130     if (key == NULL)
131         return;
132 
133     slh_dsa_key_hash_cleanup(key);
134     OPENSSL_cleanse(&key->priv, sizeof(key->priv) >> 1);
135     OPENSSL_free(key);
136 }
137 
138 /**
139  * @brief Duplicate a key
140  *
141  * @param src A SLH_DSA_KEY object to copy
142  * @param selection to select public and/or private components. Selecting the
143  *                  private key will also select the public key
144  * @returns The duplicated key, or NULL on failure.
145  */
ossl_slh_dsa_key_dup(const SLH_DSA_KEY * src,int selection)146 SLH_DSA_KEY *ossl_slh_dsa_key_dup(const SLH_DSA_KEY *src, int selection)
147 {
148     SLH_DSA_KEY *ret = NULL;
149 
150     if (src == NULL)
151         return NULL;
152 
153     ret = OPENSSL_zalloc(sizeof(*ret));
154     if (ret != NULL) {
155         *ret = *src; /* this copies everything including the keydata in priv[] */
156         ret->propq = NULL;
157         ret->pub = NULL;
158         ret->has_priv = 0;
159         slh_dsa_key_hash_dup(ret, src);
160         if (src->propq != NULL) {
161             ret->propq = OPENSSL_strdup(src->propq);
162             if (ret->propq == NULL)
163                 goto err;
164         }
165         if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
166             /* The public components are present if the private key is present */
167             if (src->pub != NULL)
168                 ret->pub = SLH_DSA_PUB(ret);
169             if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
170                 ret->has_priv = src->has_priv;
171         }
172     }
173     return ret;
174  err:
175     ossl_slh_dsa_key_free(ret);
176     return NULL;
177 }
178 
179 /**
180  * @brief Are 2 keys equal?
181  *
182  * To be equal the keys must have the same key data and algorithm name.
183  *
184  * @param key1 A SLH_DSA_KEY object
185  * @param key2 A SLH_DSA_KEY object
186  * @param selection to select public and/or private component comparison.
187  * @returns 1 if the keys are equal otherwise it returns 0.
188  */
ossl_slh_dsa_key_equal(const SLH_DSA_KEY * key1,const SLH_DSA_KEY * key2,int selection)189 int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2,
190                            int selection)
191 {
192     int key_checked = 0;
193 
194     /* The parameter sets must match - i.e. The same algorithm name */
195     if (key1->params != key2->params)
196         return 0;
197 
198     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
199         if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
200             if (key1->pub != NULL && key2->pub != NULL) {
201                 if (memcmp(key1->pub, key2->pub, key1->params->pk_len) != 0)
202                     return 0;
203                 key_checked = 1;
204             }
205         }
206         if (!key_checked
207                 && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
208             if (key1->has_priv && key2->has_priv) {
209                 if (memcmp(key1->priv, key2->priv,
210                            key1->params->pk_len) != 0)
211                     return 0;
212                 key_checked = 1;
213             }
214         }
215         return key_checked;
216     }
217     return 1;
218 }
219 
ossl_slh_dsa_key_has(const SLH_DSA_KEY * key,int selection)220 int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection)
221 {
222     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
223         if (key->pub == NULL)
224             return 0; /* No public key */
225         if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
226                 && key->has_priv == 0)
227             return 0; /* No private key */
228         return 1;
229     }
230     return 0;
231 }
232 
ossl_slh_dsa_key_pairwise_check(const SLH_DSA_KEY * key)233 int ossl_slh_dsa_key_pairwise_check(const SLH_DSA_KEY *key)
234 {
235     int ret;
236     SLH_DSA_HASH_CTX *ctx = NULL;
237 
238     if (key->pub == NULL || key->has_priv == 0)
239         return 0;
240 
241     ctx = ossl_slh_dsa_hash_ctx_new(key);
242     if (ctx == NULL)
243         return 0;
244     ret = slh_dsa_compute_pk_root(ctx, (SLH_DSA_KEY *)key, 1);
245     ossl_slh_dsa_hash_ctx_free(ctx);
246     return ret;
247 }
248 
ossl_slh_dsa_key_reset(SLH_DSA_KEY * key)249 void ossl_slh_dsa_key_reset(SLH_DSA_KEY *key)
250 {
251     key->pub = NULL;
252     if (key->has_priv) {
253         key->has_priv = 0;
254         OPENSSL_cleanse(key->priv, sizeof(key->priv));
255     }
256 }
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  */
ossl_slh_dsa_key_fromdata(SLH_DSA_KEY * key,const OSSL_PARAM params[],int include_private)267 int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[],
268                               int include_private)
269 {
270     size_t priv_len, key_len, data_len = 0;
271     const OSSL_PARAM *param_priv = NULL, *param_pub = NULL;
272     void *p;
273 
274     if (key == NULL)
275         return 0;
276 
277     /* The private key consists of 4 elements SK_SEED, SK_PRF, PK_SEED and PK_ROOT */
278     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     key_len = priv_len >> 1;
281 
282     /* Private key is optional */
283     if (include_private) {
284         param_priv = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
285         if (param_priv != NULL) {
286             p = key->priv;
287             if (!OSSL_PARAM_get_octet_string(param_priv, &p, priv_len, &data_len))
288                 return 0;
289             /* If the data read includes all 4 elements then we are finished */
290             if (data_len == priv_len) {
291                 key->has_priv = 1;
292                 key->pub = SLH_DSA_PUB(key);
293                 return 1;
294             }
295             /* Otherwise it must be just SK_SEED + SK_PRF */
296             if (data_len != key_len)
297                 goto err;
298             key->has_priv = 1;
299         }
300     }
301     /*
302      * In the case where the passed in private key does not contain the public key
303      * there MUST be a separate public key, since the private key cannot exist
304      * without the public key elements. NOTE that this does not accept half of
305      * the public key, (Keygen must be used for this case currently).
306      */
307     p = SLH_DSA_PUB(key);
308     param_pub = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
309     if (param_pub == NULL
310             || !OSSL_PARAM_get_octet_string(param_pub, &p, key_len, &data_len)
311             || data_len != key_len)
312         goto err;
313     key->pub = p;
314     return 1;
315  err:
316     ossl_slh_dsa_key_reset(key);
317     return 0;
318 }
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  */
slh_dsa_compute_pk_root(SLH_DSA_HASH_CTX * ctx,SLH_DSA_KEY * out,int validate)331 static int slh_dsa_compute_pk_root(SLH_DSA_HASH_CTX *ctx, SLH_DSA_KEY *out,
332                                    int validate)
333 {
334     const SLH_DSA_KEY *key = ctx->key;
335     SLH_ADRS_FUNC_DECLARE(key, adrsf);
336     SLH_ADRS_DECLARE(adrs);
337     const SLH_DSA_PARAMS *params = key->params;
338     size_t n = params->n;
339     uint8_t pk_root[SLH_DSA_MAX_N], *dst;
340 
341     adrsf->zero(adrs);
342     adrsf->set_layer_address(adrs, params->d - 1);
343 
344     dst = validate ? pk_root : SLH_DSA_PK_ROOT(out);
345 
346     /* Generate the ROOT public key */
347     return ossl_slh_xmss_node(ctx, SLH_DSA_SK_SEED(key), 0, params->hm,
348                               SLH_DSA_PK_SEED(key), adrs, dst, n)
349         && (validate == 0 || memcmp(dst, SLH_DSA_PK_ROOT(out), n) == 0);
350 }
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  */
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)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 {
370     size_t n = out->params->n;
371     size_t secret_key_len = 2 * n; /* The length of SK_SEED + SK_PRF */
372     size_t pk_seed_len = n;        /* The length of PK_SEED */
373     size_t entropy_len_expected = secret_key_len + pk_seed_len;
374     uint8_t *priv = SLH_DSA_PRIV(out);
375     uint8_t *pub = SLH_DSA_PUB(out);
376 
377     if (entropy != NULL && entropy_len != 0) {
378         if (entropy_len != entropy_len_expected)
379             goto err;
380         memcpy(priv, entropy, entropy_len_expected);
381     } else {
382         if (RAND_priv_bytes_ex(lib_ctx, priv, secret_key_len, 0) <= 0
383                 || RAND_bytes_ex(lib_ctx, pub, pk_seed_len, 0) <= 0)
384             goto err;
385     }
386     if (!slh_dsa_compute_pk_root(ctx, out, 0))
387         goto err;
388     out->pub = pub;
389     out->has_priv = 1;
390     return 1;
391 err:
392     out->pub = NULL;
393     out->has_priv = 0;
394     OPENSSL_cleanse(priv, secret_key_len);
395     return 0;
396 }
397 
398 /**
399  * @brief This is used when a SLH key is used for an operation.
400  * This checks that the algorithm is the same (i.e. uses the same parameters)
401  *
402  * @param ctx Contains SLH_DSA algorithm functions and constants to be used for
403  *            an operation.
404  * @param key A SLH_DSA key to use for an operation.
405  *
406  * @returns 1 if the algorithm matches, or 0 otherwise.
407  */
ossl_slh_dsa_key_type_matches(const SLH_DSA_KEY * key,const char * alg)408 int ossl_slh_dsa_key_type_matches(const SLH_DSA_KEY *key, const char *alg)
409 {
410     return (OPENSSL_strcasecmp(key->params->alg, alg) == 0);
411 }
412 
413 /* Returns the public key data or NULL if there is no public key */
ossl_slh_dsa_key_get_pub(const SLH_DSA_KEY * key)414 const uint8_t *ossl_slh_dsa_key_get_pub(const SLH_DSA_KEY *key)
415 {
416     return key->pub;
417 }
418 
419 /* 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)420 size_t ossl_slh_dsa_key_get_pub_len(const SLH_DSA_KEY *key)
421 {
422     return 2 * key->params->n;
423 }
424 
425 /* Returns the private key data or NULL if there is no private key */
ossl_slh_dsa_key_get_priv(const SLH_DSA_KEY * key)426 const uint8_t *ossl_slh_dsa_key_get_priv(const SLH_DSA_KEY *key)
427 {
428     return key->has_priv ? key->priv : NULL;
429 }
430 
431 /*
432  * Returns the constant 4 * |n| which is the size of both
433  * the private and public key components.
434  * SK_SEED + SK_ROOT + PK_SEED + PK_ROOT
435  */
ossl_slh_dsa_key_get_priv_len(const SLH_DSA_KEY * key)436 size_t ossl_slh_dsa_key_get_priv_len(const SLH_DSA_KEY *key)
437 {
438     return 4 * key->params->n;
439 }
440 
ossl_slh_dsa_key_get_n(const SLH_DSA_KEY * key)441 size_t ossl_slh_dsa_key_get_n(const SLH_DSA_KEY *key)
442 {
443     return key->params->n;
444 }
445 
ossl_slh_dsa_key_get_sig_len(const SLH_DSA_KEY * key)446 size_t ossl_slh_dsa_key_get_sig_len(const SLH_DSA_KEY *key)
447 {
448     return key->params->sig_len;
449 }
ossl_slh_dsa_key_get_name(const SLH_DSA_KEY * key)450 const char *ossl_slh_dsa_key_get_name(const SLH_DSA_KEY *key)
451 {
452     return key->params->alg;
453 }
ossl_slh_dsa_key_get_type(const SLH_DSA_KEY * key)454 int ossl_slh_dsa_key_get_type(const SLH_DSA_KEY *key)
455 {
456     return key->params->type;
457 }
458 
ossl_slh_dsa_set_priv(SLH_DSA_KEY * key,const uint8_t * priv,size_t priv_len)459 int ossl_slh_dsa_set_priv(SLH_DSA_KEY *key, const uint8_t *priv, size_t priv_len)
460 {
461     if (ossl_slh_dsa_key_get_priv_len(key) != priv_len)
462         return 0;
463     memcpy(key->priv, priv, priv_len);
464     key->has_priv = 1;
465     key->pub = SLH_DSA_PUB(key);
466     return 1;
467 }
468 
ossl_slh_dsa_set_pub(SLH_DSA_KEY * key,const uint8_t * pub,size_t pub_len)469 int ossl_slh_dsa_set_pub(SLH_DSA_KEY *key, const uint8_t *pub, size_t pub_len)
470 {
471     if (ossl_slh_dsa_key_get_pub_len(key) != pub_len)
472         return 0;
473     key->pub = SLH_DSA_PUB(key);
474     memcpy(key->pub, pub, pub_len);
475     key->has_priv = 0;
476     return 1;
477 }
478 
479 #ifndef FIPS_MODULE
ossl_slh_dsa_key_to_text(BIO * out,const SLH_DSA_KEY * key,int selection)480 int ossl_slh_dsa_key_to_text(BIO *out, const SLH_DSA_KEY *key, int selection)
481 {
482     const char *name;
483 
484     if (out == NULL || key == NULL) {
485         ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
486         return 0;
487     }
488     name = ossl_slh_dsa_key_get_name(key);
489     if (ossl_slh_dsa_key_get_pub(key) == NULL) {
490         /* Regardless of the |selection|, there must be a public key */
491         ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
492                        "no %s key material available", name);
493         return 0;
494     }
495 
496     if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
497         if (ossl_slh_dsa_key_get_priv(key) == NULL) {
498             ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
499                            "no %s key material available", name);
500             return 0;
501         }
502         if (BIO_printf(out, "%s Private-Key:\n", name) <= 0)
503             return 0;
504         if (!ossl_bio_print_labeled_buf(out, "priv:", ossl_slh_dsa_key_get_priv(key),
505                                         ossl_slh_dsa_key_get_priv_len(key)))
506             return 0;
507     } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
508         if (BIO_printf(out, "%s Public-Key:\n", name) <= 0)
509             return 0;
510     }
511 
512     if (!ossl_bio_print_labeled_buf(out, "pub:", ossl_slh_dsa_key_get_pub(key),
513                                     ossl_slh_dsa_key_get_pub_len(key)))
514         return 0;
515 
516     return 1;
517 }
518 #endif /* FIPS_MODULE */
519