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.
* 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:
*
* 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:
*
{
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? */
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);
{
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 */
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)) {