--- /dev/null
--- /dev/null
++from .state import ST
++from .cldivrem import cldivrem
++from .clmul import clmul
++from .gfbmul import gfbmul
++from .gfbmadd import gfbmadd
++from .gfbinv import gfbinv
++from .gfpadd import gfpadd
++from .gfpsub import gfpsub
++from .gfpmul import gfpmul
++from .gfpinv import gfpinv
++from .gfpmadd import gfpmadd
++from .gfpmsub import gfpmsub
++from .gfpmsubr import gfpmsubr
++from .pack_poly import pack_poly, unpack_poly
++import unittest
++
++
++class GF2Poly:
++ """Polynomial with GF(2) coefficients.
++
++ `self.coefficients`: a list where `coefficients[-1] != 0`.
++ `coefficients[i]` is the coefficient for `x ** i`.
++ """
++
++ def __init__(self, coefficients=None):
++ self.coefficients = []
++ if coefficients is not None:
++ if not isinstance(coefficients, (tuple, list)):
++ coefficients = list(coefficients)
++ # reversed to resize self.coefficients once
++ for i in reversed(range(len(coefficients))):
++ self[i] = coefficients[i]
++
++ def __len__(self):
++ return len(self.coefficients)
++
++ @property
++ def degree(self):
++ return len(self) - 1
++
++ @property
++ def lc(self):
++ """leading coefficient."""
++ return 0 if len(self) == 0 else self.coefficients[-1]
++
++ def __getitem__(self, key):
++ assert key >= 0
++ if key < len(self):
++ return self.coefficients[key]
++ return 0
++
++ def __setitem__(self, key, value):
++ assert key >= 0
++ assert value == 0 or value == 1
++ if key < len(self):
++ self.coefficients[key] = value
++ while len(self) and self.coefficients[-1] == 0:
++ self.coefficients.pop()
++ elif value != 0:
++ self.coefficients += [0] * (key + 1 - len(self))
++ self.coefficients[key] = value
++
++ def __repr__(self):
++ return f"GF2Poly({self.coefficients})"
++
++ def __iadd__(self, rhs):
++ for i in range(max(len(self), len(rhs))):
++ self[i] ^= rhs[i]
++ return self
++
++ def __add__(self, rhs):
++ return GF2Poly(self).__iadd__(rhs)
++
++ def __isub__(self, rhs):
++ return self.__iadd__(rhs)
++
++ def __sub__(self, rhs):
++ return self.__add__(rhs)
++
++ def __iter__(self):
++ return iter(self.coefficients)
++
++ def __mul__(self, rhs):
++ retval = GF2Poly()
++ # reversed to resize retval.coefficients once
++ for i in reversed(range(len(self))):
++ if self[i]:
++ for j in reversed(range(len(rhs))):
++ retval[i + j] ^= rhs[j]
++ return retval
++
++ def __ilshift__(self, amount):
++ """multiplies `self` by the polynomial `x**amount`"""
++ if len(self) != 0:
++ self.coefficients[:0] = [0] * amount
++ return self
++
++ def __lshift__(self, amount):
++ """returns the polynomial `self * x**amount`"""
++ return GF2Poly(self).__ilshift__(amount)
++
++ def __irshift__(self, amount):
++ """divides `self` by the polynomial `x**amount`, discarding the
++ remainder.
++ """
++ if amount < len(self):
++ del self.coefficients[:amount]
++ else:
++ del self.coefficients[:]
++ return self
++
++ def __rshift__(self, amount):
++ """divides `self` by the polynomial `x**amount`, discarding the
++ remainder.
++ """
++ return GF2Poly(self).__irshift__(amount)
++
++ def __divmod__(self, divisor):
++ # based on https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division
++ assert isinstance(divisor, GF2Poly)
++ if len(divisor) == 0:
++ raise ZeroDivisionError
++ q = GF2Poly()
++ r = GF2Poly(self)
++ while r.degree >= divisor.degree:
++ shift = r.degree - divisor.degree
++ q[shift] ^= 1
++ r -= divisor << shift
++ return q, r
++
++ def __floordiv__(self, divisor):
++ q, r = divmod(self, divisor)
++ return q
++
++ def __mod__(self, divisor):
++ q, r = divmod(self, divisor)
++ return r
++
++ def __pow__(self, exponent, modulus=None):
++ assert isinstance(exponent, int) and exponent >= 0
++ assert modulus is None or isinstance(modulus, GF2Poly)
++ retval = GF2Poly([1])
++ pow2 = GF2Poly(self)
++ while exponent != 0:
++ if exponent & 1:
++ retval *= pow2
++ if modulus is not None:
++ retval %= modulus
++ exponent &= ~1
++ else:
++ pow2 *= pow2
++ if modulus is not None:
++ pow2 %= modulus
++ exponent >>= 1
++ return retval
++
++ def __eq__(self, rhs):
++ if isinstance(rhs, GF2Poly):
++ return self.coefficients == rhs.coefficients
++ return NotImplemented
++
++
++class TestGF2Poly(unittest.TestCase):
++ def test_add(self):
++ a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
++ b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0])
++ c = a + b
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
++ self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]))
++ c = b + a
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
++ self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]))
++ a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
++ b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1])
++ c = a + b
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
++ c = a - b
++ self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
++ c = b - a
++ self.assertEqual(c, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
++
++ def test_shift(self):
++ a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
++ c = a << 0
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ c = a << 5
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly(
++ [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ c = a << 10
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly(
++ [0] * 10 + [1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ c = a >> 0
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ c = a >> 5
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly([1, 1, 0, 1, 0, 1]))
++ c = a >> 10
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly([1]))
++ c = a >> 11
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly([]))
++ c = a >> 100
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(c, GF2Poly([]))
++
++ def test_mul(self):
++ a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
++ b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1])
++ c = a * b
++ expected = GF2Poly([0, 0, 1, 0, 1, 0, 1, 1, 1, 0,
++ 0, 1, 0, 1, 1, 0, 0, 1, 1])
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
++ self.assertEqual(c, expected)
++
++ def test_divmod(self):
++ a = GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
++ b = GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1])
++ q, r = divmod(a, b)
++ self.assertEqual(a, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
++ self.assertEqual(b, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
++ self.assertEqual(q, GF2Poly([1, 1, 1]))
++ self.assertEqual(r, GF2Poly([1, 0, 1, 0, 0, 1, 0, 1]))
++ q = a // b
++ self.assertEqual(q, GF2Poly([1, 1, 1]))
++ r = a % b
++ self.assertEqual(r, GF2Poly([1, 0, 1, 0, 0, 1, 0, 1]))
++
++ def test_pow(self):
++ b = GF2Poly([0, 1])
++ for e in range(8):
++ expected = GF2Poly([0] * e + [1])
++ with self.subTest(b=str(b), e=e, expected=str(expected)):
++ v = b ** e
++ self.assertEqual(b, GF2Poly([0, 1]))
++ self.assertEqual(v, expected)
++
++ # AES's finite field reducing polynomial
++ m = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
++ period = 2 ** m.degree - 1
++ b = GF2Poly([1, 1, 0, 0, 1, 0, 1])
++ e = period - 1
++ expected = GF2Poly([0, 1, 0, 1, 0, 0, 1, 1])
++ v = pow(b, e, m)
++ self.assertEqual(m, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(b, GF2Poly([1, 1, 0, 0, 1, 0, 1]))
++ self.assertEqual(v, expected)
++
++ # test that pow doesn't take inordinately long when given a modulus.
++ # adding a multiple of `period` should leave results unchanged.
++ e += period * 10 ** 15
++ v = pow(b, e, m)
++ self.assertEqual(m, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(b, GF2Poly([1, 1, 0, 0, 1, 0, 1]))
++ self.assertEqual(v, expected)
++
++
++class GFB:
++ def __init__(self, value, red_poly=None):
++ if isinstance(value, GFB):
++ # copy value
++ assert red_poly is None
++ self.red_poly = GF2Poly(value.red_poly)
++ self.value = GF2Poly(value.value)
++ return
++ assert isinstance(value, GF2Poly)
++ assert isinstance(red_poly, GF2Poly)
++ assert red_poly.degree > 0
++ self.value = value % red_poly
++ self.red_poly = red_poly
++
++ def __repr__(self):
++ return f"GFB({self.value}, {self.red_poly})"
++
++ def __add__(self, rhs):
++ assert isinstance(rhs, GFB)
++ assert self.red_poly == rhs.red_poly
++ return GFB((self.value + rhs.value) % self.red_poly, self.red_poly)
++
++ def __sub__(self, rhs):
++ return self.__add__(rhs)
++
++ def __eq__(self, rhs):
++ if isinstance(rhs, GFB):
++ return self.value == rhs.value and self.red_poly == rhs.red_poly
++ return NotImplemented
++
++ def __mul__(self, rhs):
++ assert isinstance(rhs, GFB)
++ assert self.red_poly == rhs.red_poly
++ return GFB((self.value * rhs.value) % self.red_poly, self.red_poly)
++
++ def __div__(self, rhs):
++ assert isinstance(rhs, GFB)
++ assert self.red_poly == rhs.red_poly
++ return self * rhs ** -1
++
++ @property
++ def __pow_period(self):
++ period = (1 << self.red_poly.degree) - 1
++ assert period > 0, "internal logic error"
++ return period
++
++ def __pow__(self, exponent):
++ assert isinstance(exponent, int)
++ if len(self.value) == 0:
++ if exponent < 0:
++ raise ZeroDivisionError
++ else:
++ return GFB(self)
++ exponent %= self.__pow_period
++ return GFB(pow(self.value, exponent, self.red_poly), self.red_poly)
++
++
++class TestGFBClass(unittest.TestCase):
++ def test_add(self):
++ # AES's finite field reducing polynomial
++ red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
++ a = GFB(GF2Poly([0, 1, 0, 1]), red_poly)
++ b = GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly)
++ expected = GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly)
++ c = a + b
++ self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1]), red_poly))
++ self.assertEqual(b, GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly))
++ self.assertEqual(c, expected)
++ c = a - b
++ self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1]), red_poly))
++ self.assertEqual(b, GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly))
++ self.assertEqual(c, expected)
++ c = b - a
++ self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1]), red_poly))
++ self.assertEqual(b, GFB(GF2Poly([0, 0, 0, 0, 0, 0, 1, 1]), red_poly))
++ self.assertEqual(c, expected)
++
++ def test_mul(self):
++ # AES's finite field reducing polynomial
++ red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
++ a = GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly)
++ b = GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly)
++ expected = GFB(GF2Poly([1]), red_poly)
++ c = a * b
++ self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(a, GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly))
++ self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
++ self.assertEqual(c, expected)
++
++ def test_pow(self):
++ # AES's finite field reducing polynomial
++ red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
++ period = 2 ** red_poly.degree - 1
++ b = GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly)
++ e = period - 1
++ expected = GFB(GF2Poly([0, 1, 0, 1, 0, 0, 1, 1]), red_poly)
++ v = b ** e
++ self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
++ self.assertEqual(v, expected)
++ e = -1
++ v = b ** e
++ self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
++ self.assertEqual(v, expected)
++
++ # test that pow doesn't take inordinately long when given a modulus.
++ # adding a multiple of `period` should leave results unchanged.
++ e += period * 10 ** 15
++ v = b ** e
++ self.assertEqual(red_poly, GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1]))
++ self.assertEqual(b, GFB(GF2Poly([1, 1, 0, 0, 1, 0, 1]), red_poly))
++ self.assertEqual(v, expected)
++
++
++class GFP:
++ def __init__(self, value, size):
++ assert isinstance(value, int)
++ assert isinstance(size, int) and size >= 2, "size is not a prime"
++ self.value = value % size
++ self.size = size
++
++ def __repr__(self):
++ return f"GFP({self.value}, {self.size})"
++
++ def __eq__(self, rhs):
++ if isinstance(rhs, GFP):
++ return self.value == rhs.value and self.size == rhs.size
++ return NotImplemented
++
++ def __add__(self, rhs):
++ assert isinstance(rhs, GFP)
++ assert self.size == rhs.size
++ return GFP((self.value + rhs.value) % self.size, self.size)
++
++ def __sub__(self, rhs):
++ assert isinstance(rhs, GFP)
++ assert self.size == rhs.size
++ return GFP((self.value - rhs.value) % self.size, self.size)
++
++ def __mul__(self, rhs):
++ assert isinstance(rhs, GFP)
++ assert self.size == rhs.size
++ return GFP((self.value * rhs.value) % self.size, self.size)
++
++ def __div__(self, rhs):
++ assert isinstance(rhs, GFP)
++ assert self.size == rhs.size
++ return self * rhs ** -1
++
++ @property
++ def __pow_period(self):
++ period = self.size - 1
++ assert period > 0, "internal logic error"
++ return period
++
++ def __pow__(self, exponent):
++ assert isinstance(exponent, int)
++ if self.value == 0:
++ if exponent < 0:
++ raise ZeroDivisionError
++ else:
++ return GFP(self.value, self.size)
++ exponent %= self.__pow_period
++ return GFP(pow(self.value, exponent, self.size), self.size)
++
++
++PRIMES = 2, 3, 5, 7, 11, 13, 17, 19
++"""handy list of small primes for testing"""
++
++
++class TestGFPClass(unittest.TestCase):
++ def test_add_sub(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ with self.subTest(av=av, bv=bv, prime=prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ expected = GFP((av + bv) % prime, prime)
++ c = a + b
++ self.assertEqual(a, GFP(av, prime))
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(c, expected)
++ c = b + a
++ self.assertEqual(a, GFP(av, prime))
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(c, expected)
++ expected = GFP((av - bv) % prime, prime)
++ c = a - b
++ self.assertEqual(a, GFP(av, prime))
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(c, expected)
++ expected = GFP((bv - av) % prime, prime)
++ c = b - a
++ self.assertEqual(a, GFP(av, prime))
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(c, expected)
++
++ def test_mul(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ with self.subTest(av=av, bv=bv, prime=prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ expected = GFP((av * bv) % prime, prime)
++ c = a * b
++ self.assertEqual(a, GFP(av, prime))
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(c, expected)
++ c = b * a
++ self.assertEqual(a, GFP(av, prime))
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(c, expected)
++
++ def test_pow(self):
++ for prime in PRIMES:
++ for bv in range(prime):
++ with self.subTest(bv=bv, prime=prime):
++ b = GFP(bv, prime)
++ period = prime - 1
++ e = period - 1
++ expected = GFP(pow(bv, e, prime) if bv != 0 else 0, prime)
++ v = b ** e
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(v, expected)
++ e = -1
++ if bv != 0:
++ v = b ** e
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(v, expected)
++
++ # test that pow doesn't take inordinately long when given
++ # a modulus. adding a multiple of `period` should leave
++ # results unchanged.
++ e += period * 10 ** 15
++ v = b ** e
++ self.assertEqual(b, GFP(bv, prime))
++ self.assertEqual(v, expected)
++
++
++class TestCL(unittest.TestCase):
++ def test_cldivrem(self):
++ n_width = 8
++ d_width = 4
++ width = max(n_width, d_width)
++ for nv in range(2 ** n_width):
++ n = GF2Poly(unpack_poly(nv))
++ for dv in range(1, 2 ** d_width):
++ d = GF2Poly(unpack_poly(dv))
++ with self.subTest(n=str(n), nv=nv, d=str(d), dv=dv):
++ q_expected, r_expected = divmod(n, d)
++ self.assertEqual(q_expected * d + r_expected, n)
++ q, r = cldivrem(nv, dv, width)
++ q_expected = pack_poly(q_expected.coefficients)
++ r_expected = pack_poly(r_expected.coefficients)
++ self.assertEqual((q, r), (q_expected, r_expected))
++
++ def test_clmul(self):
++ a_width = 5
++ b_width = 5
++ for av in range(2 ** a_width):
++ a = GF2Poly(unpack_poly(av))
++ for bv in range(2 ** b_width):
++ b = GF2Poly(unpack_poly(bv))
++ with self.subTest(a=str(a), av=av, b=str(b), bv=bv):
++ expected = a * b
++ product = clmul(av, bv)
++ expected = pack_poly(expected.coefficients)
++ self.assertEqual(product, expected)
++
++
++class TestGFBInstructions(unittest.TestCase):
++ @staticmethod
++ def init_aes_red_poly():
++ # AES's finite field reducing polynomial
++ red_poly = GF2Poly([1, 1, 0, 1, 1, 0, 0, 0, 1])
++ ST.reinit(GFBREDPOLY=pack_poly(red_poly.coefficients))
++ return red_poly
++
++ def test_gfbmul(self):
++ # AES's finite field reducing polynomial
++ red_poly = self.init_aes_red_poly()
++ a_width = 8
++ b_width = 4
++ for av in range(2 ** a_width):
++ a = GFB(GF2Poly(unpack_poly(av)), red_poly)
++ for bv in range(2 ** b_width):
++ b = GFB(GF2Poly(unpack_poly(bv)), red_poly)
++ expected = a * b
++ with self.subTest(a=str(a), av=av, b=str(b), bv=bv, expected=str(expected)):
++ product = gfbmul(av, bv)
++ expectedv = pack_poly(expected.value.coefficients)
++ self.assertEqual(product, expectedv)
++
++ def test_gfbmadd(self):
++ # AES's finite field reducing polynomial
++ red_poly = self.init_aes_red_poly()
++ a_width = 5
++ b_width = 4
++ c_width = 4
++ for av in range(2 ** a_width):
++ a = GFB(GF2Poly(unpack_poly(av)), red_poly)
++ for bv in range(2 ** b_width):
++ b = GFB(GF2Poly(unpack_poly(bv)), red_poly)
++ for cv in range(2 ** c_width):
++ c = GFB(GF2Poly(unpack_poly(cv)), red_poly)
++ expected = a * b + c
++ with self.subTest(a=str(a), av=av,
++ b=str(b), bv=bv,
++ c=str(c), cv=cv,
++ expected=str(expected)):
++ result = gfbmadd(av, bv, cv)
++ expectedv = pack_poly(expected.value.coefficients)
++ self.assertEqual(result, expectedv)
++
++ def test_gfbinv(self):
++ # AES's finite field reducing polynomial
++ red_poly = self.init_aes_red_poly()
++ width = 8
++ for av in range(2 ** width):
++ a = GFB(GF2Poly(unpack_poly(av)), red_poly)
++ expected = a ** -1 if av != 0 else GFB(GF2Poly(), red_poly)
++ with self.subTest(a=str(a), av=av, expected=str(expected)):
++ result = gfbinv(av)
++ expectedv = pack_poly(expected.value.coefficients)
++ self.assertEqual(result, expectedv)
++
++
++class TestGFPInstructions(unittest.TestCase):
++ def test_gfpadd(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ expected = a + b
++ with self.subTest(a=str(a), b=str(b),
++ expected=str(expected)):
++ ST.reinit(GFPRIME=prime)
++ v = gfpadd(av, bv)
++ self.assertEqual(v, expected.value)
++
++ def test_gfpsub(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ expected = a - b
++ with self.subTest(a=str(a), b=str(b),
++ expected=str(expected)):
++ ST.reinit(GFPRIME=prime)
++ v = gfpsub(av, bv)
++ self.assertEqual(v, expected.value)
++
++ def test_gfpmul(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ expected = a * b
++ with self.subTest(a=str(a), b=str(b),
++ expected=str(expected)):
++ ST.reinit(GFPRIME=prime)
++ v = gfpmul(av, bv)
++ self.assertEqual(v, expected.value)
++
++ def test_gfpinv(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ a = GFP(av, prime)
++ if av == 0:
++ # TODO: determine what's expected for division by zero
++ continue
++ else:
++ expected = a ** -1
++ with self.subTest(a=str(a), expected=str(expected)):
++ ST.reinit(GFPRIME=prime)
++ v = gfpinv(av)
++ self.assertEqual(v, expected.value)
++
++ def test_gfpmadd(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ for cv in range(prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ c = GFP(cv, prime)
++ expected = a * b + c
++ with self.subTest(a=str(a), b=str(b), c=str(c),
++ expected=str(expected)):
++ ST.reinit(GFPRIME=prime)
++ v = gfpmadd(av, bv, cv)
++ self.assertEqual(v, expected.value)
++
++ def test_gfpmsub(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ for cv in range(prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ c = GFP(cv, prime)
++ expected = a * b - c
++ with self.subTest(a=str(a), b=str(b), c=str(c),
++ expected=str(expected)):
++ ST.reinit(GFPRIME=prime)
++ v = gfpmsub(av, bv, cv)
++ self.assertEqual(v, expected.value)
++
++ def test_gfpmsubr(self):
++ for prime in PRIMES:
++ for av in range(prime):
++ for bv in range(prime):
++ for cv in range(prime):
++ a = GFP(av, prime)
++ b = GFP(bv, prime)
++ c = GFP(cv, prime)
++ expected = c - a * b
++ with self.subTest(a=str(a), b=str(b), c=str(c),
++ expected=str(expected)):
++ ST.reinit(GFPRIME=prime)
++ v = gfpmsubr(av, bv, cv)
++ self.assertEqual(v, expected.value)
++
++
++if __name__ == "__main__":
++ unittest.main()