forked from GiacomoPope/kyber-py
-
Notifications
You must be signed in to change notification settings - Fork 1
/
test_kyber.py
163 lines (132 loc) · 4.64 KB
/
test_kyber.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import unittest
import os
import pytest
from kyber_py.kyber import Kyber512, Kyber768, Kyber1024
from kyber_py.drbg.aes256_ctr_drbg import AES256_CTR_DRBG
def parse_kat_data(data):
parsed_data = {}
count_blocks = data.split("\n\n")
for block in count_blocks[:-1]:
block_data = block.split("\n")
count, seed, pk, sk, ct, ss = [
line.split(" = ")[-1] for line in block_data
]
parsed_data[int(count)] = {
"seed": bytes.fromhex(seed),
"pk": bytes.fromhex(pk),
"sk": bytes.fromhex(sk),
"ct": bytes.fromhex(ct),
"ss": bytes.fromhex(ss),
}
return parsed_data
class TestKyber(unittest.TestCase):
"""
Test Kyber levels for internal consistency by generating keypairs and
shared secrets.
"""
def generic_test_kyber(self, Kyber, count):
for _ in range(count):
pk, sk = Kyber.keygen()
for _ in range(count):
key, c = Kyber.encaps(pk)
# Correct decaps works
_key = Kyber.decaps(c, sk)
self.assertEqual(key, _key)
# Incorrect ct does not work
_bad_ct = bytes([0] * len(c))
_bad = Kyber.decaps(_bad_ct, sk)
self.assertNotEqual(key, _bad)
def test_kyber512(self):
self.generic_test_kyber(Kyber512, 5)
def test_kyber768(self):
self.generic_test_kyber(Kyber768, 5)
def test_kyber1024(self):
self.generic_test_kyber(Kyber1024, 5)
def test_xof_failure(self):
self.assertRaises(ValueError, lambda: Kyber512._xof(b"1", b"2", b"3"))
def test_prf_failure(self):
self.assertRaises(ValueError, lambda: Kyber512._prf(b"1", b"2", 32))
class TestKyberDeterministic(unittest.TestCase):
"""
Ensure that deterministic DRBG is deterministic!
Uses AES256 CTR DRBG for randomness.
Note: requires pycryptodome for AES impl.
(Seemed overkill to code my own AES for Kyber)
"""
def generic_test_kyber_deterministic(self, Kyber, count):
"""
First we generate five pk,sk pairs
from the same seed and make sure
they're all the same
"""
seed = os.urandom(48)
pk_output = []
for _ in range(count):
Kyber.set_drbg_seed(seed)
pk, sk = Kyber.keygen()
pk_output.append(pk + sk)
self.assertEqual(len(pk_output), 5)
self.assertEqual(len(set(pk_output)), 1)
"""
Now given a fixed keypair make sure
that c,key are the same for a fixed seed
"""
key_output = []
seed = os.urandom(48)
pk, sk = Kyber.keygen()
for _ in range(count):
Kyber.set_drbg_seed(seed)
key, c = Kyber.encaps(pk)
_key = Kyber.decaps(c, sk)
# Check key derivation works
self.assertEqual(key, _key)
key_output.append(c + key)
self.assertEqual(len(key_output), count)
self.assertEqual(len(set(key_output)), 1)
def test_kyber512_deterministic(self):
self.generic_test_kyber_deterministic(Kyber512, 5)
def test_kyber768_deterministic(self):
self.generic_test_kyber_deterministic(Kyber768, 5)
def test_kyber1024_deterministic(self):
self.generic_test_kyber_deterministic(Kyber1024, 5)
def data_parse(filename):
# Set DRBG to generate seeds
entropy_input = bytes([i for i in range(48)])
rng = AES256_CTR_DRBG(entropy_input)
with open(filename) as f:
kat_data = f.read()
parsed_data = parse_kat_data(kat_data)
return [(rng.random_bytes(48), i) for i in parsed_data.values()]
@pytest.mark.parametrize(
"Kyber, seed, data",
[
(kem, seed, param)
for kem, filename in [
(Kyber512, "assets/PQCkemKAT_1632.rsp"),
(Kyber768, "assets/PQCkemKAT_2400.rsp"),
(Kyber1024, "assets/PQCkemKAT_3168.rsp"),
]
for seed, param in data_parse(filename)
],
ids=[
f"{kem}-test-{num}"
for kem in ["Kyber512", "Kyber768", "Kyber1024"]
for num in range(100)
],
)
def test_generic_kyber_known_answer(Kyber, seed, data):
# Set the seed and check it matches the KAT
assert seed == data["seed"]
# Seed DRBG with KAT seed
Kyber.set_drbg_seed(seed)
# Assert keygen matches
pk, sk = Kyber.keygen()
assert pk == data["pk"]
assert sk == data["sk"]
# Assert encapsulation matches
ss, ct = Kyber.encaps(pk)
assert ss == data["ss"]
assert ct == data["ct"]
# Assert decapsulation matches
_ss = Kyber.decaps(ct, sk)
assert _ss == data["ss"]