blob: 2ed94cdc581d37851014f4210d9e724ff997c2f8 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
//
// fp12.swift
//
// Created by Michael Scott on 07/07/2015.
// Copyright (c) 2015 Michael Scott. All rights reserved.
//
/* AMCL Fp^12 functions */
/* FP12 elements are of the form a+i.b+i^2.c */
public struct FP12
{
private var a:FP4
private var b:FP4
private var c:FP4
/* reduce all components of this mod Modulus */
mutating func reduce()
{
a.reduce()
b.reduce()
c.reduce()
}
/* normalise all components of this */
mutating func norm()
{
a.norm();
b.norm();
c.norm();
}
/* Constructors */
init(_ d:FP4)
{
a=FP4(d)
b=FP4(0)
c=FP4(0)
}
init(_ d:Int)
{
a=FP4(d)
b=FP4(0)
c=FP4(0)
}
init(_ d:FP4,_ e:FP4,_ f:FP4)
{
a=FP4(d)
b=FP4(e)
c=FP4(f)
}
init(_ x:FP12)
{
a=FP4(x.a)
b=FP4(x.b)
c=FP4(x.c)
}
/* test x==0 ? */
func iszilch() -> Bool
{
//reduce();
return a.iszilch() && b.iszilch() && c.iszilch()
}
mutating func cmove(_ g:FP12,_ d:Int)
{
a.cmove(g.a,d)
b.cmove(g.b,d)
c.cmove(g.c,d)
}
/* return 1 if b==c, no branching */
private static func teq(_ b:Int32,_ c:Int32) -> Int
{
var x=b^c
x-=1 // if x=0, x now -1
return Int((x>>31)&1)
}
/* Constant time select from pre-computed table */
mutating func select(_ g:[FP12],_ b:Int32)
{
let m=b>>31
var babs=(b^m)-m
babs=(babs-1)/2
cmove(g[0],FP12.teq(babs,0)) // conditional move
cmove(g[1],FP12.teq(babs,1))
cmove(g[2],FP12.teq(babs,2))
cmove(g[3],FP12.teq(babs,3))
cmove(g[4],FP12.teq(babs,4))
cmove(g[5],FP12.teq(babs,5))
cmove(g[6],FP12.teq(babs,6))
cmove(g[7],FP12.teq(babs,7))
var invf=FP12(self)
invf.conj()
cmove(invf,Int(m&1))
}
/* test x==1 ? */
public func isunity() -> Bool
{
let one=FP4(1)
return a.equals(one) && b.iszilch() && c.iszilch()
}
/* return 1 if x==y, else 0 */
public func equals(_ x:FP12) -> Bool
{
return a.equals(x.a) && b.equals(x.b) && c.equals(x.c)
}
/* extract a from self */
func geta() -> FP4
{
return a
}
/* extract b */
func getb() -> FP4
{
return b
}
/* extract c */
func getc() -> FP4
{
return c
}
/* copy self=x */
public mutating func copy(_ x:FP12)
{
a.copy(x.a)
b.copy(x.b)
c.copy(x.c)
}
/* set self=1 */
mutating func one()
{
a.one()
b.zero()
c.zero()
}
/* self=conj(self) */
mutating func conj()
{
a.conj()
b.nconj()
c.conj()
}
/* Granger-Scott Unitary Squaring */
mutating func usqr()
{
var A=FP4(a)
var B=FP4(c)
var C=FP4(b)
var D=FP4(0)
a.sqr()
D.copy(a); D.add(a)
a.add(D)
a.norm()
A.nconj()
A.add(A)
a.add(A)
B.sqr()
B.times_i()
D.copy(B); D.add(B)
B.add(D)
B.norm()
C.sqr()
D.copy(C); D.add(C)
C.add(D)
C.norm()
b.conj()
b.add(b)
c.nconj()
c.add(c)
b.add(B)
c.add(C)
reduce()
}
/* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
mutating func sqr()
{
var A=FP4(a)
var B=FP4(b)
var C=FP4(c)
var D=FP4(a)
A.sqr()
B.mul(c)
B.add(B); B.norm()
C.sqr()
D.mul(b)
D.add(D)
c.add(a)
c.add(b); c.norm()
c.sqr()
a.copy(A)
A.add(B)
A.norm()
A.add(C)
A.add(D)
A.norm()
A.neg()
B.times_i()
C.times_i()
a.add(B)
b.copy(C); b.add(D)
c.add(A)
norm()
}
/* FP12 full multiplication this=this*y */
mutating func mul(_ y:FP12)
{
var z0=FP4(a)
var z1=FP4(0)
var z2=FP4(b)
var z3=FP4(0)
var t0=FP4(a)
var t1=FP4(y.a)
z0.mul(y.a)
z2.mul(y.b)
t0.add(b)
t1.add(y.b)
t0.norm(); t1.norm()
z1.copy(t0); z1.mul(t1)
t0.copy(b); t0.add(c)
t1.copy(y.b); t1.add(y.c)
t0.norm(); t1.norm()
z3.copy(t0); z3.mul(t1)
t0.copy(z0); t0.neg()
t1.copy(z2); t1.neg()
z1.add(t0)
b.copy(z1); b.add(t1)
z3.add(t1)
z2.add(t0)
t0.copy(a); t0.add(c)
t1.copy(y.a); t1.add(y.c)
t0.norm(); t1.norm()
t0.mul(t1)
z2.add(t0)
t0.copy(c); t0.mul(y.c)
t1.copy(t0); t1.neg()
c.copy(z2); c.add(t1)
z3.add(t1)
t0.times_i()
b.add(t0)
z3.norm()
z3.times_i()
a.copy(z0); a.add(z3)
norm()
}
/* Special case of multiplication arises from special form of ATE pairing line function */
mutating func smul(_ y:FP12,_ twist:Int)
{
if twist == ECP.D_TYPE {
var z0=FP4(a)
var z2=FP4(b)
var z3=FP4(b)
var t0=FP4(0)
var t1=FP4(y.a)
z0.mul(y.a)
z2.pmul(y.b.real())
b.add(a)
t1.adds(y.b.real())
//t1.real().add(y.b.real())
b.norm(); t1.norm()
b.mul(t1)
z3.add(c); z3.norm()
z3.pmul(y.b.real())
t0.copy(z0); t0.neg()
t1.copy(z2); t1.neg()
b.add(t0)
b.add(t1)
z3.add(t1)
z2.add(t0)
t0.copy(a); t0.add(c)
t0.norm(); z3.norm()
t0.mul(y.a)
c.copy(z2); c.add(t0)
z3.times_i()
a.copy(z0); a.add(z3)
}
if twist == ECP.M_TYPE {
var z0=FP4(a)
var z1=FP4(0)
var z2=FP4(0)
var z3=FP4(0)
var t0=FP4(a)
var t1=FP4(0)
z0.mul(y.a)
t0.add(b)
t0.norm()
z1.copy(t0); z1.mul(y.a)
t0.copy(b); t0.add(c)
t0.norm();
z3.copy(t0); //z3.mul(y.c);
z3.pmul(y.c.getb())
z3.times_i()
t0.copy(z0); t0.neg()
z1.add(t0)
b.copy(z1);
z2.copy(t0)
t0.copy(a); t0.add(c)
t1.copy(y.a); t1.add(y.c)
t0.norm()
t1.norm()
t0.mul(t1)
z2.add(t0)
t0.copy(c)
t0.pmul(y.c.getb())
t0.times_i()
t1.copy(t0); t1.neg()
c.copy(z2); c.add(t1)
z3.add(t1)
t0.times_i()
b.add(t0)
z3.norm();
z3.times_i()
a.copy(z0); a.add(z3)
}
norm()
}
/* self=1/self */
mutating func inverse()
{
var f0=FP4(a)
var f1=FP4(b)
var f2=FP4(a)
var f3=FP4(0)
norm()
f0.sqr()
f1.mul(c)
f1.times_i()
f0.sub(f1); f0.norm()
f1.copy(c); f1.sqr()
f1.times_i()
f2.mul(b)
f1.sub(f2); f1.norm()
f2.copy(b); f2.sqr()
f3.copy(a); f3.mul(c)
f2.sub(f3); f2.norm()
f3.copy(b); f3.mul(f2)
f3.times_i()
a.mul(f0)
f3.add(a)
c.mul(f1)
c.times_i()
f3.add(c); f3.norm()
f3.inverse()
a.copy(f0); a.mul(f3)
b.copy(f1); b.mul(f3)
c.copy(f2); c.mul(f3)
}
/* self=self^p using Frobenius */
mutating func frob(_ f:FP2)
{
var f2=FP2(f)
var f3=FP2(f)
f2.sqr()
f3.mul(f2)
a.frob(f3)
b.frob(f3)
c.frob(f3)
b.pmul(f)
c.pmul(f2)
}
/* trace function */
func trace() -> FP4
{
var t=FP4(0)
t.copy(a)
t.imul(3)
t.reduce()
return t
}
/* convert from byte array to FP12 */
static func fromBytes(_ w:[UInt8]) -> FP12
{
let RM=Int(BIG.MODBYTES)
var t=[UInt8](repeating: 0,count: RM)
for i in 0 ..< RM {t[i]=w[i]}
var a=BIG.fromBytes(t)
for i in 0 ..< RM {t[i]=w[i+RM]}
var b=BIG.fromBytes(t)
var c=FP2(a,b)
for i in 0 ..< RM {t[i]=w[i+2*RM]}
a=BIG.fromBytes(t)
for i in 0 ..< RM {t[i]=w[i+3*RM]}
b=BIG.fromBytes(t)
var d=FP2(a,b)
let e=FP4(c,d)
for i in 0 ..< RM {t[i]=w[i+4*RM]}
a=BIG.fromBytes(t)
for i in 0 ..< RM {t[i]=w[i+5*RM]}
b=BIG.fromBytes(t)
c=FP2(a,b)
for i in 0 ..< RM {t[i]=w[i+6*RM]}
a=BIG.fromBytes(t)
for i in 0 ..< RM {t[i]=w[i+7*RM]}
b=BIG.fromBytes(t)
d=FP2(a,b)
let f=FP4(c,d)
for i in 0 ..< RM {t[i]=w[i+8*RM]}
a=BIG.fromBytes(t)
for i in 0 ..< RM {t[i]=w[i+9*RM]}
b=BIG.fromBytes(t)
c=FP2(a,b)
for i in 0 ..< RM {t[i]=w[i+10*RM]}
a=BIG.fromBytes(t)
for i in 0 ..< RM {t[i]=w[i+11*RM]}
b=BIG.fromBytes(t);
d=FP2(a,b)
let g=FP4(c,d)
return FP12(e,f,g)
}
/* convert this to byte array */
func toBytes(_ w:inout [UInt8])
{
let RM=Int(BIG.MODBYTES)
var t=[UInt8](repeating: 0,count: RM)
a.geta().getA().toBytes(&t)
for i in 0 ..< RM {w[i]=t[i]}
a.geta().getB().toBytes(&t)
for i in 0 ..< RM {w[i+RM]=t[i]}
a.getb().getA().toBytes(&t)
for i in 0 ..< RM {w[i+2*RM]=t[i]}
a.getb().getB().toBytes(&t)
for i in 0 ..< RM {w[i+3*RM]=t[i]}
b.geta().getA().toBytes(&t)
for i in 0 ..< RM {w[i+4*RM]=t[i]}
b.geta().getB().toBytes(&t);
for i in 0 ..< RM {w[i+5*RM]=t[i]}
b.getb().getA().toBytes(&t)
for i in 0 ..< RM {w[i+6*RM]=t[i]}
b.getb().getB().toBytes(&t)
for i in 0 ..< RM {w[i+7*RM]=t[i]}
c.geta().getA().toBytes(&t)
for i in 0 ..< RM {w[i+8*RM]=t[i]}
c.geta().getB().toBytes(&t)
for i in 0 ..< RM {w[i+9*RM]=t[i]}
c.getb().getA().toBytes(&t)
for i in 0 ..< RM {w[i+10*RM]=t[i]}
c.getb().getB().toBytes(&t)
for i in 0 ..< RM {w[i+11*RM]=t[i]}
}
/* convert to hex string */
public func toString() -> String
{
return ("["+a.toString()+","+b.toString()+","+c.toString()+"]")
}
/* self=self^e */
/* Note this is simple square and multiply, so not side-channel safe */
func pow(_ e:BIG) -> FP12
{
var sf = FP12(self)
sf.norm()
//e.norm()
var e1=BIG(e)
e1.norm()
var e3=BIG(e1)
e3.pmul(3)
e3.norm();
var w=FP12(sf)
let nb=e3.nbits()
for i in (1...nb-2).reversed()
{
w.usqr()
let bt=e3.bit(UInt(i))-e1.bit(UInt(i))
if bt == 1 {
w.mul(sf)
}
if bt == -1 {
sf.conj(); w.mul(sf); sf.conj()
}
}
w.reduce()
return w
}
/* constant time powering by small integer of max length bts */
mutating func pinpow(_ e:Int32,_ bts:Int32)
{
var R=[FP12]()
R.append(FP12(1))
R.append(FP12(self))
//for var i=bts-1;i>=0;i--
for i in (0...bts-1).reversed()
{
let b=Int((e>>i)&1)
R[1-b].mul(R[b])
R[b].usqr()
}
copy(R[0])
}
public func compow(_ e :BIG,_ r :BIG) -> FP4
{
let f=FP2(BIG(ROM.Fra),BIG(ROM.Frb))
let q=BIG(ROM.Modulus)
var g1=FP12(self)
var g2=FP12(self)
var m=BIG(q)
m.mod(r)
var a=BIG(e)
a.mod(m)
var b=BIG(e)
b.div(m);
var c=g1.trace()
if b.iszilch()
{
c=c.xtr_pow(e)
return c
}
g2.frob(f)
let cp=g2.trace()
g1.conj()
g2.mul(g1)
let cpm1=g2.trace()
g2.mul(g1)
let cpm2=g2.trace()
c=c.xtr_pow2(cp,cpm1,cpm2,a,b)
return c
}
/* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
// Bos & Costello https://eprint.iacr.org/2013/458.pdf
// Faz-Hernandez & Longa & Sanchez https://eprint.iacr.org/2013/158.pdf
// Side channel attack secure
static func pow4(_ q:[FP12],_ u:[BIG]) -> FP12
{
var g=[FP12]();
for _ in 0 ..< 8 {g.append(FP12(0))}
var r=FP12(0)
var p=FP12(0)
var t=[BIG]()
for i in 0 ..< 4 {
t.append(BIG(u[i]))
t[i].norm()
}
var mt=BIG(0);
var w=[Int8](repeating: 0,count: BIG.NLEN*Int(BIG.BASEBITS)+1)
var s=[Int8](repeating: 0,count: BIG.NLEN*Int(BIG.BASEBITS)+1)
// precompute table
g[0].copy(q[0]) // q[0]
g[1].copy(g[0]); g[1].mul(q[1]) // q[0].q[1]
g[2].copy(g[0]); g[2].mul(q[2]) // q[0].q[2]
g[3].copy(g[1]); g[3].mul(q[2]) // q[0].q[1].q[2]
g[4].copy(g[0]); g[4].mul(q[3]) // q[0].q[3]
g[5].copy(g[1]); g[5].mul(q[3]) // q[0].q[1].q[3]
g[6].copy(g[2]); g[6].mul(q[3]) // q[0].q[2].q[3]
g[7].copy(g[3]); g[7].mul(q[3]) // q[0].q[1].q[2].q[3]
// Make it odd
let pb=1-t[0].parity()
t[0].inc(pb)
t[0].norm()
// Number of bits
mt.zero();
for i in 0 ..< 4 {
mt.or(t[i]);
}
let nb=1+mt.nbits()
// Sign pivot
s[nb-1]=1
for i in 0 ..< nb-1 {
t[0].fshr(1)
s[i]=2*Int8(t[0].parity())-1
}
// Recoded exponent
for i in 0 ..< nb {
w[i]=0
var k=1
for j in 1 ..< 4 {
let bt=s[i]*Int8(t[j].parity())
t[j].fshr(1)
t[j].dec(Int(bt>>1))
t[j].norm()
w[i]+=bt*Int8(k)
k=2*k
}
}
// Main loop
p.select(g,Int32(2*w[nb-1]+1))
for i in (0 ..< nb-1).reversed() {
p.usqr()
r.select(g,Int32(2*w[i]+s[i]))
p.mul(r)
}
// apply correction
r.copy(q[0]); r.conj()
r.mul(p)
p.cmove(r,pb)
p.reduce()
return p
}
/* p=q0^u0.q1^u1.q2^u2.q3^u3 */
/* Timing attack secure, but not cache attack secure */
/*
static func pow4(_ q:[FP12],_ u:[BIG]) -> FP12
{
var a=[Int32](repeating: 0,count: 4)
var g=[FP12]();
for _ in 0 ..< 8 {g.append(FP12(0))}
var s=[FP12]();
for _ in 0 ..< 2 {s.append(FP12(0))}
let c=FP12(1)
let p=FP12(0)
var t=[BIG]()
for i in 0 ..< 4
{t.append(BIG(u[i]))}
let mt=BIG(0);
var w=[Int8](repeating: 0,count: BIG.NLEN*Int(BIG.BASEBITS)+1)
g[0].copy(q[0]); s[0].copy(q[1]); s[0].conj(); g[0].mul(s[0])
g[1].copy(g[0])
g[2].copy(g[0])
g[3].copy(g[0])
g[4].copy(q[0]); g[4].mul(q[1])
g[5].copy(g[4])
g[6].copy(g[4])
g[7].copy(g[4])
s[1].copy(q[2]); s[0].copy(q[3]); s[0].conj(); s[1].mul(s[0])
s[0].copy(s[1]); s[0].conj(); g[1].mul(s[0])
g[2].mul(s[1])
g[5].mul(s[0])
g[6].mul(s[1])
s[1].copy(q[2]); s[1].mul(q[3])
s[0].copy(s[1]); s[0].conj(); g[0].mul(s[0])
g[3].mul(s[1])
g[4].mul(s[0])
g[7].mul(s[1])
// if power is even add 1 to power, and add q to correction
for i in 0 ..< 4
{
if t[i].parity()==0
{
t[i].inc(1); t[i].norm()
c.mul(q[i])
}
mt.add(t[i]); mt.norm()
}
c.conj();
let nb=1+mt.nbits();
// convert exponent to signed 1-bit window
for j in 0 ..< nb
{
for i in 0 ..< 4
{
a[i]=Int32(t[i].lastbits(2)-2)
t[i].dec(Int(a[i]));
t[i].norm()
t[i].fshr(1)
}
let sum=8*a[0]+4*a[1]+2*a[2]+a[3]
w[j]=Int8(sum)
}
w[nb]=Int8(8*t[0].lastbits(2)+4*t[1].lastbits(2))
w[nb]+=Int8(2*t[2].lastbits(2)+t[3].lastbits(2))
p.copy(g[Int(w[nb]-1)/2])
//for var i=nb-1;i>=0;i--
for i in (0...nb-1).reversed()
{
let m=w[i]>>7
let j=(w[i]^m)-m // j=abs(w[i])
let k=Int((j-1)/2)
s[0].copy(g[k]); s[1].copy(g[k]); s[1].conj()
p.usqr()
p.mul(s[Int(m&1)])
}
p.mul(c) // apply correction
p.reduce()
return p
}
*/
}