blob: 11bfa8167861b33e2ddb7c542c23690bd4e26aa8 [file] [log] [blame]
#
# Fp^12 CLass - towered over Fp^4
# M.Scott August 2018
#
import copy
from XXX import curve
from XXX.fp4 import *
class Fp12:
def __init__(self, a=None, b=None, c=None):
if b is None:
if a is None:
self.a = Fp4()
self.b = Fp4()
self.c = Fp4()
else:
self.a = a.copy()
self.b = Fp4()
self.c = Fp4()
else:
self.a = a.copy()
self.b = b.copy()
self.c = c.copy()
def copy(self):
return copy.deepcopy(self)
def one():
return Fp12(Fp4(Fp2(Fp(1))))
def get(self):
return(self.a, self.b, self.c)
def set(self, a, b, c):
self.a = a
self.b = b
self.c = c
return self
def __add__(self, other):
R = Fp12(self.a + other.a, self.b + other.b, self.c + other.c)
return R
def __iadd__(self, other):
self.a += other.a
self.b += other.b
self.c += other.c
return self
def __sub__(self, other):
R = Fp12(self.a - other.a, self.b - other.b, self.c - other.c)
return R
def __isub__(self, other):
self.a -= other.a
self.b -= other.b
self.c -= other.c
return self
def __eq__(self, other):
return (self.a == other.a and self.b == other.b and self.c == other.c)
def __ne__(self, other):
return (self.a != other.a or self.b != other.b or self.c != other.c)
def conj(self):
self.a = self.a.conj()
self.b = -self.b.conj()
self.c = self.c.conj()
return self
# unitary squaring
def usqr(self):
A = self.a.copy()
self.a.sqr()
D = self.a.copy()
self.a += self.a
self.a += D
A = A.conj()
A += A
self.a -= A
B = self.c.copy()
B.sqr()
B = B.mulQNR()
D = B.copy()
B += B
B += D
C = self.b.copy()
C.sqr()
D = C.copy()
C += C
C += D
self.b = self.b.conj()
self.b += self.b
self.c = self.c.conj()
self.c += self.c
self.c = -self.c
self.b += B
self.c += C
return self
# regular squaring
def sqr(self): # mutable
A = self.a.copy()
A.sqr()
B = self.b * self.c
B += B
C = self.c.copy()
C.sqr()
D = self.a * self.b
D += D
self.c += (self.a + self.b)
self.c.sqr()
self.a = A + B.mulQNR()
self.b = D + C.mulQNR()
self.c -= (A + B + C + D)
return self
# optimized to take advantage of sparse multiplier
def __imul__(self, other):
zero_c = other.c.iszero()
zero_b = other.b.iszero()
Z0 = self.a * other.a
if not zero_b:
Z2 = self.b * other.b
T0 = self.a + self.b
T1 = other.a + other.b
Z1 = T0 * T1
Z1 -= Z0
if not zero_b:
Z1 -= Z2
T0 = self.b + self.c
T1 = other.b + other.c
Z3 = T0 * T1
if not zero_b:
Z3 -= Z2
T0 = self.a + self.c
T1 = other.a + other.c
T0 *= T1
if not zero_b:
Z2 += T0
else:
Z2 = T0.copy()
Z2 -= Z0
self.b = Z1.copy()
if not zero_c:
T0 = self.c * other.c
Z2 -= T0
Z3 -= T0
self.b += T0.mulQNR()
self.a = Z0 + Z3.mulQNR()
self.c = Z2.copy()
return self
def __mul__(self, other):
R = self.copy()
if R == other:
R.sqr()
else:
R *= other
return R
def muls(self, other): # multiple Fp12 by Fp4
R = Fp12(self.a * other, self.b * other, self.c * other)
return R
def __neg__(self):
R = Fp12(-self.a, -self.b, -self.c)
return R
def iszero(self):
if self.a.iszero() and self.b.iszero() and self.c.iszero():
return True
return False
def isone(self):
if self.a.isone() and self.b.iszero() and self.c.iszero():
return True
return False
def rand(self): # mutable
r = Fp4()
r.rand()
self.a = r.copy()
r.rand()
self.b = r.copy()
r.rand()
self.c = r.copy()
return self
def pow(self, other): # unitary only
e = other
e3 = e * 3
k = e3.bit_length()
x = self.copy()
r = self.copy()
for i in range(k - 2, 0, -1):
r.usqr()
if big.bit(e3, i) == 1 and big.bit(e, i) == 0:
r *= x
if big.bit(e3, i) == 0 and big.bit(e, i) == 1:
x.conj()
r *= x
x.conj()
return r
def __str__(self): # pretty print
return "[%s,%s,%s]" % (self.a, self.b, self.c)
def mulQNR(self): # assume p=3 mod 8, QNR=1+i
return Fp12(self.c.mulQNR(), self.a, self.b)
def inverse(self):
wa = self.a * self.a - (self.b * self.c).mulQNR()
wb = (self.c * self.c).mulQNR() - self.a * self.b
wc = self.b * self.b - self.a * self.c
f = ((self.b * wc).mulQNR() + self.a *
wa + (self.c * wb).mulQNR()).inverse()
return Fp12(wa * f, wb * f, wc * f)
def powq(self):
X = Fp2(Fp(curve.Fra), Fp(curve.Frb))
X2 = Fp2(Fp(curve.Fra), Fp(curve.Frb))
X2.sqr()
self.a = self.a.powq()
self.b = self.b.powq().muls(X)
self.c = self.c.powq().muls(X2)
return self
def trace(self):
R = self.a.copy()
R += R
R += self.a
return R
def fromBytes(self, E):
FS = curve.EFS
a = Fp4(Fp2(Fp(big.from_bytes(E[0:FS])),
Fp(big.from_bytes(E[FS:2 * FS]))),
Fp2(Fp(big.from_bytes(E[2 * FS:3 * FS])),
Fp(big.from_bytes(E[3 * FS:4 * FS]))))
b = Fp4(Fp2(Fp(big.from_bytes(E[4 * FS:5 * FS])),
Fp(big.from_bytes(E[5 * FS:6 * FS]))),
Fp2(Fp(big.from_bytes(E[6 * FS:7 * FS])),
Fp(big.from_bytes(E[7 * FS:8 * FS]))))
c = Fp4(Fp2(Fp(big.from_bytes(E[8 * FS:9 * FS])),
Fp(big.from_bytes(E[9 * FS:10 * FS]))),
Fp2(Fp(big.from_bytes(E[10 * FS:11 * FS])),
Fp(big.from_bytes(E[11 * FS:12 * FS]))))
self.set(a, b, c)
def toBytes(self):
FS = curve.EFS
a, b, c = self.get()
aa, ab = a.get()
aaa, aab = aa.get()
aba, abb = ab.get()
ba, bb = b.get()
baa, bab = ba.get()
bba, bbb = bb.get()
ca, cb = c.get()
caa, cab = ca.get()
cba, cbb = cb.get()
E = bytearray(12 * FS)
W = big.to_bytes(aaa)
for i in range(0, FS):
E[i] = W[i]
W = big.to_bytes(aab)
for i in range(0, FS):
E[FS + i] = W[i]
W = big.to_bytes(aba)
for i in range(0, FS):
E[2 * FS + i] = W[i]
W = big.to_bytes(abb)
for i in range(0, FS):
E[3 * FS + i] = W[i]
W = big.to_bytes(baa)
for i in range(0, FS):
E[4 * FS + i] = W[i]
W = big.to_bytes(bab)
for i in range(0, FS):
E[5 * FS + i] = W[i]
W = big.to_bytes(bba)
for i in range(0, FS):
E[6 * FS + i] = W[i]
W = big.to_bytes(bbb)
for i in range(0, FS):
E[7 * FS + i] = W[i]
W = big.to_bytes(caa)
for i in range(0, FS):
E[8 * FS + i] = W[i]
W = big.to_bytes(cab)
for i in range(0, FS):
E[9 * FS + i] = W[i]
W = big.to_bytes(cba)
for i in range(0, FS):
E[10 * FS + i] = W[i]
W = big.to_bytes(cbb)
for i in range(0, FS):
E[11 * FS + i] = W[i]
return E