118 lines
3.7 KiB
Python
118 lines
3.7 KiB
Python
import random
|
|
from .polynomial import polynomialsOver
|
|
from .modp import *
|
|
|
|
|
|
|
|
# isIrreducible: Polynomial, int -> bool
|
|
# determine if the given monic polynomial with coefficients in Z/p is
|
|
# irreducible over Z/p where p is the given integer
|
|
# Algorithm 4.69 in the Handbook of Applied Cryptography
|
|
def isIrreducible(polynomial, p):
|
|
ZmodP = IntegersModP(p)
|
|
if polynomial.field is not ZmodP:
|
|
raise TypeError("Given a polynomial that's not over %s, but instead %r" %
|
|
(ZmodP.__name__, polynomial.field.__name__))
|
|
|
|
poly = polynomialsOver(ZmodP).factory
|
|
x = poly([0,1])
|
|
powerTerm = x
|
|
isUnit = lambda p: p.degree() == 0
|
|
|
|
for _ in range(int(polynomial.degree() / 2)):
|
|
powerTerm = powerTerm.powmod(p, polynomial)
|
|
gcdOverZmodp = gcd(polynomial, powerTerm - x)
|
|
if not isUnit(gcdOverZmodp):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
# generateIrreduciblePolynomial: int, int -> Polynomial
|
|
# generate a random irreducible polynomial of a given degree over Z/p, where p
|
|
# is given by the integer 'modulus'. This algorithm is expected to terminate
|
|
# after 'degree' many irreducilibity tests. By Chernoff bounds the probability
|
|
# it deviates from this by very much is exponentially small.
|
|
def generateIrreduciblePolynomial(modulus, degree):
|
|
Zp = IntegersModP(modulus)
|
|
Polynomial = polynomialsOver(Zp)
|
|
|
|
while True:
|
|
coefficients = [Zp(random.randint(0, modulus-1)) for _ in range(degree)]
|
|
randomMonicPolynomial = Polynomial(coefficients + [Zp(1)])
|
|
print(randomMonicPolynomial)
|
|
|
|
if isIrreducible(randomMonicPolynomial, modulus):
|
|
return randomMonicPolynomial
|
|
|
|
|
|
# create a type constructor for the finite field of order p^m for p prime, m >= 1
|
|
@memoize
|
|
def FiniteField(p, m, polynomialModulus=None):
|
|
Zp = IntegersModP(p)
|
|
if m == 1:
|
|
return Zp
|
|
|
|
Polynomial = polynomialsOver(Zp)
|
|
if polynomialModulus is None:
|
|
polynomialModulus = generateIrreduciblePolynomial(modulus=p, degree=m)
|
|
|
|
class Fq(FieldElement):
|
|
fieldSize = int(p ** m)
|
|
primeSubfield = Zp
|
|
idealGenerator = polynomialModulus
|
|
operatorPrecedence = 3
|
|
|
|
def __init__(self, poly):
|
|
if type(poly) is Fq:
|
|
self.poly = poly.poly
|
|
elif type(poly) is int or type(poly) is Zp:
|
|
self.poly = Polynomial([Zp(poly)])
|
|
elif isinstance(poly, Polynomial):
|
|
self.poly = poly % polynomialModulus
|
|
else:
|
|
self.poly = Polynomial([Zp(x) for x in poly]) % polynomialModulus
|
|
|
|
self.field = Fq
|
|
|
|
@typecheck
|
|
def __add__(self, other): return Fq(self.poly + other.poly)
|
|
@typecheck
|
|
def __sub__(self, other): return Fq(self.poly - other.poly)
|
|
@typecheck
|
|
def __mul__(self, other): return Fq(self.poly * other.poly)
|
|
@typecheck
|
|
def __eq__(self, other): return isinstance(other, Fq) and self.poly == other.poly
|
|
|
|
def __pow__(self, n): return Fq(pow(self.poly, n))
|
|
def __neg__(self): return Fq(-self.poly)
|
|
def __abs__(self): return abs(self.poly)
|
|
def __repr__(self): return repr(self.poly) + ' \u2208 ' + self.__class__.__name__
|
|
|
|
@typecheck
|
|
def __divmod__(self, divisor):
|
|
q,r = divmod(self.poly, divisor.poly)
|
|
return (Fq(q), Fq(r))
|
|
|
|
|
|
def inverse(self):
|
|
if self == Fq(0):
|
|
raise ZeroDivisionError
|
|
|
|
x,y,d = extendedEuclideanAlgorithm(self.poly, self.idealGenerator)
|
|
if d.degree() != 0:
|
|
raise Exception('Somehow, this element has no inverse! Maybe intialized with a non-prime?')
|
|
|
|
return Fq(x) * Fq(d.coefficients[0].inverse())
|
|
|
|
|
|
Fq.__name__ = 'F_{%d^%d}' % (p,m)
|
|
return Fq
|
|
|
|
|
|
if __name__ == "__main__":
|
|
F23 = FiniteField(2,3)
|
|
x = F23([1,1])
|
|
|
|
F35 = FiniteField(3,5)
|
|
y = F35([1,1,2])
|