]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Check full ML-KEM encoded key
authorViktor Dukhovni <openssl-users@dukhovni.org>
Wed, 26 Feb 2025 02:04:12 +0000 (13:04 +1100)
committerNeil Horman <nhorman@openssl.org>
Thu, 27 Feb 2025 16:06:29 +0000 (11:06 -0500)
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 <tomas@openssl.org>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26905)

providers/implementations/keymgmt/ml_kem_kmgmt.c
test/recipes/15-test_ml_kem_codecs.t

index 94f923d6537e3b76423b8a4720f6b22164cf0620..2294d0fd4f7f87e821c06012d76445c372c5a816 100644 (file)
@@ -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,
index feee099aaab87c5d32047b5974e70acf14455068..72e1b3333435d142201a33f1628232ef94a15b48 100644 (file)
@@ -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();