From: Viktor Dukhovni Date: Fri, 7 Feb 2025 08:16:33 +0000 (+1100) Subject: Two more private key checks. X-Git-Tag: openssl-3.5.0-alpha1~512 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b3dd681f073817660ba4710516a033b6e1344b46;p=thirdparty%2Fopenssl.git Two more private key checks. - When a PKCS#8 has both seed and key cross check the implicit rejection value |z| - When an import (EVP_PKEY_fromdata call) provides both a private and public key, fail if the redundant public key does not match the copy in the private key. Reviewed-by: Shane Lontis Reviewed-by: Tim Hudson Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/26656) --- diff --git a/doc/man7/EVP_PKEY-ML-KEM.pod b/doc/man7/EVP_PKEY-ML-KEM.pod index f71a96a9822..07ede01cf02 100644 --- a/doc/man7/EVP_PKEY-ML-KEM.pod +++ b/doc/man7/EVP_PKEY-ML-KEM.pod @@ -146,11 +146,12 @@ This format represents B objects in which both the FIPS 203 64-byte B<(d, z)> seed and the decapsulation key B are present in the private key as part of the DER encoding of the ASN.1 sequence: - PrivateKey ::= SEQUENCE { - seed OCTET STRING OPTIONAL, - expandedKey [1] IMPLICIT OCTET STRING OPTIONAL } - (WITH COMPONENTS {..., seed PRESENT } | - WITH COMPONENTS {..., expandedKey PRESENT }) + ML-KEM-PrivateKey ::= CHOICE { + seed [0] IMPLICIT OCTET STRING (SIZE (64)), + expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)), + both SEQUENCE { + seed OCTET STRING (SIZE (64)), + expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)) } } If the C format is not included in the list, this format will not be recognised on input. diff --git a/providers/implementations/encode_decode/ml_kem_codecs.c b/providers/implementations/encode_decode/ml_kem_codecs.c index 21a95bae561..46fe8110480 100644 --- a/providers/implementations/encode_decode/ml_kem_codecs.c +++ b/providers/implementations/encode_decode/ml_kem_codecs.c @@ -21,11 +21,11 @@ * corresponding to the "either or both" variants of: * * ML-KEM-PrivateKey ::= CHOICE { - * seed [0] IMPLICIT OCTET STRING SIZE (64), - * expandedKey OCTET STRING SIZE (1632 | 2400 | 3168) + * seed [0] IMPLICIT OCTET STRING (SIZE (64)), + * expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)), * both SEQUENCE { - * seed OCTET STRING SIZE (64), - * expandedKey OCTET STRING SIZE (1632 | 2400 | 3168) } } + * seed OCTET STRING (SIZE (64)), + * expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)) } } * * one more for a historical OQS encoding: * diff --git a/providers/implementations/encode_decode/ml_kem_codecs.h b/providers/implementations/encode_decode/ml_kem_codecs.h index 4d64a536f65..aa9e77e360d 100644 --- a/providers/implementations/encode_decode/ml_kem_codecs.h +++ b/providers/implementations/encode_decode/ml_kem_codecs.h @@ -36,11 +36,11 @@ typedef struct { * corresponding to the "either or both" variants of: * * ML-KEM-PrivateKey ::= CHOICE { - * seed [0] IMPLICIT OCTET STRING SIZE (64), - * expandedKey OCTET STRING SIZE (1632 | 2400 | 3168) + * seed [0] IMPLICIT OCTET STRING (SIZE (64)), + * expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)), * both SEQUENCE { - * seed OCTET STRING SIZE (64), - * expandedKey OCTET STRING SIZE (1632 | 2400 | 3168) } } + * seed OCTET STRING (SIZE (64)), + * expandedKey OCTET STRING SIZE ((1632 | 2400 | 3168)) } } * * one more for a historical OQS encoding: * diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.c b/providers/implementations/keymgmt/ml_kem_kmgmt.c index 16f5736ab30..2853698cc2c 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.c +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.c @@ -318,7 +318,7 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key, { const OSSL_PARAM *p = NULL; const void *pubenc = NULL, *prvenc = NULL, *seedenc = NULL; - size_t publen = 0, prvlen = 0, seedlen = 0; + size_t publen = 0, prvlen = 0, seedlen = 0, puboff; const ML_KEM_VINFO *v; /* Invalid attempt to mutate a key, what is the right error to report? */ @@ -370,6 +370,18 @@ static int ml_kem_key_fromdata(ML_KEM_KEY *key, return 0; } + /* Check any explicit public key against embedded value in private key */ + if (publen > 0 && prvlen > 0) { + /* point to the ek offset in the DKpke||ek||H(ek)||z */ + puboff = prvlen - ML_KEM_RANDOM_BYTES - ML_KEM_PKHASH_BYTES - publen; + if (memcmp(pubenc, (unsigned char *)prvenc + puboff, publen) != 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "explicit %s public key does not match private", + v->algorithm_name); + return 0; + } + } + if (seedlen != 0 && (prvlen == 0 || key->prefer_seed)) return ossl_ml_kem_set_seed(seedenc, seedlen, key) && ossl_ml_kem_genkey(NULL, 0, key); @@ -427,6 +439,8 @@ void *ml_kem_load(const void *reference, size_t reference_sz) { ML_KEM_KEY *key = NULL; uint8_t *encoded_dk; + uint8_t seed[ML_KEM_SEED_BYTES]; + size_t zlen = ML_KEM_RANDOM_BYTES; if (ossl_prov_is_running() && reference_sz == sizeof(key)) { /* The contents of the reference is the address to our object */ @@ -435,6 +449,20 @@ void *ml_kem_load(const void *reference, size_t reference_sz) key->encoded_dk = NULL; /* We grabbed, so we detach it */ *(ML_KEM_KEY **)reference = NULL; + /* + * Reject |z| mismatch between seed and key, the seed buffer holds |z| + * followed by |d|. + */ + if (encoded_dk != NULL + && ossl_ml_kem_encode_seed(seed, sizeof(seed), key) + && memcmp(seed + sizeof(seed) - zlen, + encoded_dk + key->vinfo->prvkey_bytes - zlen, + zlen) != 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "private %s key implicit rejection secret does" + " not match seed", key->vinfo->algorithm_name); + 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)) {