nexus-crypt 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nexus/__init__.py +14 -0
- nexus/constants.py +28 -0
- nexus/core.py +232 -0
- nexus/exceptions.py +11 -0
- nexus/kem.py +25 -0
- nexus/math_utils.py +124 -0
- nexus/signature.py +26 -0
- nexus_crypt-1.0.0.dist-info/METADATA +133 -0
- nexus_crypt-1.0.0.dist-info/RECORD +18 -0
- nexus_crypt-1.0.0.dist-info/WHEEL +5 -0
- nexus_crypt-1.0.0.dist-info/licenses/LICENSE +21 -0
- nexus_crypt-1.0.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/test_cipher.py +259 -0
- tests/test_integrations.py +281 -0
- tests/test_kem.py +0 -0
- tests/test_math_utils.py +241 -0
- tests/test_signature.py +0 -0
tests/test_math_utils.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Unit tests for mathematical utilities"""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import secrets
|
|
5
|
+
from nexus.math_utils import (
|
|
6
|
+
rol, ror, bytes_to_words, words_to_bytes,
|
|
7
|
+
gf128_multiply, generate_lwe_sbox, generate_ntru_permutation,
|
|
8
|
+
hkdf_sha256, ensure_bijection
|
|
9
|
+
)
|
|
10
|
+
from nexus.constants import SBOX_SIZE, NTRU_N
|
|
11
|
+
|
|
12
|
+
class TestRotationOperations:
|
|
13
|
+
"""Test rotation operations"""
|
|
14
|
+
|
|
15
|
+
def test_rotate_left(self):
|
|
16
|
+
"""Test left rotation"""
|
|
17
|
+
value = 0b1010
|
|
18
|
+
rotated = rol(value, 1, bits=4)
|
|
19
|
+
assert rotated == 0b0101
|
|
20
|
+
|
|
21
|
+
def test_rotate_right(self):
|
|
22
|
+
"""Test right rotation"""
|
|
23
|
+
value = 0b1010
|
|
24
|
+
rotated = ror(value, 1, bits=4)
|
|
25
|
+
assert rotated == 0b0101
|
|
26
|
+
|
|
27
|
+
def test_rotate_left_wraparound(self):
|
|
28
|
+
"""Test left rotation with wraparound"""
|
|
29
|
+
value = 0b1000
|
|
30
|
+
rotated = rol(value, 1, bits=4)
|
|
31
|
+
assert rotated == 0b0001
|
|
32
|
+
|
|
33
|
+
def test_rotate_128bit(self):
|
|
34
|
+
"""Test 128-bit rotation"""
|
|
35
|
+
value = (1 << 127) # MSB set
|
|
36
|
+
rotated = rol(value, 1, bits=128)
|
|
37
|
+
assert rotated == 1 # Wrapped to LSB
|
|
38
|
+
|
|
39
|
+
def test_rotation_identity(self):
|
|
40
|
+
"""Test that rotating by full width returns original"""
|
|
41
|
+
value = 0xABCD
|
|
42
|
+
rotated = rol(value, 128, bits=128)
|
|
43
|
+
assert rotated == value
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TestWordConversion:
|
|
47
|
+
"""Test byte/word conversion"""
|
|
48
|
+
|
|
49
|
+
def test_bytes_to_words(self):
|
|
50
|
+
"""Test converting bytes to 128-bit words"""
|
|
51
|
+
data = b'\x00' * 16 + b'\xFF' * 16
|
|
52
|
+
words = bytes_to_words(data)
|
|
53
|
+
|
|
54
|
+
assert len(words) == 2
|
|
55
|
+
assert words[0] == 0
|
|
56
|
+
assert words[1] == (1 << 128) - 1
|
|
57
|
+
|
|
58
|
+
def test_words_to_bytes(self):
|
|
59
|
+
"""Test converting words back to bytes"""
|
|
60
|
+
words = [0xABCD, 0x1234]
|
|
61
|
+
data = words_to_bytes(words)
|
|
62
|
+
|
|
63
|
+
assert len(data) == 32
|
|
64
|
+
assert bytes_to_words(data) == words
|
|
65
|
+
|
|
66
|
+
def test_round_trip_conversion(self):
|
|
67
|
+
"""Test round-trip byte <-> word conversion"""
|
|
68
|
+
original = secrets.token_bytes(64)
|
|
69
|
+
words = bytes_to_words(original)
|
|
70
|
+
recovered = words_to_bytes(words)
|
|
71
|
+
|
|
72
|
+
assert recovered == original
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestGaloisFieldArithmetic:
|
|
76
|
+
"""Test GF(2^128) operations"""
|
|
77
|
+
|
|
78
|
+
def test_gf128_multiply_zero(self):
|
|
79
|
+
"""Test multiplication by zero"""
|
|
80
|
+
a = 0xFFFFFFFF
|
|
81
|
+
result = gf128_multiply(a, 0)
|
|
82
|
+
assert result == 0
|
|
83
|
+
|
|
84
|
+
def test_gf128_multiply_one(self):
|
|
85
|
+
"""Test multiplication by one (identity)"""
|
|
86
|
+
a = 0x123456789ABCDEF
|
|
87
|
+
result = gf128_multiply(a, 1)
|
|
88
|
+
assert result == a
|
|
89
|
+
|
|
90
|
+
def test_gf128_multiply_commutativity(self):
|
|
91
|
+
"""Test that multiplication is commutative"""
|
|
92
|
+
a = 0xABCD1234
|
|
93
|
+
b = 0x5678EF12 # Random value
|
|
94
|
+
|
|
95
|
+
result1 = gf128_multiply(a, b)
|
|
96
|
+
result2 = gf128_multiply(b, a)
|
|
97
|
+
|
|
98
|
+
assert result1 == result2
|
|
99
|
+
|
|
100
|
+
def test_gf128_multiply_produces_valid_result(self):
|
|
101
|
+
"""Test that result fits in 128 bits"""
|
|
102
|
+
a = (1 << 127) # Large value
|
|
103
|
+
b = (1 << 127)
|
|
104
|
+
|
|
105
|
+
result = gf128_multiply(a, b)
|
|
106
|
+
assert result < (1 << 128)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class TestLWESbox:
|
|
110
|
+
"""Test LWE S-box generation"""
|
|
111
|
+
|
|
112
|
+
def test_sbox_correct_size(self):
|
|
113
|
+
"""Test S-box has correct size"""
|
|
114
|
+
key = secrets.token_bytes(32)
|
|
115
|
+
sbox = generate_lwe_sbox(key, 0, 0)
|
|
116
|
+
|
|
117
|
+
assert len(sbox) == SBOX_SIZE
|
|
118
|
+
|
|
119
|
+
def test_sbox_is_bijection(self):
|
|
120
|
+
"""Test S-box is a valid permutation"""
|
|
121
|
+
key = secrets.token_bytes(32)
|
|
122
|
+
sbox = generate_lwe_sbox(key, 0, 0)
|
|
123
|
+
|
|
124
|
+
# All values should be unique
|
|
125
|
+
assert len(set(sbox)) == SBOX_SIZE
|
|
126
|
+
# All values in range [0, 255]
|
|
127
|
+
assert all(0 <= v < 256 for v in sbox)
|
|
128
|
+
|
|
129
|
+
def test_sbox_deterministic(self):
|
|
130
|
+
"""Test S-box generation is deterministic"""
|
|
131
|
+
key = secrets.token_bytes(32)
|
|
132
|
+
|
|
133
|
+
sbox1 = generate_lwe_sbox(key, 0, 0)
|
|
134
|
+
sbox2 = generate_lwe_sbox(key, 0, 0)
|
|
135
|
+
|
|
136
|
+
assert sbox1 == sbox2
|
|
137
|
+
|
|
138
|
+
def test_different_keys_produce_different_sboxes(self):
|
|
139
|
+
"""Test different keys produce different S-boxes"""
|
|
140
|
+
key1 = secrets.token_bytes(32)
|
|
141
|
+
key2 = secrets.token_bytes(32)
|
|
142
|
+
|
|
143
|
+
sbox1 = generate_lwe_sbox(key1, 0, 0)
|
|
144
|
+
sbox2 = generate_lwe_sbox(key2, 0, 0)
|
|
145
|
+
|
|
146
|
+
assert sbox1 != sbox2
|
|
147
|
+
|
|
148
|
+
def test_different_counters_produce_different_sboxes(self):
|
|
149
|
+
"""Test different counters regenerate S-boxes"""
|
|
150
|
+
key = secrets.token_bytes(32)
|
|
151
|
+
|
|
152
|
+
sbox1 = generate_lwe_sbox(key, 0, 0)
|
|
153
|
+
sbox2 = generate_lwe_sbox(key, 0, 1)
|
|
154
|
+
|
|
155
|
+
assert sbox1 != sbox2
|
|
156
|
+
|
|
157
|
+
def test_ensure_bijection_fixes_duplicates(self):
|
|
158
|
+
"""Test bijection enforcement"""
|
|
159
|
+
# Create list with duplicates
|
|
160
|
+
sbox = list(range(256))
|
|
161
|
+
sbox[5] = sbox[10] # Create duplicate
|
|
162
|
+
|
|
163
|
+
fixed = ensure_bijection(sbox)
|
|
164
|
+
|
|
165
|
+
assert len(set(fixed)) == 256
|
|
166
|
+
assert all(0 <= v < 256 for v in fixed)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class TestNTRUPermutation:
|
|
170
|
+
"""Test NTRU permutation generation"""
|
|
171
|
+
|
|
172
|
+
def test_permutation_correct_size(self):
|
|
173
|
+
"""Test permutation has correct size"""
|
|
174
|
+
key = secrets.token_bytes(32)
|
|
175
|
+
perm = generate_ntru_permutation(key, 0)
|
|
176
|
+
|
|
177
|
+
assert len(perm) == NTRU_N
|
|
178
|
+
|
|
179
|
+
def test_permutation_is_valid(self):
|
|
180
|
+
"""Test permutation contains all indices"""
|
|
181
|
+
key = secrets.token_bytes(32)
|
|
182
|
+
perm = generate_ntru_permutation(key, 0)
|
|
183
|
+
|
|
184
|
+
assert set(perm) == set(range(NTRU_N))
|
|
185
|
+
|
|
186
|
+
def test_permutation_deterministic(self):
|
|
187
|
+
"""Test permutation generation is deterministic"""
|
|
188
|
+
key = secrets.token_bytes(32)
|
|
189
|
+
|
|
190
|
+
perm1 = generate_ntru_permutation(key, 0)
|
|
191
|
+
perm2 = generate_ntru_permutation(key, 0)
|
|
192
|
+
|
|
193
|
+
assert perm1 == perm2
|
|
194
|
+
|
|
195
|
+
def test_different_rounds_produce_different_permutations(self):
|
|
196
|
+
"""Test different rounds produce different permutations"""
|
|
197
|
+
key = secrets.token_bytes(32)
|
|
198
|
+
|
|
199
|
+
perm1 = generate_ntru_permutation(key, 0)
|
|
200
|
+
perm2 = generate_ntru_permutation(key, 1)
|
|
201
|
+
|
|
202
|
+
assert perm1 != perm2
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class TestHKDF:
|
|
206
|
+
"""Test HKDF key derivation"""
|
|
207
|
+
|
|
208
|
+
def test_hkdf_correct_length(self):
|
|
209
|
+
"""Test HKDF produces correct length"""
|
|
210
|
+
key = secrets.token_bytes(32)
|
|
211
|
+
derived = hkdf_sha256(key, 64)
|
|
212
|
+
|
|
213
|
+
assert len(derived) == 64
|
|
214
|
+
|
|
215
|
+
def test_hkdf_deterministic(self):
|
|
216
|
+
"""Test HKDF is deterministic"""
|
|
217
|
+
key = secrets.token_bytes(32)
|
|
218
|
+
|
|
219
|
+
derived1 = hkdf_sha256(key, 64)
|
|
220
|
+
derived2 = hkdf_sha256(key, 64)
|
|
221
|
+
|
|
222
|
+
assert derived1 == derived2
|
|
223
|
+
|
|
224
|
+
def test_hkdf_different_info_produces_different_output(self):
|
|
225
|
+
"""Test different info strings produce different outputs"""
|
|
226
|
+
key = secrets.token_bytes(32)
|
|
227
|
+
|
|
228
|
+
derived1 = hkdf_sha256(key, 64, b"info1")
|
|
229
|
+
derived2 = hkdf_sha256(key, 64, b"info2")
|
|
230
|
+
|
|
231
|
+
assert derived1 != derived2
|
|
232
|
+
|
|
233
|
+
def test_hkdf_different_keys_produce_different_output(self):
|
|
234
|
+
"""Test different keys produce different outputs"""
|
|
235
|
+
key1 = b'A' * 32
|
|
236
|
+
key2 = b'B' * 32
|
|
237
|
+
|
|
238
|
+
derived1 = hkdf_sha256(key1, 64)
|
|
239
|
+
derived2 = hkdf_sha256(key2, 64)
|
|
240
|
+
|
|
241
|
+
assert derived1 != derived2
|
tests/test_signature.py
ADDED
|
File without changes
|