Configurable import-time PCT for ML-KEM
And related cleanup. Reviewed-by: Shane Lontis <shane.lontis@oracle.com> Reviewed-by: Tim Hudson <tjh@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26789)
This commit is contained in:
parent
f31b98fefe
commit
cab4e7cbd1
14 changed files with 207 additions and 118 deletions
|
@ -28,17 +28,12 @@ const uint8_t *ossl_ml_dsa_key_get_seed(const ML_DSA_KEY *key)
|
|||
return key->seed;
|
||||
}
|
||||
|
||||
int ossl_ml_dsa_key_prefer_seed(const ML_DSA_KEY *key)
|
||||
int ossl_ml_dsa_key_get_prov_flags(const ML_DSA_KEY *key)
|
||||
{
|
||||
return key->prefer_seed;
|
||||
return key->prov_flags;
|
||||
}
|
||||
|
||||
int ossl_ml_dsa_key_retain_seed(const ML_DSA_KEY *key)
|
||||
{
|
||||
return key->retain_seed;
|
||||
}
|
||||
|
||||
int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int prefer_seed, int retain_seed,
|
||||
int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int flags_set, int flags_clr,
|
||||
const uint8_t *seed, size_t seed_len,
|
||||
const uint8_t *sk, size_t sk_len)
|
||||
{
|
||||
|
@ -58,10 +53,8 @@ int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int prefer_seed, int retain_seed,
|
|||
if (seed != NULL
|
||||
&& (key->seed = OPENSSL_memdup(seed, seed_len)) == NULL)
|
||||
goto end;
|
||||
if (prefer_seed >= 0)
|
||||
key->prefer_seed = prefer_seed;
|
||||
if (retain_seed >= 0)
|
||||
key->retain_seed = retain_seed;
|
||||
key->prov_flags |= flags_set;
|
||||
key->prov_flags &= ~flags_clr;
|
||||
ret = 1;
|
||||
|
||||
end:
|
||||
|
@ -94,8 +87,7 @@ ML_DSA_KEY *ossl_ml_dsa_key_new(OSSL_LIB_CTX *libctx, const char *propq,
|
|||
if (ret != NULL) {
|
||||
ret->libctx = libctx;
|
||||
ret->params = params;
|
||||
ret->prefer_seed = 1;
|
||||
ret->retain_seed = 1;
|
||||
ret->prov_flags = ML_DSA_KEY_PROV_FLAGS_DEFAULT;
|
||||
ret->shake128_md = EVP_MD_fetch(libctx, "SHAKE-128", propq);
|
||||
ret->shake256_md = EVP_MD_fetch(libctx, "SHAKE-256", propq);
|
||||
if (ret->shake128_md == NULL || ret->shake256_md == NULL)
|
||||
|
@ -190,8 +182,7 @@ ML_DSA_KEY *ossl_ml_dsa_key_dup(const ML_DSA_KEY *src, int selection)
|
|||
if (ret != NULL) {
|
||||
ret->libctx = src->libctx;
|
||||
ret->params = src->params;
|
||||
ret->retain_seed = src->retain_seed;
|
||||
ret->prefer_seed = src->prefer_seed;
|
||||
ret->prov_flags = src->prov_flags;
|
||||
if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
|
||||
if (src->pub_encoding != NULL) {
|
||||
/* The public components are present if the private key is present */
|
||||
|
@ -443,7 +434,7 @@ static int keygen_internal(ML_DSA_KEY *out)
|
|||
&& ossl_ml_dsa_sk_encode(out);
|
||||
|
||||
err:
|
||||
if (out->seed != NULL && !out->retain_seed) {
|
||||
if (out->seed != NULL && (out->prov_flags & ML_DSA_KEY_RETAIN_SEED) == 0) {
|
||||
OPENSSL_clear_free(out->seed, ML_DSA_SEED_BYTES);
|
||||
out->seed = NULL;
|
||||
}
|
||||
|
|
|
@ -34,8 +34,7 @@ struct ml_dsa_key_st {
|
|||
uint8_t *pub_encoding;
|
||||
uint8_t *priv_encoding;
|
||||
uint8_t *seed;
|
||||
int retain_seed;
|
||||
int prefer_seed;
|
||||
int prov_flags;
|
||||
|
||||
/*
|
||||
* t1 is the Polynomial encoding of the 10 MSB of each coefficient of the
|
||||
|
|
|
@ -1394,7 +1394,7 @@ int genkey(const uint8_t seed[ML_KEM_SEED_BYTES],
|
|||
|
||||
/* Optionally save the |d| portion of the seed */
|
||||
key->d = key->z + ML_KEM_RANDOM_BYTES;
|
||||
if (key->retain_seed) {
|
||||
if (key->prov_flags & ML_KEM_KEY_RETAIN_SEED) {
|
||||
memcpy(key->d, seed, ML_KEM_RANDOM_BYTES);
|
||||
} else {
|
||||
OPENSSL_cleanse(key->d, ML_KEM_RANDOM_BYTES);
|
||||
|
@ -1576,10 +1576,6 @@ const ML_KEM_VINFO *ossl_ml_kem_get_vinfo(int evp_type)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The |retain_seed| parameter indicates whether the seed should be retained
|
||||
* once the key is generated.
|
||||
*/
|
||||
ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
|
||||
int evp_type)
|
||||
{
|
||||
|
@ -1594,8 +1590,7 @@ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
|
|||
|
||||
key->vinfo = vinfo;
|
||||
key->libctx = libctx;
|
||||
key->prefer_seed = 1;
|
||||
key->retain_seed = 1;
|
||||
key->prov_flags = ML_KEM_KEY_PROV_FLAGS_DEFAULT;
|
||||
key->shake128_md = EVP_MD_fetch(libctx, "SHAKE128", properties);
|
||||
key->shake256_md = EVP_MD_fetch(libctx, "SHAKE256", properties);
|
||||
key->sha3_256_md = EVP_MD_fetch(libctx, "SHA3-256", properties);
|
||||
|
|
|
@ -108,6 +108,17 @@ configuration options programmatically.
|
|||
|
||||
=over 4
|
||||
|
||||
=item C<ml-kem.import_pct_type> (B<OSSL_PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE>) <UTF8 string>
|
||||
|
||||
When an B<ML-KEM> key is imported as an explict FIPS 203 B<dk> decapsulation
|
||||
key, rather than a seed, a pairwise consistency test (PCT) is optionally
|
||||
performed.
|
||||
By default, or when this parameter is set explicitly to C<random>, the PCT
|
||||
is performed with a random entropy value for the encapsulation step.
|
||||
Setting the parameter to C<fixed>, still runs the test, but the encapsulation
|
||||
entropy is a fixed 32 byte value.
|
||||
Specifying any other value of the parameter, e.g. C<none>, skips the test.
|
||||
|
||||
=item C<ml-kem.retain_seed> (B<OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED>) <UTF8 string>
|
||||
|
||||
When set to a string representing a false boolean value (see
|
||||
|
|
|
@ -42,6 +42,12 @@
|
|||
# define MAX_ML_DSA_PUB_LEN ML_DSA_87_PUB_LEN
|
||||
# define MAX_ML_DSA_SIG_LEN ML_DSA_87_SIG_LEN
|
||||
|
||||
# define ML_DSA_KEY_PREFER_SEED (1 << 0)
|
||||
# define ML_DSA_KEY_RETAIN_SEED (1 << 1)
|
||||
/* Default provider flags */
|
||||
# define ML_DSA_KEY_PROV_FLAGS_DEFAULT \
|
||||
(ML_DSA_KEY_PREFER_SEED | ML_DSA_KEY_RETAIN_SEED)
|
||||
|
||||
/*
|
||||
* Refer to FIPS 204 Section 4 Parameter sets.
|
||||
* Fields that are shared between all algorithms (such as q & d) have been omitted.
|
||||
|
@ -86,9 +92,8 @@ __owur size_t ossl_ml_dsa_key_get_pub_len(const ML_DSA_KEY *key);
|
|||
__owur const uint8_t *ossl_ml_dsa_key_get_priv(const ML_DSA_KEY *key);
|
||||
__owur size_t ossl_ml_dsa_key_get_priv_len(const ML_DSA_KEY *key);
|
||||
__owur const uint8_t *ossl_ml_dsa_key_get_seed(const ML_DSA_KEY *key);
|
||||
__owur int ossl_ml_dsa_key_prefer_seed(const ML_DSA_KEY *key);
|
||||
__owur int ossl_ml_dsa_key_retain_seed(const ML_DSA_KEY *key);
|
||||
int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int prefer_seed, int retain_seed,
|
||||
__owur int ossl_ml_dsa_key_get_prov_flags(const ML_DSA_KEY *key);
|
||||
int ossl_ml_dsa_set_prekey(ML_DSA_KEY *key, int flags_set, int flags_clr,
|
||||
const uint8_t *seed, size_t seed_len,
|
||||
const uint8_t *sk, size_t sk_len);
|
||||
__owur size_t ossl_ml_dsa_key_get_collision_strength_bits(const ML_DSA_KEY *key);
|
||||
|
|
|
@ -117,6 +117,17 @@
|
|||
# define ML_KEM_1024_DV 5
|
||||
# define ML_KEM_1024_SECBITS 256
|
||||
|
||||
# define ML_KEM_KEY_RANDOM_PCT (1 << 0)
|
||||
# define ML_KEM_KEY_FIXED_PCT (1 << 1)
|
||||
# define ML_KEM_KEY_PREFER_SEED (1 << 2)
|
||||
# define ML_KEM_KEY_RETAIN_SEED (1 << 3)
|
||||
/* Mask to check whether PCT on import is enabled */
|
||||
# define ML_KEM_KEY_PCT_TYPE \
|
||||
(ML_KEM_KEY_RANDOM_PCT | ML_KEM_KEY_FIXED_PCT)
|
||||
/* Default provider flags */
|
||||
# define ML_KEM_KEY_PROV_FLAGS_DEFAULT \
|
||||
(ML_KEM_KEY_RANDOM_PCT | ML_KEM_KEY_PREFER_SEED | ML_KEM_KEY_RETAIN_SEED)
|
||||
|
||||
/*
|
||||
* External variant-specific API
|
||||
* -----------------------------
|
||||
|
@ -171,8 +182,7 @@ typedef struct ossl_ml_kem_key_st {
|
|||
struct ossl_ml_kem_scalar_st *s; /* Private key secret vector */
|
||||
uint8_t *z; /* Private key FO failure secret */
|
||||
uint8_t *d; /* Private key seed */
|
||||
int prefer_seed; /* Given seed and key use seed? */
|
||||
int retain_seed; /* Retain the seed after keygen? */
|
||||
int prov_flags; /* prefer/retain seed and PCT flags */
|
||||
|
||||
/*
|
||||
* Fixed-size built-in buffer, which holds the |rho| and the public key
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <openssl/x509.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include "internal/encoder.h"
|
||||
#include "prov/ml_dsa.h"
|
||||
#include "ml_dsa_codecs.h"
|
||||
|
||||
/*-
|
||||
|
@ -139,7 +140,6 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|||
int evp_type, PROV_CTX *provctx,
|
||||
const char *propq)
|
||||
{
|
||||
OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
|
||||
const ML_DSA_PARAMS *v;
|
||||
const ML_COMMON_CODEC *codec;
|
||||
ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot;
|
||||
|
@ -149,7 +149,7 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|||
const uint8_t *buf, *pos;
|
||||
const X509_ALGOR *alg = NULL;
|
||||
const char *formats;
|
||||
int len, ptype, retain, prefer;
|
||||
int len, ptype;
|
||||
uint32_t magic;
|
||||
uint16_t seed_magic;
|
||||
const uint8_t *seed = NULL;
|
||||
|
@ -244,7 +244,7 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|||
* Collect the seed and/or key into a "decoded" private key object,
|
||||
* to be turned into a real key on provider "load" or "import".
|
||||
*/
|
||||
if ((key = ossl_ml_dsa_key_new(libctx, propq, evp_type)) == NULL)
|
||||
if ((key = ossl_prov_ml_dsa_new(provctx, propq, evp_type)) == NULL)
|
||||
goto end;
|
||||
if (p8fmt->seed_length > 0)
|
||||
seed = buf + p8fmt->seed_offset;
|
||||
|
@ -252,19 +252,7 @@ ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|||
priv = buf + p8fmt->priv_offset;
|
||||
/* Any OQS public key content is ignored */
|
||||
|
||||
/*
|
||||
* If the key ends up "loaded" into the same provider, these are the
|
||||
* correct config settings, otherwise, new values will be assigned on
|
||||
* import into a different provider. The "load" API does not pass along
|
||||
* the provider context.
|
||||
*/
|
||||
retain =
|
||||
ossl_prov_ctx_get_bool_param(
|
||||
provctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1);
|
||||
prefer =
|
||||
ossl_prov_ctx_get_bool_param(
|
||||
provctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1);
|
||||
if (ossl_ml_dsa_set_prekey(key, prefer, retain,
|
||||
if (ossl_ml_dsa_set_prekey(key, 0, 0,
|
||||
seed, ML_DSA_SEED_BYTES, priv, v->sk_len))
|
||||
ret = key;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <openssl/x509.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include "internal/encoder.h"
|
||||
#include "prov/ml_kem.h"
|
||||
#include "ml_kem_codecs.h"
|
||||
|
||||
/* Tables describing supported ASN.1 input/output formats. */
|
||||
|
@ -138,7 +139,6 @@ ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|||
int evp_type, PROV_CTX *provctx,
|
||||
const char *propq)
|
||||
{
|
||||
OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
|
||||
const ML_KEM_VINFO *v;
|
||||
const ML_COMMON_CODEC *codec;
|
||||
ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot;
|
||||
|
@ -241,12 +241,9 @@ ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
|
|||
* Collect the seed and/or key into a "decoded" private key object,
|
||||
* to be turned into a real key on provider "load" or "import".
|
||||
*/
|
||||
if ((key = ossl_ml_kem_key_new(libctx, propq, evp_type)) == NULL)
|
||||
if ((key = ossl_prov_ml_kem_new(provctx, propq, evp_type)) == NULL)
|
||||
goto end;
|
||||
key->retain_seed = ossl_prov_ctx_get_bool_param(
|
||||
provctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
|
||||
key->prefer_seed = ossl_prov_ctx_get_bool_param(
|
||||
provctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1);
|
||||
|
||||
if (p8fmt->seed_length > 0) {
|
||||
if (!ossl_ml_kem_set_seed(buf + p8fmt->seed_offset,
|
||||
ML_KEM_SEED_BYTES, key)) {
|
||||
|
|
14
providers/implementations/include/prov/ml_dsa.h
Normal file
14
providers/implementations/include/prov/ml_dsa.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include "crypto/ml_dsa.h"
|
||||
#include "prov/provider_ctx.h"
|
||||
|
||||
ML_DSA_KEY *
|
||||
ossl_prov_ml_dsa_new(PROV_CTX *provctx, const char *propq, int evp_type);
|
14
providers/implementations/include/prov/ml_kem.h
Normal file
14
providers/implementations/include/prov/ml_kem.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include "crypto/ml_kem.h"
|
||||
#include "prov/provider_ctx.h"
|
||||
|
||||
ML_KEM_KEY *
|
||||
ossl_prov_ml_kem_new(PROV_CTX *provctx, const char *propq, int evp_type);
|
|
@ -18,6 +18,7 @@
|
|||
#include "prov/implementations.h"
|
||||
#include "prov/providercommon.h"
|
||||
#include "prov/provider_ctx.h"
|
||||
#include "prov/ml_dsa.h"
|
||||
|
||||
static OSSL_FUNC_keymgmt_free_fn ml_dsa_free_key;
|
||||
static OSSL_FUNC_keymgmt_has_fn ml_dsa_has;
|
||||
|
@ -93,22 +94,36 @@ static int ml_dsa_pairwise_test(const ML_DSA_KEY *key)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void *ml_dsa_new_key(void *provctx, const char *propq,
|
||||
int evp_type)
|
||||
ML_DSA_KEY *ossl_prov_ml_dsa_new(PROV_CTX *ctx, const char *propq, int evp_type)
|
||||
{
|
||||
ML_DSA_KEY *key;
|
||||
int prefer, retain;
|
||||
|
||||
if (!ossl_prov_is_running())
|
||||
return 0;
|
||||
|
||||
key = ossl_ml_dsa_key_new(PROV_LIBCTX_OF(provctx), propq, evp_type);
|
||||
key = ossl_ml_dsa_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type);
|
||||
/*
|
||||
* When decoding, if the key ends up "loaded" into the same provider, these
|
||||
* are the correct config settings, otherwise, new values will be assigned
|
||||
* on import into a different provider. The "load" API does not pass along
|
||||
* the provider context.
|
||||
*/
|
||||
if (key != NULL) {
|
||||
prefer = ossl_prov_ctx_get_bool_param(
|
||||
provctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1);
|
||||
retain = ossl_prov_ctx_get_bool_param(
|
||||
provctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1);
|
||||
ossl_ml_dsa_set_prekey(key, prefer, retain, NULL, 0, NULL, 0);
|
||||
int flags_set = 0, flags_clr = 0;
|
||||
|
||||
if (ossl_prov_ctx_get_bool_param(
|
||||
ctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1))
|
||||
flags_set |= ML_DSA_KEY_RETAIN_SEED;
|
||||
else
|
||||
flags_clr = ML_DSA_KEY_RETAIN_SEED;
|
||||
|
||||
if (ossl_prov_ctx_get_bool_param(
|
||||
ctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1))
|
||||
flags_set |= ML_DSA_KEY_PREFER_SEED;
|
||||
else
|
||||
flags_clr |= ML_DSA_KEY_PREFER_SEED;
|
||||
|
||||
ossl_ml_dsa_set_prekey(key, flags_set, flags_clr, NULL, 0, NULL, 0);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
@ -217,8 +232,9 @@ static int ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[],
|
|||
}
|
||||
|
||||
if (seed_len != 0
|
||||
&& (sk_len == 0 || ossl_ml_dsa_key_prefer_seed(key))) {
|
||||
if (!ossl_ml_dsa_set_prekey(key, -1, -1, seed, seed_len, NULL, 0))
|
||||
&& (sk_len == 0
|
||||
|| (ossl_ml_dsa_key_get_prov_flags(key) & ML_DSA_KEY_PREFER_SEED))) {
|
||||
if (!ossl_ml_dsa_set_prekey(key, 0, 0, seed, seed_len, NULL, 0))
|
||||
return 0;
|
||||
if (!ossl_ml_dsa_generate_key(key)) {
|
||||
ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
|
||||
|
@ -373,7 +389,8 @@ static int ml_dsa_export(void *keydata, int selection,
|
|||
#ifndef FIPS_MODULE
|
||||
static void *ml_dsa_load(const void *reference, size_t reference_sz)
|
||||
{
|
||||
ML_DSA_KEY *ret = NULL, *key = NULL;
|
||||
ML_DSA_KEY *key = NULL;
|
||||
const ML_DSA_PARAMS *key_params;
|
||||
const uint8_t *sk, *seed;
|
||||
|
||||
if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
|
||||
|
@ -388,18 +405,25 @@ static void *ml_dsa_load(const void *reference, size_t reference_sz)
|
|||
sk = ossl_ml_dsa_key_get_priv(key);
|
||||
seed = ossl_ml_dsa_key_get_seed(key);
|
||||
if (seed != NULL
|
||||
&& (sk == NULL || ossl_ml_dsa_key_prefer_seed(key))) {
|
||||
&& (sk == NULL || (ossl_ml_dsa_key_get_prov_flags(key)
|
||||
& ML_DSA_KEY_PREFER_SEED))) {
|
||||
if (ossl_ml_dsa_generate_key(key))
|
||||
ret = key;
|
||||
return key;
|
||||
} else if (sk != NULL) {
|
||||
if (ossl_ml_dsa_sk_decode(key, sk,
|
||||
ossl_ml_dsa_key_get_priv_len(key)))
|
||||
ret = key;
|
||||
return key;
|
||||
key_params = ossl_ml_dsa_key_params(key);
|
||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
|
||||
"error parsing %s private key",
|
||||
key_params->alg);
|
||||
} else {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
if (ret == NULL)
|
||||
|
||||
ossl_ml_dsa_key_free(key);
|
||||
return ret;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -428,11 +452,11 @@ static void *ml_dsa_gen(void *genctx, int evp_type)
|
|||
|
||||
if (!ossl_prov_is_running())
|
||||
return NULL;
|
||||
key = ml_dsa_new_key(gctx->provctx, gctx->propq, evp_type);
|
||||
key = ossl_prov_ml_dsa_new(gctx->provctx, gctx->propq, evp_type);
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
if (gctx->entropy_len != 0
|
||||
&& !ossl_ml_dsa_set_prekey(key, -1, -1,
|
||||
&& !ossl_ml_dsa_set_prekey(key, 0, 0,
|
||||
gctx->entropy, gctx->entropy_len, NULL, 0))
|
||||
goto err;
|
||||
if (!ossl_ml_dsa_generate_key(key)) {
|
||||
|
@ -510,7 +534,7 @@ static void ml_dsa_gen_cleanup(void *genctx)
|
|||
static OSSL_FUNC_keymgmt_gen_fn ml_dsa_##alg##_gen; \
|
||||
static void *ml_dsa_##alg##_new_key(void *provctx) \
|
||||
{ \
|
||||
return ml_dsa_new_key(provctx, NULL, EVP_PKEY_ML_DSA_##alg); \
|
||||
return ossl_prov_ml_dsa_new(provctx, NULL, EVP_PKEY_ML_DSA_##alg); \
|
||||
} \
|
||||
static void *ml_dsa_##alg##_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)\
|
||||
{ \
|
||||
|
|
|
@ -17,11 +17,12 @@
|
|||
#include <openssl/self_test.h>
|
||||
#include <openssl/param_build.h>
|
||||
#include "crypto/ml_kem.h"
|
||||
#include "internal/param_build_set.h"
|
||||
#include "prov/implementations.h"
|
||||
#include "prov/providercommon.h"
|
||||
#include "prov/provider_ctx.h"
|
||||
#include "prov/securitycheck.h"
|
||||
#include "internal/param_build_set.h"
|
||||
#include "prov/ml_kem.h"
|
||||
|
||||
static OSSL_FUNC_keymgmt_new_fn ml_kem_512_new;
|
||||
static OSSL_FUNC_keymgmt_new_fn ml_kem_768_new;
|
||||
|
@ -61,14 +62,14 @@ typedef struct ml_kem_gen_ctx_st {
|
|||
uint8_t *seed;
|
||||
} PROV_ML_KEM_GEN_CTX;
|
||||
|
||||
static int ml_kem_pairwise_test(const ML_KEM_KEY *key)
|
||||
static int ml_kem_pairwise_test(const ML_KEM_KEY *key, int key_flags)
|
||||
{
|
||||
#ifdef FIPS_MODULE
|
||||
OSSL_SELF_TEST *st = NULL;
|
||||
OSSL_CALLBACK *cb = NULL;
|
||||
void *cbarg = NULL;
|
||||
unsigned char entropy[ML_KEM_RANDOM_BYTES];
|
||||
#endif
|
||||
unsigned char entropy[ML_KEM_RANDOM_BYTES];
|
||||
unsigned char secret[ML_KEM_SHARED_SECRET_BYTES];
|
||||
unsigned char out[ML_KEM_SHARED_SECRET_BYTES];
|
||||
unsigned char *ctext = NULL;
|
||||
|
@ -77,9 +78,10 @@ static int ml_kem_pairwise_test(const ML_KEM_KEY *key)
|
|||
int ret = 0;
|
||||
|
||||
/* Unless we have both a public and private key, we can't do the test */
|
||||
if (!ossl_ml_kem_have_prvkey(key) || !ossl_ml_kem_have_pubkey(key))
|
||||
if (!ossl_ml_kem_have_prvkey(key)
|
||||
|| !ossl_ml_kem_have_pubkey(key)
|
||||
|| (key_flags & ML_KEM_KEY_PCT_TYPE) == 0)
|
||||
return 1;
|
||||
|
||||
#ifdef FIPS_MODULE
|
||||
/*
|
||||
* The functions `OSSL_SELF_TEST_*` will return directly if parameter `st`
|
||||
|
@ -100,22 +102,21 @@ static int ml_kem_pairwise_test(const ML_KEM_KEY *key)
|
|||
goto err;
|
||||
|
||||
memset(out, 0, sizeof(out));
|
||||
#ifdef FIPS_MODULE
|
||||
|
||||
/*
|
||||
* The FIPS module does a PCT on power-on, and would leak the RNG
|
||||
* handle if use random entropy here. So we use fixed entropy in
|
||||
* the FIPS case. Ideally, the leak will be fixed, and the test
|
||||
* will also use random entropy in FIPS mode.
|
||||
* The pairwise test is skipped unless either RANDOM or FIXED entropy PCTs
|
||||
* are enabled.
|
||||
*/
|
||||
if (key_flags & ML_KEM_KEY_RANDOM_PCT) {
|
||||
operation_result = ossl_ml_kem_encap_rand(ctext, v->ctext_bytes,
|
||||
secret, sizeof(secret), key);
|
||||
} else {
|
||||
memset(entropy, 0125, sizeof(entropy));
|
||||
operation_result = ossl_ml_kem_encap_seed(ctext, v->ctext_bytes,
|
||||
secret, sizeof(secret),
|
||||
entropy, sizeof(entropy),
|
||||
key);
|
||||
#else
|
||||
operation_result = ossl_ml_kem_encap_rand(ctext, v->ctext_bytes,
|
||||
secret, sizeof(secret), key);
|
||||
#endif
|
||||
}
|
||||
if (operation_result != 1)
|
||||
goto err;
|
||||
|
||||
|
@ -144,17 +145,38 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void *ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type)
|
||||
ML_KEM_KEY *ossl_prov_ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type)
|
||||
{
|
||||
ML_KEM_KEY *key;
|
||||
|
||||
if (!ossl_prov_is_running())
|
||||
return NULL;
|
||||
/*
|
||||
* When decoding, if the key ends up "loaded" into the same provider, these
|
||||
* are the correct config settings, otherwise, new values will be assigned
|
||||
* on import into a different provider. The "load" API does not pass along
|
||||
* the provider context.
|
||||
*/
|
||||
if ((key = ossl_ml_kem_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type)) != NULL) {
|
||||
key->retain_seed = ossl_prov_ctx_get_bool_param(
|
||||
ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1);
|
||||
key->prefer_seed = ossl_prov_ctx_get_bool_param(
|
||||
ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1);
|
||||
const char *pct_type = ossl_prov_ctx_get_param(
|
||||
ctx, OSSL_PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE, "random");
|
||||
|
||||
if (ossl_prov_ctx_get_bool_param(
|
||||
ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1))
|
||||
key->prov_flags |= ML_KEM_KEY_RETAIN_SEED;
|
||||
else
|
||||
key->prov_flags &= ~ML_KEM_KEY_RETAIN_SEED;
|
||||
if (ossl_prov_ctx_get_bool_param(
|
||||
ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1))
|
||||
key->prov_flags |= ML_KEM_KEY_PREFER_SEED;
|
||||
else
|
||||
key->prov_flags &= ~ML_KEM_KEY_PREFER_SEED;
|
||||
if (OPENSSL_strcasecmp(pct_type, "random") == 0)
|
||||
key->prov_flags |= ML_KEM_KEY_RANDOM_PCT;
|
||||
else if (OPENSSL_strcasecmp(pct_type, "fixed") == 0)
|
||||
key->prov_flags |= ML_KEM_KEY_FIXED_PCT;
|
||||
else
|
||||
key->prov_flags &= ~ML_KEM_KEY_PCT_TYPE;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
@ -200,7 +222,7 @@ static int ml_kem_validate(const void *vkey, int selection, int check_type)
|
|||
return 0;
|
||||
|
||||
if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)
|
||||
return ml_kem_pairwise_test(key);
|
||||
return ml_kem_pairwise_test(key, ML_KEM_KEY_RANDOM_PCT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -412,7 +434,8 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key,
|
|||
}
|
||||
}
|
||||
|
||||
if (seedlen != 0 && (prvlen == 0 || key->prefer_seed)) {
|
||||
if (seedlen != 0
|
||||
&& (prvlen == 0 || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {
|
||||
if (prvlen != 0 && !check_seed(seedenc, prvenc, key))
|
||||
return 0;
|
||||
if (!ossl_ml_kem_set_seed(seedenc, seedlen, key)
|
||||
|
@ -439,7 +462,8 @@ static int ml_kem_import(void *vkey, int selection, const OSSL_PARAM params[])
|
|||
|
||||
include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
|
||||
res = ml_kem_key_fromdata(key, params, include_private);
|
||||
if (res > 0 && include_private && !ml_kem_pairwise_test(key)) {
|
||||
if (res > 0 && include_private
|
||||
&& !ml_kem_pairwise_test(key, key->prov_flags)) {
|
||||
#ifdef FIPS_MODULE
|
||||
ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
|
||||
#endif
|
||||
|
@ -488,8 +512,9 @@ void *ml_kem_load(const void *reference, size_t reference_sz)
|
|||
&& !check_seed(seed, encoded_dk, key))
|
||||
goto err;
|
||||
/* Generate the key now, if it holds only a stashed seed. */
|
||||
if (ossl_ml_kem_have_seed(key) &&
|
||||
(encoded_dk == NULL || key->prefer_seed)) {
|
||||
if (ossl_ml_kem_have_seed(key)
|
||||
&& (encoded_dk == NULL
|
||||
|| (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {
|
||||
if (!ossl_ml_kem_genkey(NULL, 0, key)
|
||||
|| (encoded_dk != NULL && !check_pkhash(encoded_dk, key)))
|
||||
goto err;
|
||||
|
@ -501,7 +526,7 @@ void *ml_kem_load(const void *reference, size_t reference_sz)
|
|||
key->vinfo->algorithm_name);
|
||||
goto err;
|
||||
}
|
||||
if (!ml_kem_pairwise_test(key))
|
||||
if (!ml_kem_pairwise_test(key, key->prov_flags))
|
||||
goto err;
|
||||
}
|
||||
OPENSSL_free(encoded_dk);
|
||||
|
@ -724,7 +749,7 @@ static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
|
|||
OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
|
||||
return NULL;
|
||||
seed = gctx->seed;
|
||||
key = ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type);
|
||||
key = ossl_prov_ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type);
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -742,7 +767,7 @@ static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
|
|||
|
||||
if (genok) {
|
||||
#ifdef FIPS_MODULE
|
||||
if (!ml_kem_pairwise_test(key)) {
|
||||
if (!ml_kem_pairwise_test(key, ML_KEM_KEY_FIXED_PCT)) {
|
||||
ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
|
||||
ossl_ml_kem_key_free(key);
|
||||
return NULL;
|
||||
|
@ -785,7 +810,7 @@ static void *ml_kem_dup(const void *vkey, int selection)
|
|||
#define DECLARE_VARIANT(bits) \
|
||||
static void *ml_kem_##bits##_new(void *provctx) \
|
||||
{ \
|
||||
return ml_kem_new(provctx, NULL, EVP_PKEY_ML_KEM_##bits); \
|
||||
return ossl_prov_ml_kem_new(provctx, NULL, EVP_PKEY_ML_KEM_##bits); \
|
||||
} \
|
||||
static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \
|
||||
const OSSL_PARAM params[]) \
|
||||
|
|
|
@ -25,7 +25,7 @@ my @formats = qw(seed-priv priv-only seed-only oqskeypair bare-seed bare-priv);
|
|||
plan skip_all => "ML-KEM isn't supported in this build"
|
||||
if disabled("ml-kem");
|
||||
|
||||
plan tests => @algs * (24 + 10 * @formats);
|
||||
plan tests => @algs * (25 + 10 * @formats);
|
||||
my $seed = join ("", map {sprintf "%02x", $_} (0..63));
|
||||
my $weed = join ("", map {sprintf "%02x", $_} (1..64));
|
||||
my $ikme = join ("", map {sprintf "%02x", $_} (0..31));
|
||||
|
@ -160,7 +160,7 @@ foreach my $alg (@algs) {
|
|||
sprintf("text form private key: %s with %s", $alg, $f));
|
||||
}
|
||||
|
||||
# (5 tests): Test import/load PCT failure
|
||||
# (6 tests): Test import/load PCT failure
|
||||
my $real = sprintf('real-%s.der', $alg);
|
||||
my $fake = sprintf('fake-%s.der', $alg);
|
||||
my $mixt = sprintf('mixt-%s.der', $alg);
|
||||
|
@ -181,23 +181,38 @@ foreach my $alg (@algs) {
|
|||
local $/ = undef;
|
||||
my $realder = <$realfh>;
|
||||
my $fakeder = <$fakefh>;
|
||||
ok (length($realder) == 28 + (2 + 64) + (4 + $slen + $plen + $zlen)
|
||||
#
|
||||
# - 20 bytes PKCS8 fixed overhead,
|
||||
# - 4 byte private key octet string tag + length
|
||||
# - 4 byte seed + key sequence tag + length
|
||||
# - 2 byte seed tag + length
|
||||
# - 64 byte seed
|
||||
# - 4 byte key tag + length
|
||||
# - |dk| 's' vector
|
||||
# - |ek| public key ('t' vector || 'rho')
|
||||
# - implicit rejection 'z' seed component
|
||||
#
|
||||
ok(length($realder) == 28 + (2 + 64) + (4 + $slen + $plen + $zlen)
|
||||
&& length($fakeder) == 28 + (2 + 64) + (4 + $slen + $plen + $zlen));
|
||||
my $mixtder = substr($realder, 0, 28 + 66 + 4 + $slen)
|
||||
. substr($fakeder, 28 + 66 + 4 + $slen, $plen)
|
||||
. substr($realder, 28 + 66 + 4 + $slen + $plen, $zlen);
|
||||
my $mixtfh = IO::File->new($mixt, "w");
|
||||
my $mixtfh = IO::File->new($mixt, ">:raw");
|
||||
print $mixtfh $mixtder;
|
||||
$mixtfh->close();
|
||||
ok(run(app([qw(openssl pkey -inform DER -noout -in), $real],
|
||||
sprintf("accept valid keypair: %s", $alg))));
|
||||
ok(run(app([qw(openssl pkey -inform DER -noout -in), $real])),
|
||||
sprintf("accept valid keypair: %s", $alg));
|
||||
ok(!run(app([qw(openssl pkey -provparam ml-kem.prefer_seed=no),
|
||||
qw(-inform DER -noout -in), $mixt])),
|
||||
sprintf("reject real private and fake public: %s", $alg));
|
||||
ok(run(app([qw(openssl pkey -provparam ml-kem.prefer_seed=no),
|
||||
qw(-provparam ml-kem.import_pct_type=none),
|
||||
qw(-inform DER -noout -in), $mixt])),
|
||||
sprintf("Absent PCT accept fake public: %s", $alg));
|
||||
# Mutate the public key hash
|
||||
my $mashder = $realder;
|
||||
substr($mashder, -64, 1) =~ s{(.)}{chr(ord($1)^1)}es;
|
||||
my $mashfh = IO::File->new($mash, "w");
|
||||
my $mashfh = IO::File->new($mash, ">:raw");
|
||||
print $mashfh $mashder;
|
||||
$mashfh->close();
|
||||
ok(!run(app([qw(openssl pkey -inform DER -noout -in), $mash])),
|
||||
|
|
|
@ -423,6 +423,7 @@ my %params = (
|
|||
'PKEY_PARAM_ML_KEM_RETAIN_SEED' => "ml-kem.retain_seed",
|
||||
'PKEY_PARAM_ML_KEM_INPUT_FORMATS' => "ml-kem.input_formats",
|
||||
'PKEY_PARAM_ML_KEM_OUTPUT_FORMATS' => "ml-kem.output_formats",
|
||||
'PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE' => "ml-kem.import_pct_type",
|
||||
|
||||
# Key generation parameters
|
||||
'PKEY_PARAM_FFC_TYPE' => "type",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue