change test_cldivrem to check all input values for each width
[nmigen-gf.git] / gf_reference / test_cl_gfb_gfp.py
1 from nmigen_gf.reference.state import ST
2 from nmigen_gf.reference.cldivrem import cldivrem
3 from nmigen_gf.reference.clmul import clmul
4 from nmigen_gf.reference.gfbmul import gfbmul
5 from nmigen_gf.reference.gfbmadd import gfbmadd
6 from nmigen_gf.reference.gfbinv import gfbinv
7 from nmigen_gf.reference.gfpadd import gfpadd
8 from nmigen_gf.reference.gfpsub import gfpsub
9 from nmigen_gf.reference.gfpmul import gfpmul
10 from nmigen_gf.reference.gfpinv import gfpinv
11 from nmigen_gf.reference.gfpmadd import gfpmadd
12 from nmigen_gf.reference.gfpmsub import gfpmsub
13 from nmigen_gf.reference.gfpmsubr import gfpmsubr
14 from nmigen_gf.reference.pack_poly import pack_poly, unpack_poly
15 import unittest
16
17
18 class GF2Poly:
19 """Polynomial with GF(2) coefficients.
20
21 `self.coefficients`: a list where `coefficients[-1] != 0`.
22 `coefficients[i]` is the coefficient for `x ** i`.
23 """
24
25 def __init__(self, coefficients=None):
26 self.coefficients = []
27 if coefficients is not None:
28 if not isinstance(coefficients, (tuple, list)):
29 coefficients = list(coefficients)
30 # reversed to resize self.coefficients once
31 for i in reversed(range(len(coefficients))):
32 self[i] = coefficients[i]
33
34 def __len__(self):
35 return len(self.coefficients)
36
37 @property
38 def degree(self):
39 return len(self) - 1
40
41 @property
42 def lc(self):
43 """leading coefficient."""
44 return 0 if len(self) == 0 else self.coefficients[-1]
45
46 def __getitem__(self, key):
47 assert key >= 0
48 if key < len(self):
49 return self.coefficients[key]
50 return 0
51
52 def __setitem__(self, key, value):
53 assert key >= 0
54 assert value == 0 or value == 1
55 if key < len(self):
56 self.coefficients[key] = value
57 while len(self) and self.coefficients[-1] == 0:
58 self.coefficients.pop()
59 elif value != 0:
60 self.coefficients += [0] * (key + 1 - len(self))
61 self.coefficients[key] = value
62
63 def __repr__(self):
64 return f"GF2Poly({self.coefficients})"
65
66 def __iadd__(self, rhs):
67 for i in range(max(len(self), len(rhs))):
68 self[i] ^= rhs[i]
69 return self
70
71 def __add__(self, rhs):
72 return GF2Poly(self).__iadd__(rhs)
73
74 def __isub__(self, rhs):
75 return self.__iadd__(rhs)
76
77 def __sub__(self, rhs):
78 return self.__add__(rhs)
79
80 def __iter__(self):
81 return iter(self.coefficients)
82
83 def __mul__(self, rhs):
84 retval = GF2Poly()
85 # reversed to resize retval.coefficients once
86 for i in reversed(range(len(self))):
87 if self[i]:
88 for j in reversed(range(len(rhs))):
89 retval[i + j] ^= rhs[j]
90 return retval
91
92 def __ilshift__(self, amount):
93 """multiplies `self` by the polynomial `x**amount`"""
94 if len(self) != 0:
95 self.coefficients[:0] = [0] * amount
96 return self
97
98 def __lshift__(self, amount):
99 """returns the polynomial `self * x**amount`"""
100 return GF2Poly(self).__ilshift__(amount)
101
102 def __irshift__(self, amount):
103 """divides `self` by the polynomial `x**amount`, discarding the
104 remainder.
105 """
106 if amount < len(self):
107 del self.coefficients[:amount]
108 else:
109 del self.coefficients[:]
110 return self
111
112 def __rshift__(self, amount):
113 """divides `self` by the polynomial `x**amount`, discarding the
114 remainder.
115 """
116 return GF2Poly(self).__irshift__(amount)
117
118 def __divmod__(self, divisor):
119 # based on https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division
120 assert isinstance(divisor, GF2Poly)
121 if len(divisor) == 0:
122 raise ZeroDivisionError
123 q = GF2Poly()
124 r = GF2Poly(self)
125 while r.degree >= divisor.degree:
126 shift = r.degree - divisor.degree
127 q[shift] ^= 1
128 r -= divisor << shift
129 return q, r
130
131 def __floordiv__(self, divisor):
132 q, r = divmod(self, divisor)
133 return q
134
135 def __mod__(self, divisor):
136 q, r = divmod(self, divisor)
137 return r
138
139 def __pow__(self, exponent, modulus=None):
140 assert isinstance(exponent, int) and exponent >= 0
141 assert modulus is None or isinstance(modulus, GF2Poly)
142 retval = GF2Poly([1])
143 pow2 = GF2Poly(self)
144 while exponent != 0:
145 if exponent & 1:
146 retval *= pow2
147 if modulus is not None:
148 retval %= modulus
149 exponent &= ~1
150 else:
151 pow2 *= pow2
152 if modulus is not None:
153 pow2 %= modulus
154 exponent >>= 1
155 return retval
156
157 def __eq__(self, rhs):
158 if isinstance(rhs, GF2Poly):
159 return self.coefficients == rhs.coefficients
160 return NotImplemented
161
162
163 class TestGF2Poly(unittest.TestCase):
164 def test_add(self):
165 a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
166 b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0])
167 c = a + b
168 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
169 self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
170 self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]))
171 c = b + a
172 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
173 self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
174 self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]))
175 a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
176 b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1])
177 c = a + b
178 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
179 self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1]))
180 self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
181 c = a - b
182 self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
183 c = b - a
184 self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
185
186 def test_shift(self):
187 a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
188 c = a << 0
189 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
190 self.assertEqual(c, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
191 c = a << 5
192 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
193 self.assertEqual(c, GF2Poly(
194 [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
195 c = a << 10
196 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
197 self.assertEqual(c, GF2Poly(
198 [0] * 10 + [1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
199 c = a >> 0
200 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
201 self.assertEqual(c, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
202 c = a >> 5
203 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
204 self.assertEqual(c, GF2Poly([1, 1, 0, 1, 0, 1]))
205 c = a >> 10
206 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
207 self.assertEqual(c, GF2Poly([1]))
208 c = a >> 11
209 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
210 self.assertEqual(c, GF2Poly([]))
211 c = a >> 100
212 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
213 self.assertEqual(c, GF2Poly([]))
214
215 def test_mul(self):
216 a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
217 b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1])
218 c = a * b
219 expected = GF2Poly([0, 0, 1, 0, 1, 0, 1, 1, 1, 0,
220 0, 1, 0, 1, 1, 0, 0, 1, 1])
221 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
222 self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
223 self.assertEqual(c, expected)
224
225 def test_divmod(self):
226 a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
227 b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1])
228 q, r = divmod(a, b)
229 self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
230 self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
231 self.assertEqual(q, GF2Poly([1, 1, 1]))
232 self.assertEqual(r, GF2Poly([1, 0, 1, 0, 0, 1, 0, 1]))
233 q = a // b
234 self.assertEqual(q, GF2Poly([1, 1, 1]))
235 r = a % b
236 self.assertEqual(r, GF2Poly([1, 0, 1, 0, 0, 1, 0, 1]))
237
238 def test_pow(self):
239 b = GF2Poly([0, 1])
240 for e in range(8):
241 expected = GF2Poly([0] * e + [1])
242 with self.subTest(b=str(b), e=e, expected=str(expected)):
243 v = b ** e
244 self.assertEqual(b, GF2Poly([0, 1]))
245 self.assertEqual(v, expected)
246
247 # AES's finite field reducing polynomial
248 m = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
249 period = 2 ** m.degree - 1
250 b = GF2Poly([1, 1, 0, 0, 1, 0, 1])
251 e = period - 1
252 expected = GF2Poly([0, 1, 0, 1, 0, 0, 1, 1])
253 v = pow(b, e, m)
254 self.assertEqual(m, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
255 self.assertEqual(b, GF2Poly([1, 1, 0, 0, 1, 0, 1]))
256 self.assertEqual(v, expected)
257
258 # test that pow doesn't take inordinately long when given a modulus.
259 # adding a multiple of `period` should leave results unchanged.
260 e += period * 10 ** 15
261 v = pow(b, e, m)
262 self.assertEqual(m, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
263 self.assertEqual(b, GF2Poly([1, 1, 0, 0, 1, 0, 1]))
264 self.assertEqual(v, expected)
265
266
267 class GFB:
268 def __init__(self, value, red_poly=None):
269 if isinstance(value, GFB):
270 # copy value
271 assert red_poly is None
272 self.red_poly = GF2Poly(value.red_poly)
273 self.value = GF2Poly(value.value)
274 return
275 assert isinstance(value, GF2Poly)
276 assert isinstance(red_poly, GF2Poly)
277 assert red_poly.degree > 0
278 self.value = value % red_poly
279 self.red_poly = red_poly
280
281 def __repr__(self):
282 return f"GFB({self.value}, {self.red_poly})"
283
284 def __add__(self, rhs):
285 assert isinstance(rhs, GFB)
286 assert self.red_poly == rhs.red_poly
287 return GFB((self.value + rhs.value) % self.red_poly, self.red_poly)
288
289 def __sub__(self, rhs):
290 return self.__add__(rhs)
291
292 def __eq__(self, rhs):
293 if isinstance(rhs, GFB):
294 return self.value == rhs.value and self.red_poly == rhs.red_poly
295 return NotImplemented
296
297 def __mul__(self, rhs):
298 assert isinstance(rhs, GFB)
299 assert self.red_poly == rhs.red_poly
300 return GFB((self.value * rhs.value) % self.red_poly, self.red_poly)
301
302 def __div__(self, rhs):
303 assert isinstance(rhs, GFB)
304 assert self.red_poly == rhs.red_poly
305 return self * rhs ** -1
306
307 @property
308 def __pow_period(self):
309 period = (1 << self.red_poly.degree) - 1
310 assert period > 0, "internal logic error"
311 return period
312
313 def __pow__(self, exponent):
314 assert isinstance(exponent, int)
315 if len(self.value) == 0:
316 if exponent < 0:
317 raise ZeroDivisionError
318 else:
319 return GFB(self)
320 exponent %= self.__pow_period
321 return GFB(pow(self.value, exponent, self.red_poly), self.red_poly)
322
323
324 class TestGFBClass(unittest.TestCase):
325 def test_add(self):
326 # AES's finite field reducing polynomial
327 red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
328 a = GFB(GF2Poly([0, 1, 0, 1]), red_poly)
329 b = GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly)
330 expected = GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly)
331 c = a + b
332 self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
333 self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1]), red_poly))
334 self.assertEqual(b, GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly))
335 self.assertEqual(c, expected)
336 c = a - b
337 self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
338 self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1]), red_poly))
339 self.assertEqual(b, GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly))
340 self.assertEqual(c, expected)
341 c = b - a
342 self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
343 self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1]), red_poly))
344 self.assertEqual(b, GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly))
345 self.assertEqual(c, expected)
346
347 def test_mul(self):
348 # AES's finite field reducing polynomial
349 red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
350 a = GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly)
351 b = GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly)
352 expected = GFB(GF2Poly([1]), red_poly)
353 c = a * b
354 self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
355 self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly))
356 self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
357 self.assertEqual(c, expected)
358
359 def test_pow(self):
360 # AES's finite field reducing polynomial
361 red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
362 period = 2 ** red_poly.degree - 1
363 b = GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly)
364 e = period - 1
365 expected = GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly)
366 v = b ** e
367 self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
368 self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
369 self.assertEqual(v, expected)
370 e = -1
371 v = b ** e
372 self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
373 self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
374 self.assertEqual(v, expected)
375
376 # test that pow doesn't take inordinately long when given a modulus.
377 # adding a multiple of `period` should leave results unchanged.
378 e += period * 10 ** 15
379 v = b ** e
380 self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
381 self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
382 self.assertEqual(v, expected)
383
384
385 class GFP:
386 def __init__(self, value, size):
387 assert isinstance(value, int)
388 assert isinstance(size, int) and size >= 2, "size is not a prime"
389 self.value = value % size
390 self.size = size
391
392 def __repr__(self):
393 return f"GFP({self.value}, {self.size})"
394
395 def __eq__(self, rhs):
396 if isinstance(rhs, GFP):
397 return self.value == rhs.value and self.size == rhs.size
398 return NotImplemented
399
400 def __add__(self, rhs):
401 assert isinstance(rhs, GFP)
402 assert self.size == rhs.size
403 return GFP((self.value + rhs.value) % self.size, self.size)
404
405 def __sub__(self, rhs):
406 assert isinstance(rhs, GFP)
407 assert self.size == rhs.size
408 return GFP((self.value - rhs.value) % self.size, self.size)
409
410 def __mul__(self, rhs):
411 assert isinstance(rhs, GFP)
412 assert self.size == rhs.size
413 return GFP((self.value * rhs.value) % self.size, self.size)
414
415 def __div__(self, rhs):
416 assert isinstance(rhs, GFP)
417 assert self.size == rhs.size
418 return self * rhs ** -1
419
420 @property
421 def __pow_period(self):
422 period = self.size - 1
423 assert period > 0, "internal logic error"
424 return period
425
426 def __pow__(self, exponent):
427 assert isinstance(exponent, int)
428 if self.value == 0:
429 if exponent < 0:
430 raise ZeroDivisionError
431 else:
432 return GFP(self.value, self.size)
433 exponent %= self.__pow_period
434 return GFP(pow(self.value, exponent, self.size), self.size)
435
436
437 PRIMES = 2, 3, 5, 7, 11, 13, 17, 19
438 """handy list of small primes for testing"""
439
440
441 class TestGFPClass(unittest.TestCase):
442 def test_add_sub(self):
443 for prime in PRIMES:
444 for av in range(prime):
445 for bv in range(prime):
446 with self.subTest(av=av, bv=bv, prime=prime):
447 a = GFP(av, prime)
448 b = GFP(bv, prime)
449 expected = GFP((av + bv) % prime, prime)
450 c = a + b
451 self.assertEqual(a, GFP(av, prime))
452 self.assertEqual(b, GFP(bv, prime))
453 self.assertEqual(c, expected)
454 c = b + a
455 self.assertEqual(a, GFP(av, prime))
456 self.assertEqual(b, GFP(bv, prime))
457 self.assertEqual(c, expected)
458 expected = GFP((av - bv) % prime, prime)
459 c = a - b
460 self.assertEqual(a, GFP(av, prime))
461 self.assertEqual(b, GFP(bv, prime))
462 self.assertEqual(c, expected)
463 expected = GFP((bv - av) % prime, prime)
464 c = b - a
465 self.assertEqual(a, GFP(av, prime))
466 self.assertEqual(b, GFP(bv, prime))
467 self.assertEqual(c, expected)
468
469 def test_mul(self):
470 for prime in PRIMES:
471 for av in range(prime):
472 for bv in range(prime):
473 with self.subTest(av=av, bv=bv, prime=prime):
474 a = GFP(av, prime)
475 b = GFP(bv, prime)
476 expected = GFP((av * bv) % prime, prime)
477 c = a * b
478 self.assertEqual(a, GFP(av, prime))
479 self.assertEqual(b, GFP(bv, prime))
480 self.assertEqual(c, expected)
481 c = b * a
482 self.assertEqual(a, GFP(av, prime))
483 self.assertEqual(b, GFP(bv, prime))
484 self.assertEqual(c, expected)
485
486 def test_pow(self):
487 for prime in PRIMES:
488 for bv in range(prime):
489 with self.subTest(bv=bv, prime=prime):
490 b = GFP(bv, prime)
491 period = prime - 1
492 e = period - 1
493 expected = GFP(pow(bv, e, prime) if bv != 0 else 0, prime)
494 v = b ** e
495 self.assertEqual(b, GFP(bv, prime))
496 self.assertEqual(v, expected)
497 e = -1
498 if bv != 0:
499 v = b ** e
500 self.assertEqual(b, GFP(bv, prime))
501 self.assertEqual(v, expected)
502
503 # test that pow doesn't take inordinately long when given
504 # a modulus. adding a multiple of `period` should leave
505 # results unchanged.
506 e += period * 10 ** 15
507 v = b ** e
508 self.assertEqual(b, GFP(bv, prime))
509 self.assertEqual(v, expected)
510
511
512 class TestCL(unittest.TestCase):
513 def test_cldivrem(self):
514 width = 6
515 for nv in range(2 ** width):
516 n = GF2Poly(unpack_poly(nv))
517 for dv in range(1, 2 ** width):
518 d = GF2Poly(unpack_poly(dv))
519 with self.subTest(n=str(n), nv=nv, d=str(d), dv=dv):
520 q_expected, r_expected = divmod(n, d)
521 self.assertEqual(q_expected * d + r_expected, n)
522 q, r = cldivrem(nv, dv, width)
523 q_expected = pack_poly(q_expected.coefficients)
524 r_expected = pack_poly(r_expected.coefficients)
525 self.assertEqual((q, r), (q_expected, r_expected))
526
527 def test_clmul(self):
528 a_width = 5
529 b_width = 5
530 for av in range(2 ** a_width):
531 a = GF2Poly(unpack_poly(av))
532 for bv in range(2 ** b_width):
533 b = GF2Poly(unpack_poly(bv))
534 with self.subTest(a=str(a), av=av, b=str(b), bv=bv):
535 expected = a * b
536 product = clmul(av, bv)
537 expected = pack_poly(expected.coefficients)
538 self.assertEqual(product, expected)
539
540
541 class TestGFBInstructions(unittest.TestCase):
542 @staticmethod
543 def init_aes_red_poly():
544 # AES's finite field reducing polynomial
545 red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
546 ST.reinit(GFBREDPOLY=pack_poly(red_poly.coefficients))
547 return red_poly
548
549 def test_gfbmul(self):
550 # AES's finite field reducing polynomial
551 red_poly = self.init_aes_red_poly()
552 a_width = 8
553 b_width = 4
554 for av in range(2 ** a_width):
555 a = GFB(GF2Poly(unpack_poly(av)), red_poly)
556 for bv in range(2 ** b_width):
557 b = GFB(GF2Poly(unpack_poly(bv)), red_poly)
558 expected = a * b
559 with self.subTest(a=str(a), av=av, b=str(b), bv=bv, expected=str(expected)):
560 product = gfbmul(av, bv)
561 expectedv = pack_poly(expected.value.coefficients)
562 self.assertEqual(product, expectedv)
563
564 def test_gfbmadd(self):
565 # AES's finite field reducing polynomial
566 red_poly = self.init_aes_red_poly()
567 a_width = 5
568 b_width = 4
569 c_width = 4
570 for av in range(2 ** a_width):
571 a = GFB(GF2Poly(unpack_poly(av)), red_poly)
572 for bv in range(2 ** b_width):
573 b = GFB(GF2Poly(unpack_poly(bv)), red_poly)
574 for cv in range(2 ** c_width):
575 c = GFB(GF2Poly(unpack_poly(cv)), red_poly)
576 expected = a * b + c
577 with self.subTest(a=str(a), av=av,
578 b=str(b), bv=bv,
579 c=str(c), cv=cv,
580 expected=str(expected)):
581 result = gfbmadd(av, bv, cv)
582 expectedv = pack_poly(expected.value.coefficients)
583 self.assertEqual(result, expectedv)
584
585 def test_gfbinv(self):
586 # AES's finite field reducing polynomial
587 red_poly = self.init_aes_red_poly()
588 width = 8
589 for av in range(2 ** width):
590 a = GFB(GF2Poly(unpack_poly(av)), red_poly)
591 expected = a ** -1 if av != 0 else GFB(GF2Poly(), red_poly)
592 with self.subTest(a=str(a), av=av, expected=str(expected)):
593 result = gfbinv(av)
594 expectedv = pack_poly(expected.value.coefficients)
595 self.assertEqual(result, expectedv)
596
597
598 class TestGFPInstructions(unittest.TestCase):
599 def test_gfpadd(self):
600 for prime in PRIMES:
601 for av in range(prime):
602 for bv in range(prime):
603 a = GFP(av, prime)
604 b = GFP(bv, prime)
605 expected = a + b
606 with self.subTest(a=str(a), b=str(b),
607 expected=str(expected)):
608 ST.reinit(GFPRIME=prime)
609 v = gfpadd(av, bv)
610 self.assertEqual(v, expected.value)
611
612 def test_gfpsub(self):
613 for prime in PRIMES:
614 for av in range(prime):
615 for bv in range(prime):
616 a = GFP(av, prime)
617 b = GFP(bv, prime)
618 expected = a - b
619 with self.subTest(a=str(a), b=str(b),
620 expected=str(expected)):
621 ST.reinit(GFPRIME=prime)
622 v = gfpsub(av, bv)
623 self.assertEqual(v, expected.value)
624
625 def test_gfpmul(self):
626 for prime in PRIMES:
627 for av in range(prime):
628 for bv in range(prime):
629 a = GFP(av, prime)
630 b = GFP(bv, prime)
631 expected = a * b
632 with self.subTest(a=str(a), b=str(b),
633 expected=str(expected)):
634 ST.reinit(GFPRIME=prime)
635 v = gfpmul(av, bv)
636 self.assertEqual(v, expected.value)
637
638 def test_gfpinv(self):
639 for prime in PRIMES:
640 for av in range(prime):
641 a = GFP(av, prime)
642 if av == 0:
643 # TODO: determine what's expected for division by zero
644 continue
645 else:
646 expected = a ** -1
647 with self.subTest(a=str(a), expected=str(expected)):
648 ST.reinit(GFPRIME=prime)
649 v = gfpinv(av)
650 self.assertEqual(v, expected.value)
651
652 def test_gfpmadd(self):
653 for prime in PRIMES:
654 for av in range(prime):
655 for bv in range(prime):
656 for cv in range(prime):
657 a = GFP(av, prime)
658 b = GFP(bv, prime)
659 c = GFP(cv, prime)
660 expected = a * b + c
661 with self.subTest(a=str(a), b=str(b), c=str(c),
662 expected=str(expected)):
663 ST.reinit(GFPRIME=prime)
664 v = gfpmadd(av, bv, cv)
665 self.assertEqual(v, expected.value)
666
667 def test_gfpmsub(self):
668 for prime in PRIMES:
669 for av in range(prime):
670 for bv in range(prime):
671 for cv in range(prime):
672 a = GFP(av, prime)
673 b = GFP(bv, prime)
674 c = GFP(cv, prime)
675 expected = a * b - c
676 with self.subTest(a=str(a), b=str(b), c=str(c),
677 expected=str(expected)):
678 ST.reinit(GFPRIME=prime)
679 v = gfpmsub(av, bv, cv)
680 self.assertEqual(v, expected.value)
681
682 def test_gfpmsubr(self):
683 for prime in PRIMES:
684 for av in range(prime):
685 for bv in range(prime):
686 for cv in range(prime):
687 a = GFP(av, prime)
688 b = GFP(bv, prime)
689 c = GFP(cv, prime)
690 expected = c - a * b
691 with self.subTest(a=str(a), b=str(b), c=str(c),
692 expected=str(expected)):
693 ST.reinit(GFPRIME=prime)
694 v = gfpmsubr(av, bv, cv)
695 self.assertEqual(v, expected.value)
696
697
698 if __name__ == "__main__":
699 unittest.main()