blob: fc13bd90da8f46d95356af7905d04181d14f0a4b [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.
*/
//
// ecp2.swift
//
// Created by Michael Scott on 07/07/2015.
// Copyright (c) 2015 Michael Scott. All rights reserved.
//
/* AMCL Weierstrass elliptic curve functions over FP2 */
public struct ECP2 {
private var x:FP2
private var y:FP2
private var z:FP2
/* Constructor - set self=O */
init()
{
x=FP2()
y=FP2(1)
z=FP2()
}
/* Test self=O? */
public func is_infinity() -> Bool
{
return x.iszilch() && z.iszilch()
}
/* copy self=P */
mutating public func copy(_ P:ECP2)
{
x.copy(P.x)
y.copy(P.y)
z.copy(P.z)
}
/* set self=O */
mutating func inf() {
x.zero()
y.one()
z.zero()
}
/* Conditional move of Q to P dependant on d */
mutating func cmove(_ Q:ECP2,_ d:Int)
{
x.cmove(Q.x,d);
y.cmove(Q.y,d);
z.cmove(Q.z,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(_ W:[ECP2],_ b:Int32)
{
var MP=ECP2()
let m=b>>31
var babs=(b^m)-m
babs=(babs-1)/2
cmove(W[0],ECP2.teq(babs,0)) // conditional move
cmove(W[1],ECP2.teq(babs,1))
cmove(W[2],ECP2.teq(babs,2))
cmove(W[3],ECP2.teq(babs,3))
cmove(W[4],ECP2.teq(babs,4))
cmove(W[5],ECP2.teq(babs,5))
cmove(W[6],ECP2.teq(babs,6))
cmove(W[7],ECP2.teq(babs,7))
MP.copy(self)
MP.neg()
cmove(MP,Int(m&1))
}
/* Test if P == Q */
func equals(_ Q:ECP2) -> Bool
{
var a=FP2(x)
var b=FP2(Q.x)
a.mul(Q.z); b.mul(z)
if !a.equals(b) {return false}
a.copy(y); a.mul(Q.z)
b.copy(Q.y); b.mul(z)
if !a.equals(b) {return false}
return true;
}
/* set self=-self */
mutating func neg()
{
y.norm(); y.neg(); y.norm()
return
}
/* set to Affine - (x,y,z) to (x,y) */
mutating func affine() {
if is_infinity() {return}
let one=FP2(1)
if z.equals(one) {
x.reduce(); y.reduce()
return
}
z.inverse()
x.mul(z); x.reduce()
y.mul(z); y.reduce()
z.copy(one)
}
/* extract affine x as FP2 */
func getX() -> FP2
{
var W=ECP2(); W.copy(self)
W.affine()
return W.x
}
/* extract affine y as FP2 */
func getY() -> FP2
{
var W=ECP2(); W.copy(self)
W.affine()
return W.y
}
/* extract projective x */
func getx() -> FP2
{
return x
}
/* extract projective y */
func gety() -> FP2
{
return y
}
/* extract projective z */
func getz() -> FP2
{
return z
}
/* convert to byte array */
func toBytes(_ b:inout [UInt8])
{
let RM=Int(CONFIG_BIG.MODBYTES)
var t=[UInt8](repeating: 0,count: RM)
var W=ECP2(); W.copy(self)
W.affine();
W.x.getA().toBytes(&t)
for i in 0 ..< RM
{b[i]=t[i]}
W.x.getB().toBytes(&t);
for i in 0 ..< RM
{b[i+RM]=t[i]}
W.y.getA().toBytes(&t);
for i in 0 ..< RM
{b[i+2*RM]=t[i]}
W.y.getB().toBytes(&t);
for i in 0 ..< RM
{b[i+3*RM]=t[i]}
}
/* convert from byte array to point */
static func fromBytes(_ b:[UInt8]) -> ECP2
{
let RM=Int(CONFIG_BIG.MODBYTES)
var t=[UInt8](repeating: 0,count: RM)
for i in 0 ..< RM {t[i]=b[i]}
var ra=BIG.fromBytes(t);
for i in 0 ..< RM {t[i]=b[i+RM]}
var rb=BIG.fromBytes(t);
let rx=FP2(ra,rb)
for i in 0 ..< RM {t[i]=b[i+2*RM]}
ra=BIG.fromBytes(t)
for i in 0 ..< RM {t[i]=b[i+3*RM]}
rb=BIG.fromBytes(t)
let ry=FP2(ra,rb)
return ECP2(rx,ry)
}
/* convert self to hex string */
func toString() -> String
{
var W=ECP2(); W.copy(self)
if W.is_infinity() {return "infinity"}
W.affine()
return "("+W.x.toString()+","+W.y.toString()+")"
}
/* Calculate RHS of twisted curve equation x^3+B/i */
static func RHS(_ x:FP2) -> FP2
{
var r=FP2(x)
r.sqr()
var b=FP2(BIG(ROM.CURVE_B))
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {
b.div_ip()
}
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
b.norm()
b.mul_ip()
b.norm()
}
r.mul(x)
r.add(b)
r.reduce()
return r
}
/* construct self from (x,y) - but set to O if not on curve */
public init(_ ix:FP2,_ iy:FP2)
{
x=FP2(ix)
y=FP2(iy)
z=FP2(1)
x.norm()
let rhs=ECP2.RHS(x)
var y2=FP2(y)
y2.sqr()
if !y2.equals(rhs) {inf()}
}
/* construct this from x - but set to O if not on curve */
init(_ ix:FP2)
{
x=FP2(ix)
y=FP2(1)
z=FP2(1)
x.norm()
var rhs=ECP2.RHS(x)
if rhs.sqrt()
{
y.copy(rhs);
}
else {inf()}
}
/* this+=this */
@discardableResult mutating func dbl() -> Int
{
if y.iszilch()
{
inf();
return -1;
}
var iy=FP2(y)
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {
iy.mul_ip(); iy.norm()
}
var t0=FP2(y)
t0.sqr();
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {
t0.mul_ip()
}
var t1=FP2(iy)
t1.mul(z)
var t2=FP2(z)
t2.sqr()
z.copy(t0)
z.add(t0); z.norm()
z.add(z)
z.add(z)
z.norm()
t2.imul(3*ROM.CURVE_B_I)
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
t2.mul_ip()
t2.norm()
}
var x3=FP2(t2)
x3.mul(z)
var y3=FP2(t0)
y3.add(t2); y3.norm()
z.mul(t1)
t1.copy(t2); t1.add(t2); t2.add(t1); t2.norm()
t0.sub(t2); t0.norm() //y^2-9bz^2
y3.mul(t0); y3.add(x3) //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
t1.copy(x); t1.mul(iy) //
x.copy(t0); x.norm(); x.mul(t1); x.add(x) //(y^2-9bz^2)xy2
x.norm()
y.copy(y3); y.norm()
return 1
}
/* this+=Q - return 0 for add, 1 for double, -1 for O */
@discardableResult mutating func add(_ Q:ECP2) -> Int
{
let b=3*ROM.CURVE_B_I
var t0=FP2(x)
t0.mul(Q.x) // x.Q.x
var t1=FP2(y)
t1.mul(Q.y) // y.Q.y
var t2=FP2(z)
t2.mul(Q.z)
var t3=FP2(x)
t3.add(y); t3.norm() //t3=X1+Y1
var t4=FP2(Q.x)
t4.add(Q.y); t4.norm() //t4=X2+Y2
t3.mul(t4) //t3=(X1+Y1)(X2+Y2)
t4.copy(t0); t4.add(t1) //t4=X1.X2+Y1.Y2
t3.sub(t4); t3.norm();
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {
t3.mul_ip(); t3.norm() //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
}
t4.copy(y)
t4.add(z); t4.norm() //t4=Y1+Z1
var x3=FP2(Q.y)
x3.add(Q.z); x3.norm() //x3=Y2+Z2
t4.mul(x3) //t4=(Y1+Z1)(Y2+Z2)
x3.copy(t1) //
x3.add(t2) //X3=Y1.Y2+Z1.Z2
t4.sub(x3); t4.norm();
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {
t4.mul_ip(); t4.norm() //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
}
x3.copy(x); x3.add(z); x3.norm() // x3=X1+Z1
var y3=FP2(Q.x)
y3.add(Q.z); y3.norm() // y3=X2+Z2
x3.mul(y3) // x3=(X1+Z1)(X2+Z2)
y3.copy(t0)
y3.add(t2) // y3=X1.X2+Z1+Z2
y3.rsub(x3); y3.norm() // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {
t0.mul_ip(); t0.norm() // x.Q.x
t1.mul_ip(); t1.norm() // y.Q.y
}
x3.copy(t0); x3.add(t0)
t0.add(x3); t0.norm()
t2.imul(b)
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
t2.mul_ip(); t2.norm()
}
var z3=FP2(t1); z3.add(t2); z3.norm()
t1.sub(t2); t1.norm()
y3.imul(b)
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
y3.mul_ip()
y3.norm()
}
x3.copy(y3); x3.mul(t4); t2.copy(t3); t2.mul(t1); x3.rsub(t2)
y3.mul(t0); t1.mul(z3); y3.add(t1)
t0.mul(t3); z3.mul(t4); z3.add(t0)
x.copy(x3); x.norm()
y.copy(y3); y.norm()
z.copy(z3); z.norm()
return 0
}
/* set self-=Q */
@discardableResult mutating func sub(_ Q:ECP2) -> Int
{
var NQ=ECP2(); NQ.copy(Q)
NQ.neg()
let D=add(NQ)
return D
}
/* set self*=q, where q is Modulus, using Frobenius */
mutating func frob(_ X:FP2)
{
var X2=FP2(X)
X2.sqr()
x.conj()
y.conj()
z.conj()
z.reduce()
x.mul(X2)
y.mul(X2)
y.mul(X)
}
/* P*=e */
func mul(_ e:BIG) -> ECP2
{
/* fixed size windows */
var mt=BIG()
var t=BIG()
var P=ECP2()
var Q=ECP2()
var C=ECP2()
var W=[ECP2]();
for _ in 0 ..< 8 {W.append(ECP2())}
var w=[Int8](repeating: 0,count: 1+(CONFIG_BIG.NLEN*Int(CONFIG_BIG.BASEBITS)+3)/4)
if is_infinity() {return ECP2()}
/* precompute table */
Q.copy(self)
Q.dbl()
W[0].copy(self)
for i in 1 ..< 8
{
W[i].copy(W[i-1])
W[i].add(Q)
}
/* make exponent odd - add 2P if even, P if odd */
t.copy(e)
let s=t.parity()
t.inc(1); t.norm(); let ns=t.parity(); mt.copy(t); mt.inc(1); mt.norm()
t.cmove(mt,s)
Q.cmove(self,ns)
C.copy(Q)
let nb=1+(t.nbits()+3)/4
/* convert exponent to signed 4-bit window */
for i in 0 ..< nb
{
w[i]=Int8(t.lastbits(5)-16)
t.dec(Int(w[i])); t.norm()
t.fshr(4)
}
w[nb]=Int8(t.lastbits(5))
P.copy(W[Int(w[nb]-1)/2])
for i in (0...nb-1).reversed()
{
Q.select(W,Int32(w[i]))
P.dbl()
P.dbl()
P.dbl()
P.dbl()
P.add(Q)
}
P.sub(C);
P.affine()
return P;
}
/* 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 mul4(_ Q:[ECP2],_ u:[BIG]) -> ECP2
{
var W=ECP2()
var P=ECP2()
var T=[ECP2]();
for _ in 0 ..< 8 {T.append(ECP2())}
var mt=BIG()
var t=[BIG]()
var w=[Int8](repeating: 0,count: CONFIG_BIG.NLEN*Int(CONFIG_BIG.BASEBITS)+1)
var s=[Int8](repeating: 0,count: CONFIG_BIG.NLEN*Int(CONFIG_BIG.BASEBITS)+1)
for i in 0 ..< 4
{
t.append(BIG(u[i]))
t[i].norm()
}
// precompute table
T[0].copy(Q[0]) // Q[0]
T[1].copy(T[0]); T[1].add(Q[1]) // Q[0]+Q[1]
T[2].copy(T[0]); T[2].add(Q[2]) // Q[0]+Q[2]
T[3].copy(T[1]); T[3].add(Q[2]) // Q[0]+Q[1]+Q[2]
T[4].copy(T[0]); T[4].add(Q[3]) // Q[0]+Q[3]
T[5].copy(T[1]); T[5].add(Q[3]) // Q[0]+Q[1]+Q[3]
T[6].copy(T[2]); T[6].add(Q[3]) // Q[0]+Q[2]+Q[3]
T[7].copy(T[3]); T[7].add(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(T,Int32(2*w[nb-1]+1));
for i in (0 ..< nb-1).reversed() {
P.dbl()
W.select(T,Int32(2*w[i]+s[i]))
P.add(W)
}
W.copy(P)
W.sub(Q[0])
P.cmove(W,pb)
P.affine()
return P
}
// needed for SOK
static func mapit(_ h:[UInt8]) -> ECP2
{
let q=BIG(ROM.Modulus)
var x=BIG.fromBytes(h)
let one=BIG(1)
var Q=ECP2()
x.mod(q);
while (true)
{
let X=FP2(one,x)
Q=ECP2(X)
if !Q.is_infinity() {break}
x.inc(1); x.norm()
}
// Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez
let Fra=BIG(ROM.Fra)
let Frb=BIG(ROM.Frb)
var X=FP2(Fra,Frb)
if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
X.inverse()
X.norm()
}
x=BIG(ROM.CURVE_Bnx)
if CONFIG_CURVE.CURVE_PAIRING_TYPE == CONFIG_CURVE.BN {
var T=Q.mul(x)
if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX {
T.neg()
}
var K=ECP2(); K.copy(T)
K.dbl(); K.add(T)
K.frob(X)
Q.frob(X); Q.frob(X); Q.frob(X)
Q.add(T); Q.add(K)
T.frob(X); T.frob(X)
Q.add(T)
}
if CONFIG_CURVE.CURVE_PAIRING_TYPE == CONFIG_CURVE.BLS {
var xQ=Q.mul(x);
var x2Q=xQ.mul(x);
if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX {
xQ.neg()
}
x2Q.sub(xQ)
x2Q.sub(Q)
xQ.sub(Q)
xQ.frob(X)
Q.dbl()
Q.frob(X)
Q.frob(X)
Q.add(x2Q)
Q.add(xQ)
}
Q.affine()
return Q
}
static public func generator() -> ECP2
{
return ECP2(FP2(BIG(ROM.CURVE_Pxa),BIG(ROM.CURVE_Pxb)),FP2(BIG(ROM.CURVE_Pya),BIG(ROM.CURVE_Pyb)))
}
}