Skip to content

Commit cd4991d

Browse files
committed
align file format with draft-ietf-lamps-kyber-certificates-06
1 parent bb6fcd5 commit cd4991d

File tree

6 files changed

+92
-13
lines changed

6 files changed

+92
-13
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,12 @@ To install dependencies, run `pip -r install requirements`.
8282

8383
### ML-KEM
8484

85-
There are three functions exposed on the `ML_KEM` class which are intended for
85+
There are four functions exposed on the `ML_KEM` class which are intended for
8686
use:
8787

8888
- `ML_KEM.keygen()`: generate a keypair `(ek, dk)`
89+
- `ML_KEM.key_derive(seed)`: generate a keypair `(ek, dk)` from the provided
90+
seed
8991
- `ML_KEM.encaps(ek)`: generate a key and ciphertext pair `(key, ct)`
9092
- `ML_KEM.decaps(dk, ct)`: generate the shared key `key`
9193

interop/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Tools that allow use of the OpenSSL Encapsulation and Decapsulation API.
22

3-
**Note:** this code expects draft-ietf-lamps-kyber-certificates-04 compatible behaviour.
4-
This is consistent with oqsprovider-0.8.0.
3+
**Note:** this code expects draft-ietf-lamps-kyber-certificates-06 compatible
4+
behaviour.
55

66

77
OpenSSL setup

interop/ml_kem_decap.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,12 @@
3737

3838
kem = OIDS[alg_id]
3939

40-
key_der, empty = der.remove_octet_string(rest)
40+
key, empty = der.remove_octet_string(rest)
4141
if empty != b"":
4242
raise der.UnexpectedDER("Trailing junk after the key")
4343

44-
keys, empty = der.remove_octet_string(key_der)
45-
if empty != b"":
46-
raise der.UnexpectedDER("Trailing junk after the key")
47-
48-
dk_len = 768 * kem.k + 96
49-
dk, ek = keys[:dk_len], keys[dk_len:]
50-
assert len(ek) == 384 * kem.k + 32
44+
assert len(key) == 64
45+
_, dk = kem.key_derive(key)
5146

5247
with open(sys.argv[3], "rb") as encaps_file:
5348
encaps = encaps_file.read()

interop/ml_kem_key_extract.py

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import sys
2+
3+
if len(sys.argv) != 3:
4+
raise ValueError(
5+
f"Usage: {sys.argv[0]} dk.pem ek.pem"
6+
)
7+
8+
from kyber_py.ml_kem import ML_KEM_512, ML_KEM_768, ML_KEM_1024
9+
10+
OIDS = {
11+
(2, 16, 840, 1, 101, 3, 4, 4, 1): ML_KEM_512,
12+
(2, 16, 840, 1, 101, 3, 4, 4, 2): ML_KEM_768,
13+
(2, 16, 840, 1, 101, 3, 4, 4, 3): ML_KEM_1024,
14+
}
15+
16+
import ecdsa.der as der
17+
18+
with open(sys.argv[1], "rt") as ek_file:
19+
ek_pem = ek_file.read()
20+
21+
ek_der = der.unpem(ek_pem)
22+
23+
s1, empty = der.remove_sequence(ek_der)
24+
if empty != b"":
25+
raise der.UnexpectedDER("Trailing junk after DER public key")
26+
27+
ver, rest = der.remove_integer(s1)
28+
29+
if ver != 0:
30+
raise der.UnexpectedDER("Unexpected format version")
31+
32+
alg_id, rest = der.remove_sequence(rest)
33+
34+
alg_id, empty = der.remove_object(alg_id)
35+
if alg_id not in OIDS:
36+
raise der.UnexpectedDER(f"Not recognised algoritm OID: {alg_id}")
37+
if empty != b"":
38+
raise der.UnexpectedDER("parameters specified for ML-KEM OID")
39+
40+
kem = OIDS[alg_id]
41+
42+
key, empty = der.remove_octet_string(rest)
43+
if empty != b"":
44+
raise der.UnexpectedDER("Trailing junk after the key")
45+
46+
#key, empty = der.remove_octet_string(key_der)
47+
#if empty != b"":
48+
# raise der.UnexpectedDER("Trailing junk after the key")
49+
50+
assert len(key) == 64
51+
ek, _ = kem.key_derive(key)
52+
53+
with open(sys.argv[2], "wb") as ek_file:
54+
encoded = der.encode_sequence(
55+
der.encode_sequence(der.encode_oid(*alg_id)),
56+
der.encode_bitstring(ek, 0),
57+
)
58+
ek_file.write(der.topem(encoded, "PUBLIC KEY"))
59+

interop/ml_kem_keygen.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
raise ValueError(f"Unrecognised algorithm: {sys.argv[1]}")
2222

2323
import ecdsa.der as der
24+
import os
2425

25-
ek, dk = ML_KEM.keygen()
26+
seed = os.urandom(64)
27+
28+
ek, _ = ML_KEM.key_derive(seed)
2629

2730
with open(sys.argv[2], "wb") as ek_file:
2831
encoded = der.encode_sequence(
@@ -35,6 +38,6 @@
3538
encoded = der.encode_sequence(
3639
der.encode_integer(0),
3740
der.encode_sequence(der.encode_oid(*oid)),
38-
der.encode_octet_string(der.encode_octet_string(dk + ek)),
41+
der.encode_octet_string(seed),
3942
)
4043
dk_file.write(der.topem(encoded, "PRIVATE KEY"))

src/kyber_py/ml_kem/ml_kem.py

+20
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,26 @@ def keygen(self):
292292
) = self._keygen_internal(d, z)
293293
return (ek, dk)
294294

295+
def key_derive(self, seed):
296+
"""
297+
Derive an encapsulation key and corresponding decapsulation key
298+
following the approach from Section 7.1 (FIPS 203)
299+
with storage of the ``seed`` value for later expansion.
300+
301+
``seed`` is a byte-encoded concatenation of the ``d`` and ``z``
302+
values.
303+
304+
:return: Tuple with encapsulation key and decapsulation key.
305+
:rtype: tuple(bytes, bytes)
306+
"""
307+
if len(seed) != 64:
308+
raise ValueError("The seed must be 64 bytes long")
309+
310+
d = seed[:32]
311+
z = seed[32:]
312+
ek, dk = self._keygen_internal(d, z)
313+
return (ek, dk)
314+
295315
def _encaps_internal(self, ek, m):
296316
"""
297317
Uses the encapsulation key and randomness to generate a key and an

0 commit comments

Comments
 (0)