blob: f7d84f51d3d6f0207dc4bca0793c0e7869a12ff5 [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.
*/
//
// ecp.swift
//
//
// Created by Michael Scott on 30/06/2015.
// Copyright (c) 2015 Michael Scott. All rights reserved.
//
final class ECP {
private var x:FP
private var y:FP
private var z:FP
private var INF:Bool
/* Constructor - set to O */
init()
{
x=FP(0)
y=FP(0)
z=FP(1)
INF=true
}
/* test for O point-at-infinity */
func is_infinity() -> Bool
{
if (ROM.CURVETYPE==ROM.EDWARDS)
{
x.reduce(); y.reduce(); z.reduce()
return x.iszilch() && y.equals(z)
}
else {return INF}
}
/* Conditional swap of P and Q dependant on d */
private func cswap(Q: ECP,_ d:Int32)
{
x.cswap(Q.x,d);
if ROM.CURVETYPE != ROM.MONTGOMERY {y.cswap(Q.y,d)}
z.cswap(Q.z,d);
if (ROM.CURVETYPE != ROM.EDWARDS)
{
var bd:Bool
if d==0 {bd=false}
else {bd=true}
bd=bd && (INF != Q.INF)
INF = (INF != bd)
Q.INF = (Q.INF != bd)
}
}
/* Conditional move of Q to P dependant on d */
private func cmove(Q: ECP,_ d:Int32)
{
x.cmove(Q.x,d);
if ROM.CURVETYPE != ROM.MONTGOMERY {y.cmove(Q.y,d)}
z.cmove(Q.z,d);
if (ROM.CURVETYPE != ROM.EDWARDS)
{
var bd:Bool
if d==0 {bd=false}
else {bd=true}
INF != (INF != Q.INF) && bd;
}
}
/* return 1 if b==c, no branching */
private static func teq(b: Int32,_ c:Int32) -> Int32
{
var x=b^c
x-=1 // if x=0, x now -1
return ((x>>31)&1)
}
/* self=P */
func copy(P: ECP)
{
x.copy(P.x)
if ROM.CURVETYPE != ROM.MONTGOMERY {y.copy(P.y)}
z.copy(P.z)
INF=P.INF
}
/* self=-self */
func neg() {
if is_infinity() {return}
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
y.neg(); y.norm();
}
if (ROM.CURVETYPE==ROM.EDWARDS)
{
x.neg(); x.norm();
}
return;
}
/* Constant time select from pre-computed table */
private func select(W:[ECP],_ b:Int32)
{
let MP=ECP()
let m=b>>31
var babs=(b^m)-m
babs=(babs-1)/2
cmove(W[0],ECP.teq(babs,0)); // conditional move
cmove(W[1],ECP.teq(babs,1))
cmove(W[2],ECP.teq(babs,2))
cmove(W[3],ECP.teq(babs,3))
cmove(W[4],ECP.teq(babs,4))
cmove(W[5],ECP.teq(babs,5))
cmove(W[6],ECP.teq(babs,6))
cmove(W[7],ECP.teq(babs,7))
MP.copy(self)
MP.neg()
cmove(MP,(m&1))
}
/* Test P == Q */
func equals(Q: ECP) -> Bool
{
if (is_infinity() && Q.is_infinity()) {return true}
if (is_infinity() || Q.is_infinity()) {return false}
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
let zs2=FP(z); zs2.sqr()
let zo2=FP(Q.z); zo2.sqr()
let zs3=FP(zs2); zs3.mul(z)
let zo3=FP(zo2); zo3.mul(Q.z)
zs2.mul(Q.x)
zo2.mul(x)
if !zs2.equals(zo2) {return false}
zs3.mul(Q.y)
zo3.mul(y)
if !zs3.equals(zo3) {return false}
}
else
{
let a=FP(0)
let b=FP(0)
a.copy(x); a.mul(Q.z); a.reduce()
b.copy(Q.x); b.mul(z); b.reduce()
if !a.equals(b) {return false}
if ROM.CURVETYPE==ROM.EDWARDS
{
a.copy(y); a.mul(Q.z); a.reduce()
b.copy(Q.y); b.mul(z); b.reduce()
if !a.equals(b) {return false}
}
}
return true
}
/* set self=O */
func inf()
{
INF=true;
x.zero()
y.one()
z.one()
}
/* Calculate RHS of curve equation */
static func RHS(x: FP) -> FP
{
x.norm();
let r=FP(x);
r.sqr();
if ROM.CURVETYPE==ROM.WEIERSTRASS
{ // x^3+Ax+B
let b=FP(BIG(ROM.CURVE_B))
r.mul(x)
if (ROM.CURVE_A == -3)
{
let cx=FP(x)
cx.imul(3)
cx.neg(); cx.norm()
r.add(cx)
}
r.add(b);
}
if (ROM.CURVETYPE==ROM.EDWARDS)
{ // (Ax^2-1)/(Bx^2-1)
let b=FP(BIG(ROM.CURVE_B))
let one=FP(1);
b.mul(r);
b.sub(one);
if ROM.CURVE_A == -1 {r.neg()}
r.sub(one)
b.inverse()
r.mul(b);
}
if ROM.CURVETYPE==ROM.MONTGOMERY
{ // x^3+Ax^2+x
let x3=FP(0)
x3.copy(r);
x3.mul(x);
r.imul(ROM.CURVE_A);
r.add(x3);
r.add(x);
}
r.reduce();
return r;
}
/* set (x,y) from two BIGs */
init(_ ix: BIG,_ iy: BIG)
{
x=FP(ix)
y=FP(iy)
z=FP(1)
INF=true
let rhs=ECP.RHS(x);
if ROM.CURVETYPE==ROM.MONTGOMERY
{
if rhs.jacobi()==1 {INF=false}
else {inf()}
}
else
{
let y2=FP(y)
y2.sqr()
if y2.equals(rhs) {INF=false}
else {inf()}
}
}
/* set (x,y) from BIG and a bit */
init(_ ix: BIG,_ s:Int32)
{
x=FP(ix)
let rhs=ECP.RHS(x)
y=FP(0)
z=FP(1)
INF=true
if rhs.jacobi()==1
{
let ny=rhs.sqrt()
if (ny.redc().parity() != s) {ny.neg()}
y.copy(ny)
INF=false;
}
else {inf()}
}
/* set from x - calculate y from curve equation */
init(_ ix:BIG)
{
x=FP(ix)
let rhs=ECP.RHS(x)
y=FP(0)
z=FP(1)
if rhs.jacobi()==1
{
if ROM.CURVETYPE != ROM.MONTGOMERY {y.copy(rhs.sqrt())}
INF=false;
}
else {INF=true}
}
/* set to affine - from (x,y,z) to (x,y) */
func affine()
{
if is_infinity() {return}
let one=FP(1)
if (z.equals(one)) {return}
z.inverse()
if ROM.CURVETYPE==ROM.WEIERSTRASS
{
let z2=FP(z)
z2.sqr()
x.mul(z2); x.reduce()
y.mul(z2)
y.mul(z); y.reduce()
}
if ROM.CURVETYPE==ROM.EDWARDS
{
x.mul(z); x.reduce()
y.mul(z); y.reduce()
}
if ROM.CURVETYPE==ROM.MONTGOMERY
{
x.mul(z); x.reduce()
}
z.copy(one)
}
/* extract x as a BIG */
func getX() -> BIG
{
affine()
return x.redc()
}
/* extract y as a BIG */
func getY() -> BIG
{
affine();
return y.redc();
}
/* get sign of Y */
func getS() -> Int32
{
affine()
let y=getY()
return y.parity()
}
/* extract x as an FP */
func getx() -> FP
{
return x;
}
/* extract y as an FP */
func gety() -> FP
{
return y;
}
/* extract z as an FP */
func getz() -> FP
{
return z;
}
/* convert to byte array */
func toBytes(inout b:[UInt8])
{
let RM=Int(ROM.MODBYTES)
var t=[UInt8](count:RM,repeatedValue:0)
if ROM.CURVETYPE != ROM.MONTGOMERY {b[0]=0x04}
else {b[0]=0x02}
affine()
x.redc().toBytes(&t)
for var i=0;i<RM;i++ {b[i+1]=t[i]}
if ROM.CURVETYPE != ROM.MONTGOMERY
{
y.redc().toBytes(&t);
for var i=0;i<RM;i++ {b[i+RM+1]=t[i]}
}
}
/* convert from byte array to point */
static func fromBytes(b: [UInt8]) -> ECP
{
let RM=Int(ROM.MODBYTES)
var t=[UInt8](count:RM,repeatedValue:0)
let p=BIG(ROM.Modulus);
for var i=0;i<RM;i++ {t[i]=b[i+1]}
let px=BIG.fromBytes(t)
if BIG.comp(px,p)>=0 {return ECP()}
if (b[0]==0x04)
{
for var i=0;i<RM;i++ {t[i]=b[i+RM+1]}
let py=BIG.fromBytes(t)
if BIG.comp(py,p)>=0 {return ECP()}
return ECP(px,py)
}
else {return ECP(px)}
}
/* convert to hex string */
func toString() -> String
{
if is_infinity() {return "infinity"}
affine();
if ROM.CURVETYPE==ROM.MONTGOMERY {return "("+x.redc().toString()+")"}
else {return "("+x.redc().toString()+","+y.redc().toString()+")"}
}
/* self*=2 */
func dbl()
{
if (ROM.CURVETYPE==ROM.WEIERSTRASS)
{
if INF {return}
if y.iszilch()
{
inf()
return
}
let w1=FP(x)
let w6=FP(z)
let w2=FP(0)
let w3=FP(x)
let w8=FP(x)
if (ROM.CURVE_A == -3)
{
w6.sqr()
w1.copy(w6)
w1.neg()
w3.add(w1)
w8.add(w6)
w3.mul(w8)
w8.copy(w3)
w8.imul(3)
}
else
{
w1.sqr()
w8.copy(w1)
w8.imul(3)
}
w2.copy(y); w2.sqr()
w3.copy(x); w3.mul(w2)
w3.imul(4)
w1.copy(w3); w1.neg()
w1.norm()
x.copy(w8); x.sqr()
x.add(w1)
x.add(w1)
x.norm()
z.mul(y)
z.add(z)
w2.add(w2)
w2.sqr()
w2.add(w2)
w3.sub(x)
y.copy(w8); y.mul(w3)
//w2.norm();
y.sub(w2)
y.norm()
z.norm()
}
if ROM.CURVETYPE==ROM.EDWARDS
{
let C=FP(x)
let D=FP(y)
let H=FP(z)
let J=FP(0)
x.mul(y); x.add(x)
C.sqr()
D.sqr()
if ROM.CURVE_A == -1 {C.neg()}
y.copy(C); y.add(D)
y.norm()
H.sqr(); H.add(H)
z.copy(y)
J.copy(y); J.sub(H)
x.mul(J)
C.sub(D)
y.mul(C)
z.mul(J)
x.norm();
y.norm();
z.norm();
}
if ROM.CURVETYPE==ROM.MONTGOMERY
{
let A=FP(x)
let B=FP(x);
let AA=FP(0);
let BB=FP(0);
let C=FP(0);
if INF {return}
A.add(z)
AA.copy(A); AA.sqr()
B.sub(z)
BB.copy(B); BB.sqr()
C.copy(AA); C.sub(BB)
//C.norm();
x.copy(AA); x.mul(BB)
A.copy(C); A.imul((ROM.CURVE_A+2)/4)
BB.add(A)
z.copy(BB); z.mul(C)
x.norm()
z.norm()
}
return
}
/* self+=Q */
func add(Q:ECP)
{
if ROM.CURVETYPE==ROM.WEIERSTRASS
{
if (INF)
{
copy(Q)
return
}
if Q.INF {return}
var aff=false;
let one=FP(1);
if Q.z.equals(one) {aff=true}
var A:FP
var C:FP
let B=FP(z)
let D=FP(z)
if (!aff)
{
A=FP(Q.z)
C=FP(Q.z)
A.sqr(); B.sqr()
C.mul(A); D.mul(B)
A.mul(x)
C.mul(y)
}
else
{
A=FP(x)
C=FP(y)
B.sqr()
D.mul(B)
}
B.mul(Q.x); B.sub(A)
D.mul(Q.y); D.sub(C)
if B.iszilch()
{
if (D.iszilch())
{
dbl()
return
}
else
{
INF=true
return
}
}
if !aff {z.mul(Q.z)}
z.mul(B);
let e=FP(B); e.sqr()
B.mul(e)
A.mul(e)
e.copy(A)
e.add(A); e.add(B)
x.copy(D); x.sqr(); x.sub(e)
A.sub(x)
y.copy(A); y.mul(D)
C.mul(B); y.sub(C)
x.norm()
y.norm()
z.norm()
}
if ROM.CURVETYPE==ROM.EDWARDS
{
let b=FP(BIG(ROM.CURVE_B))
let A=FP(z)
let B=FP(0)
let C=FP(x)
let D=FP(y)
let E=FP(0)
let F=FP(0)
let G=FP(0)
A.mul(Q.z)
B.copy(A); B.sqr()
C.mul(Q.x)
D.mul(Q.y)
E.copy(C); E.mul(D); E.mul(b)
F.copy(B); F.sub(E)
G.copy(B); G.add(E)
C.add(D)
if ROM.CURVE_A==1
{
E.copy(D); D.sub(C)
}
B.copy(x); B.add(y)
D.copy(Q.x); D.add(Q.y)
B.mul(D)
B.sub(C)
B.mul(F)
x.copy(A); x.mul(B)
if ROM.CURVE_A==1
{
C.copy(E); C.mul(G)
}
if ROM.CURVE_A == -1
{
C.mul(G)
}
y.copy(A); y.mul(C)
z.copy(F); z.mul(G)
x.norm(); y.norm(); z.norm()
}
return;
}
/* Differential Add for Montgomery curves. self+=Q where W is self-Q and is affine. */
func dadd(Q:ECP,_ W:ECP)
{
let A=FP(x)
let B=FP(x)
let C=FP(Q.x)
let D=FP(Q.x)
let DA=FP(0)
let CB=FP(0)
A.add(z)
B.sub(z)
C.add(Q.z)
D.sub(Q.z)
DA.copy(D); DA.mul(A)
CB.copy(C); CB.mul(B)
A.copy(DA); A.add(CB); A.sqr()
B.copy(DA); B.sub(CB); B.sqr()
x.copy(A)
z.copy(W.x); z.mul(B)
if z.iszilch() {inf()}
else {INF=false}
x.norm()
}
/* this-=Q */
func sub(Q:ECP)
{
Q.neg()
add(Q)
Q.neg()
}
static func multiaffine(m: Int,_ P:[ECP])
{
let t1=FP(0)
let t2=FP(0)
var work=[FP]()
for var i=0;i<m;i++
{work.append(FP(0))}
work[0].one()
work[1].copy(P[0].z)
for var i=2;i<m;i++
{
work[i].copy(work[i-1])
work[i].mul(P[i-1].z)
}
t1.copy(work[m-1]);
t1.mul(P[m-1].z);
t1.inverse();
t2.copy(P[m-1].z);
work[m-1].mul(t1);
for var i=m-2;;i--
{
if i==0
{
work[0].copy(t1)
work[0].mul(t2)
break
}
work[i].mul(t2);
work[i].mul(t1);
t2.mul(P[i].z);
}
/* now work[] contains inverses of all Z coordinates */
for var i=0;i<m;i++
{
P[i].z.one();
t1.copy(work[i]);
t1.sqr();
P[i].x.mul(t1);
t1.mul(work[i]);
P[i].y.mul(t1);
}
}
/* constant time multiply by small integer of length bts - use ladder */
func pinmul(e:Int32,_ bts:Int32) -> ECP
{
if ROM.CURVETYPE==ROM.MONTGOMERY
{return self.mul(BIG(e))}
else
{
let P=ECP()
let R0=ECP()
let R1=ECP(); R1.copy(self)
for var i=bts-1;i>=0;i--
{
let b=(e>>i)&1;
P.copy(R1);
P.add(R0);
R0.cswap(R1,b);
R1.copy(P);
R0.dbl();
R0.cswap(R1,b);
}
P.copy(R0);
P.affine();
return P;
}
}
/* return e.self */
func mul(e:BIG) -> ECP
{
if (e.iszilch() || is_infinity()) {return ECP()}
let P=ECP()
if ROM.CURVETYPE==ROM.MONTGOMERY
{
/* use Ladder */
let D=ECP()
let R0=ECP(); R0.copy(self)
let R1=ECP(); R1.copy(self)
R1.dbl();
D.copy(self); D.affine();
let nb=e.nbits();
for var i=nb-2;i>=0;i--
{
let b=e.bit(i)
//print("\(b)")
P.copy(R1)
P.dadd(R0,D)
R0.cswap(R1,b)
R1.copy(P)
R0.dbl()
R0.cswap(R1,b)
}
P.copy(R0)
}
else
{
// fixed size windows
let mt=BIG()
let t=BIG()
let Q=ECP()
let C=ECP()
var W=[ECP]()
let n=1+(ROM.NLEN*Int(ROM.BASEBITS)+3)/4
var w=[Int8](count:n,repeatedValue:0)
affine();
// precompute table
Q.copy(self)
Q.dbl()
W.append(ECP())
W[0].copy(self)
for var i=1;i<8;i++
{
W.append(ECP())
W[i].copy(W[i-1])
W[i].add(Q)
}
// convert the table to affine
if ROM.CURVETYPE==ROM.WEIERSTRASS
{ECP.multiaffine(8,W)}
// 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 var i=0;i<nb;i++
{
w[i]=Int8(t.lastbits(5)-16);
t.dec(Int32(w[i])); t.norm();
t.fshr(4);
}
w[nb]=Int8(t.lastbits(5))
P.copy(W[Int((w[nb])-1)/2]);
for var i=nb-1;i>=0;i--
{
Q.select(W,Int32(w[i]));
P.dbl();
P.dbl();
P.dbl();
P.dbl();
P.add(Q);
}
P.sub(C); /* apply correction */
}
P.affine();
return P;
}
/* Return e.this+f.Q */
func mul2(e:BIG,_ Q:ECP,_ f:BIG) -> ECP
{
let te=BIG()
let tf=BIG()
let mt=BIG()
let S=ECP()
let T=ECP()
let C=ECP()
var W=[ECP]()
let n=1+(ROM.NLEN*Int(ROM.BASEBITS)+1)/2
var w=[Int8](count:n,repeatedValue:0);
affine();
Q.affine();
te.copy(e);
tf.copy(f);
// precompute table
for var i=0;i<8;i++ {W.append(ECP())}
W[1].copy(self); W[1].sub(Q)
W[2].copy(self); W[2].add(Q)
S.copy(Q); S.dbl();
W[0].copy(W[1]); W[0].sub(S)
W[3].copy(W[2]); W[3].add(S)
T.copy(self); T.dbl()
W[5].copy(W[1]); W[5].add(T)
W[6].copy(W[2]); W[6].add(T)
W[4].copy(W[5]); W[4].sub(S)
W[7].copy(W[6]); W[7].add(S)
// convert the table to affine
if ROM.CURVETYPE==ROM.WEIERSTRASS
{ECP.multiaffine(8,W)}
// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
var s=te.parity()
te.inc(1); te.norm(); var ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm()
te.cmove(mt,s)
T.cmove(self,ns)
C.copy(T)
s=tf.parity()
tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm()
tf.cmove(mt,s)
S.cmove(Q,ns)
C.add(S)
mt.copy(te); mt.add(tf); mt.norm()
let nb=1+(mt.nbits()+1)/2
// convert exponent to signed 2-bit window
for var i=0;i<nb;i++
{
let a=(te.lastbits(3)-4);
te.dec(a); te.norm();
te.fshr(2);
let b=(tf.lastbits(3)-4);
tf.dec(b); tf.norm();
tf.fshr(2);
w[i]=Int8(4*a+b);
}
w[nb]=Int8(4*te.lastbits(3)+tf.lastbits(3));
S.copy(W[(w[nb]-1)/2]);
for var i=nb-1;i>=0;i--
{
T.select(W,Int32(w[i]));
S.dbl();
S.dbl();
S.add(T);
}
S.sub(C); /* apply correction */
S.affine();
return S;
}
}