diff --git a/README.md b/README.md index aabbebe..e9e35fa 100644 --- a/README.md +++ b/README.md @@ -82,10 +82,12 @@ To install dependencies, run `pip -r install requirements`. ### ML-KEM -There are three functions exposed on the `ML_KEM` class which are intended for +There are four functions exposed on the `ML_KEM` class which are intended for use: - `ML_KEM.keygen()`: generate a keypair `(ek, dk)` +- `ML_KEM.key_derive(seed)`: generate a keypair `(ek, dk)` from the provided + seed - `ML_KEM.encaps(ek)`: generate a key and ciphertext pair `(key, ct)` - `ML_KEM.decaps(dk, ct)`: generate the shared key `key` diff --git a/interop/README.md b/interop/README.md index da5f93c..ba230ff 100644 --- a/interop/README.md +++ b/interop/README.md @@ -1,7 +1,7 @@ Tools that allow use of the OpenSSL Encapsulation and Decapsulation API. -**Note:** this code expects draft-ietf-lamps-kyber-certificates-04 compatible behaviour. -This is consistent with oqsprovider-0.8.0. +**Note:** this code expects draft-ietf-lamps-kyber-certificates-06 compatible +behaviour. OpenSSL setup diff --git a/interop/ml_kem_decap.py b/interop/ml_kem_decap.py index 8deb2a4..e7e37a8 100644 --- a/interop/ml_kem_decap.py +++ b/interop/ml_kem_decap.py @@ -37,17 +37,12 @@ kem = OIDS[alg_id] -key_der, empty = der.remove_octet_string(rest) +key, empty = der.remove_octet_string(rest) if empty != b"": raise der.UnexpectedDER("Trailing junk after the key") -keys, empty = der.remove_octet_string(key_der) -if empty != b"": - raise der.UnexpectedDER("Trailing junk after the key") - -dk_len = 768 * kem.k + 96 -dk, ek = keys[:dk_len], keys[dk_len:] -assert len(ek) == 384 * kem.k + 32 +assert len(key) == 64 +_, dk = kem.key_derive(key) with open(sys.argv[3], "rb") as encaps_file: encaps = encaps_file.read() diff --git a/interop/ml_kem_key_extract.py b/interop/ml_kem_key_extract.py new file mode 100644 index 0000000..252f309 --- /dev/null +++ b/interop/ml_kem_key_extract.py @@ -0,0 +1,52 @@ +import sys + +if len(sys.argv) != 3: + raise ValueError(f"Usage: {sys.argv[0]} dk.pem ek.pem") + +from kyber_py.ml_kem import ML_KEM_512, ML_KEM_768, ML_KEM_1024 + +OIDS = { + (2, 16, 840, 1, 101, 3, 4, 4, 1): ML_KEM_512, + (2, 16, 840, 1, 101, 3, 4, 4, 2): ML_KEM_768, + (2, 16, 840, 1, 101, 3, 4, 4, 3): ML_KEM_1024, +} + +import ecdsa.der as der + +with open(sys.argv[1], "rt") as ek_file: + ek_pem = ek_file.read() + +ek_der = der.unpem(ek_pem) + +s1, empty = der.remove_sequence(ek_der) +if empty != b"": + raise der.UnexpectedDER("Trailing junk after DER public key") + +ver, rest = der.remove_integer(s1) + +if ver != 0: + raise der.UnexpectedDER("Unexpected format version") + +alg_id, rest = der.remove_sequence(rest) + +alg_id, empty = der.remove_object(alg_id) +if alg_id not in OIDS: + raise der.UnexpectedDER(f"Not recognised algoritm OID: {alg_id}") +if empty != b"": + raise der.UnexpectedDER("parameters specified for ML-KEM OID") + +kem = OIDS[alg_id] + +key, empty = der.remove_octet_string(rest) +if empty != b"": + raise der.UnexpectedDER("Trailing junk after the key") + +assert len(key) == 64 +ek, _ = kem.key_derive(key) + +with open(sys.argv[2], "wb") as ek_file: + encoded = der.encode_sequence( + der.encode_sequence(der.encode_oid(*alg_id)), + der.encode_bitstring(ek, 0), + ) + ek_file.write(der.topem(encoded, "PUBLIC KEY")) diff --git a/interop/ml_kem_keygen.py b/interop/ml_kem_keygen.py index 289103b..ea0331b 100644 --- a/interop/ml_kem_keygen.py +++ b/interop/ml_kem_keygen.py @@ -21,8 +21,11 @@ raise ValueError(f"Unrecognised algorithm: {sys.argv[1]}") import ecdsa.der as der +import os -ek, dk = ML_KEM.keygen() +seed = os.urandom(64) + +ek, _ = ML_KEM.key_derive(seed) with open(sys.argv[2], "wb") as ek_file: encoded = der.encode_sequence( @@ -35,6 +38,6 @@ encoded = der.encode_sequence( der.encode_integer(0), der.encode_sequence(der.encode_oid(*oid)), - der.encode_octet_string(der.encode_octet_string(dk + ek)), + der.encode_octet_string(seed), ) dk_file.write(der.topem(encoded, "PRIVATE KEY"))