]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Two more private key checks.
authorViktor Dukhovni <openssl-users@dukhovni.org>
Fri, 7 Feb 2025 08:16:33 +0000 (19:16 +1100)
committerTomas Mraz <tomas@openssl.org>
Fri, 14 Feb 2025 09:50:59 +0000 (10:50 +0100)
- 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 <shane.lontis@oracle.com>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26656)

doc/man7/EVP_PKEY-ML-KEM.pod
providers/implementations/encode_decode/ml_kem_codecs.c
providers/implementations/encode_decode/ml_kem_codecs.h
providers/implementations/keymgmt/ml_kem_kmgmt.c

index f71a96a982271be9214bff271f24e5dc9b3acd0d..07ede01cf029cca1f0169f570420259a0ac5fe81 100644 (file)
@@ -146,11 +146,12 @@ This format represents B<PKCS#8> objects in which both the FIPS 203 64-byte
 B<(d, z)> seed and the decapsulation key B<dk> 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<seed-priv> format is not included in the list, this format will not be
 recognised on input.
index 21a95bae5619d43d75cb4392db5522ac58de8076..46fe8110480149a52a503e77c9722c42502455cc 100644 (file)
  * 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:
  *
index 4d64a536f65b0400fe0b634a78aa621eca2e8044..aa9e77e360d5238c6a7115a3085f689faa080ccd 100644 (file)
@@ -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:
  *
index 16f5736ab301aaf59a72706299e803e97a9200a0..2853698cc2c8a9152998d68b25d7750d1c48bec2 100644 (file)
@@ -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)) {