| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- 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
- @typecheck
- def __ne__(self, other): return not self == other
- def __pow__(self, n):
- if n==0: return Fq([1])
- if n==1: return self
- if n%2==0:
- sqrut = self**(n//2)
- return sqrut*sqrut
- if n%2==1: return (self**(n-1))*self
- #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])
|