From: Viktor Dukhovni Date: Wed, 26 Feb 2025 02:04:12 +0000 (+1100) Subject: Check full ML-KEM encoded key X-Git-Tag: openssl-3.5.0-alpha1~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6ef393b89be1f329214ae07388812b245950095f;p=thirdparty%2Fopenssl.git Check full ML-KEM encoded key When both seed and key are provided compare the full ML-KEM private key with the seed keygen output, not just the public key. Reviewed-by: Tomas Mraz Reviewed-by: Tim Hudson Reviewed-by: Neil Horman (Merged from https://github.com/openssl/openssl/pull/26905) --- diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.c b/providers/implementations/keymgmt/ml_kem_kmgmt.c index 94f923d6537..2294d0fd4f7 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.c +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.c @@ -353,18 +353,23 @@ static int check_seed(const uint8_t *seed, const uint8_t *prvenc, return 0; } -static int check_pkhash(const uint8_t *prvenc, ML_KEM_KEY *key) +static int check_prvenc(const uint8_t *prvenc, ML_KEM_KEY *key) { - size_t off; + size_t len = key->vinfo->prvkey_bytes; + uint8_t *buf = OPENSSL_malloc(len); + int ret = 0; - /* point to the H(ek) offset in dk = DKpke||ek||H(ek)||z */ - off = key->vinfo->prvkey_bytes - ML_KEM_RANDOM_BYTES - ML_KEM_PKHASH_BYTES; - if (memcmp(key->pkhash, prvenc + off, ML_KEM_PKHASH_BYTES) == 0) + if (buf != NULL + && ossl_ml_kem_encode_private_key(buf, len, key)) + ret = memcmp(buf, prvenc, len) == 0; + OPENSSL_clear_free(buf, len); + if (ret) return 1; - ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, - "explicit %s private key does not match seed", - key->vinfo->algorithm_name); + if (buf != NULL) + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "explicit %s private key does not match seed", + key->vinfo->algorithm_name); ossl_ml_kem_key_reset(key); return 0; } @@ -446,7 +451,7 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key, if (!ossl_ml_kem_set_seed(seedenc, seedlen, key) || !ossl_ml_kem_genkey(NULL, 0, key)) return 0; - return prvlen == 0 || check_pkhash(prvenc, key); + return prvlen == 0 || check_prvenc(prvenc, key); } else if (prvlen != 0) { return ossl_ml_kem_parse_private_key(prvenc, prvlen, key); } @@ -521,7 +526,7 @@ void *ml_kem_load(const void *reference, size_t reference_sz) && (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))) + || (encoded_dk != NULL && !check_prvenc(encoded_dk, key))) goto err; } else if (encoded_dk != NULL) { if (!ossl_ml_kem_parse_private_key(encoded_dk, diff --git a/test/recipes/15-test_ml_kem_codecs.t b/test/recipes/15-test_ml_kem_codecs.t index feee099aaab..72e1b333343 100644 --- a/test/recipes/15-test_ml_kem_codecs.t +++ b/test/recipes/15-test_ml_kem_codecs.t @@ -194,12 +194,13 @@ foreach my $alg (@algs) { # - |ek| public key ('t' vector || 'rho') # - implicit rejection 'z' seed component # - my $p8_len = 28 + (2 + 64) + (4 + $slen + $plen + $zlen); + my $svec_off = 28 + (2 + 64) + 4; + my $p8_len = $svec_off + $slen + $plen + $zlen; ok((length($realder) == $p8_len && length($fakeder) == $p8_len), sprintf("Got expected DER lengths of %s seed-priv key", $alg)); - 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 $mixtder = substr($realder, 0, $svec_off + $slen) + . substr($fakeder, $svec_off + $slen, $plen) + . substr($realder, $svec_off + $slen + $plen, $zlen); my $mixtfh = IO::File->new($mixt, ">:raw"); print $mixtfh $mixtder; $mixtfh->close(); @@ -212,9 +213,9 @@ foreach my $alg (@algs) { 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 + # Mutate the first byte of the |s| vector my $mashder = $realder; - substr($mashder, -64, 1) =~ s{(.)}{chr(ord($1)^1)}es; + substr($mashder, $svec_off, 1) =~ s{(.)}{chr(ord($1)^1)}es; my $mashfh = IO::File->new($mash, ">:raw"); print $mashfh $mashder; $mashfh->close();