finitefield.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import random
  2. from .polynomial import polynomialsOver
  3. from .modp import *
  4. # isIrreducible: Polynomial, int -> bool
  5. # determine if the given monic polynomial with coefficients in Z/p is
  6. # irreducible over Z/p where p is the given integer
  7. # Algorithm 4.69 in the Handbook of Applied Cryptography
  8. def isIrreducible(polynomial, p):
  9. ZmodP = IntegersModP(p)
  10. if polynomial.field is not ZmodP:
  11. raise TypeError("Given a polynomial that's not over %s, but instead %r" %
  12. (ZmodP.__name__, polynomial.field.__name__))
  13. poly = polynomialsOver(ZmodP).factory
  14. x = poly([0,1])
  15. powerTerm = x
  16. isUnit = lambda p: p.degree() == 0
  17. for _ in range(int(polynomial.degree() / 2)):
  18. powerTerm = powerTerm.powmod(p, polynomial)
  19. gcdOverZmodp = gcd(polynomial, powerTerm - x)
  20. if not isUnit(gcdOverZmodp):
  21. return False
  22. return True
  23. # generateIrreduciblePolynomial: int, int -> Polynomial
  24. # generate a random irreducible polynomial of a given degree over Z/p, where p
  25. # is given by the integer 'modulus'. This algorithm is expected to terminate
  26. # after 'degree' many irreducilibity tests. By Chernoff bounds the probability
  27. # it deviates from this by very much is exponentially small.
  28. def generateIrreduciblePolynomial(modulus, degree):
  29. Zp = IntegersModP(modulus)
  30. Polynomial = polynomialsOver(Zp)
  31. while True:
  32. coefficients = [Zp(random.randint(0, modulus-1)) for _ in range(degree)]
  33. randomMonicPolynomial = Polynomial(coefficients + [Zp(1)])
  34. print(randomMonicPolynomial)
  35. if isIrreducible(randomMonicPolynomial, modulus):
  36. return randomMonicPolynomial
  37. # create a type constructor for the finite field of order p^m for p prime, m >= 1
  38. @memoize
  39. def FiniteField(p, m, polynomialModulus=None):
  40. Zp = IntegersModP(p)
  41. if m == 1:
  42. return Zp
  43. Polynomial = polynomialsOver(Zp)
  44. if polynomialModulus is None:
  45. polynomialModulus = generateIrreduciblePolynomial(modulus=p, degree=m)
  46. class Fq(FieldElement):
  47. fieldSize = int(p ** m)
  48. primeSubfield = Zp
  49. idealGenerator = polynomialModulus
  50. operatorPrecedence = 3
  51. def __init__(self, poly):
  52. if type(poly) is Fq:
  53. self.poly = poly.poly
  54. elif type(poly) is int or type(poly) is Zp:
  55. self.poly = Polynomial([Zp(poly)])
  56. elif isinstance(poly, Polynomial):
  57. self.poly = poly % polynomialModulus
  58. else:
  59. self.poly = Polynomial([Zp(x) for x in poly]) % polynomialModulus
  60. self.field = Fq
  61. @typecheck
  62. def __add__(self, other): return Fq(self.poly + other.poly)
  63. @typecheck
  64. def __sub__(self, other): return Fq(self.poly - other.poly)
  65. @typecheck
  66. def __mul__(self, other): return Fq(self.poly * other.poly)
  67. @typecheck
  68. def __eq__(self, other): return isinstance(other, Fq) and self.poly == other.poly
  69. @typecheck
  70. def __ne__(self, other): return not self == other
  71. def __pow__(self, n):
  72. if n==0: return Fq([1])
  73. if n==1: return self
  74. if n%2==0:
  75. sqrut = self**(n//2)
  76. return sqrut*sqrut
  77. if n%2==1: return (self**(n-1))*self
  78. #def __pow__(self, n): return Fq(pow(self.poly, n))
  79. def __neg__(self): return Fq(-self.poly)
  80. def __abs__(self): return abs(self.poly)
  81. def __repr__(self): return repr(self.poly) + ' \u2208 ' + self.__class__.__name__
  82. @typecheck
  83. def __divmod__(self, divisor):
  84. q,r = divmod(self.poly, divisor.poly)
  85. return (Fq(q), Fq(r))
  86. def inverse(self):
  87. if self == Fq(0):
  88. raise ZeroDivisionError
  89. x,y,d = extendedEuclideanAlgorithm(self.poly, self.idealGenerator)
  90. if d.degree() != 0:
  91. raise Exception('Somehow, this element has no inverse! Maybe intialized with a non-prime?')
  92. return Fq(x) * Fq(d.coefficients[0].inverse())
  93. Fq.__name__ = 'F_{%d^%d}' % (p,m)
  94. return Fq
  95. if __name__ == "__main__":
  96. F23 = FiniteField(2,3)
  97. x = F23([1,1])
  98. F35 = FiniteField(3,5)
  99. y = F35([1,1,2])