Implement GpF2 and TwistPoint functionality.
- Ported over the GpF2 and TwistPoint functionality form the derohe project to Kotlin.
This commit is contained in:
parent
e42a2acc66
commit
ad1d535752
2 changed files with 389 additions and 0 deletions
|
@ -0,0 +1,168 @@
|
|||
package net.agorise.library.crypto.bn256
|
||||
|
||||
/**
|
||||
* For details of the algorithms used, see "Multiplication and Squaring on
|
||||
* Pairing-Friendly Fields, Devegili et al. http://eprint.iacr.org/2006/471.pdf.
|
||||
* GfP2 implements a field of size p² as a quadratic extension of the base field where i²=-1.
|
||||
*
|
||||
* Ported over from https://github.com/deroproject/derohe/blob/main/cryptography/bn256/gfp2.go
|
||||
*/
|
||||
class GfP2(private var x: GfP, private var y: GfP) {
|
||||
|
||||
constructor() : this(GfP(0UL), GfP(0UL))
|
||||
|
||||
override fun toString(): String {
|
||||
return "(${this.x}, ${this.y})"
|
||||
}
|
||||
|
||||
fun set(a: GfP2): GfP2 {
|
||||
this.x.set(a.x)
|
||||
this.y.set(a.y)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setZero(): GfP2 {
|
||||
this.x = GfP(0UL)
|
||||
this.y = GfP(0UL)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setOne(): GfP2 {
|
||||
this.x = GfP(0UL)
|
||||
this.y = GfP.newGfP(1)
|
||||
return this
|
||||
}
|
||||
|
||||
fun isZero(): Boolean {
|
||||
val zero = GfP(0UL)
|
||||
return this.x == zero && this.y == zero
|
||||
}
|
||||
|
||||
fun isOne(): Boolean {
|
||||
val zero = GfP(0UL)
|
||||
val one = GfP.newGfP(1)
|
||||
return this.x == zero && this.y == one
|
||||
}
|
||||
|
||||
fun conjugate(a: GfP2): GfP2 {
|
||||
this.y.set(a.y)
|
||||
gfpNeg(this.x, a.x)
|
||||
return this
|
||||
}
|
||||
|
||||
fun neg(a: GfP2): GfP2 {
|
||||
gfpNeg(this.x, a.x)
|
||||
gfpNeg(this.y, a.y)
|
||||
return this
|
||||
}
|
||||
|
||||
fun add(a: GfP2, b: GfP2): GfP2 {
|
||||
gfpAdd(this.x, a.x, b.x)
|
||||
gfpAdd(this.y, a.y, b.y)
|
||||
return this
|
||||
}
|
||||
|
||||
fun sub(a: GfP2, b: GfP2): GfP2 {
|
||||
gfpSub(this.x, a.x, b.x)
|
||||
gfpSub(this.y, a.y, b.y)
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* See "Multiplication and Squaring in Pairing-Friendly Fields",
|
||||
* http://eprint.iacr.org/2006/471.pdf
|
||||
*/
|
||||
fun mul(a: GfP2, b: GfP2): GfP2 {
|
||||
val tx = GfP()
|
||||
val t = GfP()
|
||||
gfpMul(tx, a.x, b.y)
|
||||
gfpMul(t, b.x, a.y)
|
||||
gfpAdd(tx, tx, t)
|
||||
|
||||
val ty = GfP()
|
||||
gfpMul(ty, a.y, b.y)
|
||||
gfpMul(t, a.x, b.x)
|
||||
gfpSub(ty, ty, t)
|
||||
|
||||
this.x.set(tx)
|
||||
this.y.set(ty)
|
||||
return this
|
||||
}
|
||||
|
||||
fun mulScalar(a: GfP2, b: GfP): GfP2 {
|
||||
gfpMul(this.x, a.x, b)
|
||||
gfpMul(this.y, a.y, b)
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets e=ξa where ξ=i+9 and then returns e.
|
||||
*/
|
||||
fun mulXi(a: GfP2): GfP2 {
|
||||
// (xi+y)(i+9) = (9x+y)i+(9y-x)
|
||||
val tx = GfP()
|
||||
gfpAdd(tx, a.x, a.x)
|
||||
gfpAdd(tx, tx, tx)
|
||||
gfpAdd(tx, tx, tx)
|
||||
gfpAdd(tx, tx, a.x)
|
||||
|
||||
gfpAdd(tx, tx, a.y)
|
||||
|
||||
val ty = GfP()
|
||||
gfpAdd(ty, a.y, a.y)
|
||||
gfpAdd(ty, ty, ty)
|
||||
gfpAdd(ty, ty, ty)
|
||||
gfpAdd(ty, ty, a.y)
|
||||
|
||||
gfpSub(ty, ty, a.x)
|
||||
|
||||
this.x.set(tx)
|
||||
this.y.set(ty)
|
||||
return this
|
||||
}
|
||||
|
||||
fun square(a: GfP2): GfP2 {
|
||||
// Complex squaring algorithm: (xi+y)² = (x+y)(y-x) + 2*i*x*y
|
||||
val tx = GfP()
|
||||
val ty = GfP()
|
||||
gfpSub(tx, a.y, a.x)
|
||||
gfpAdd(ty, a.x, a.y)
|
||||
gfpMul(ty, tx, ty)
|
||||
|
||||
gfpMul(tx, a.x, a.y)
|
||||
gfpAdd(tx, tx, tx)
|
||||
|
||||
this.x.set(tx)
|
||||
this.y.set(ty)
|
||||
return this
|
||||
}
|
||||
|
||||
fun invert(a: GfP2): GfP2 {
|
||||
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
|
||||
// ftp://136.206.11.249/pub/crypto/pairings.pdf
|
||||
val t1 = GfP()
|
||||
val t2 = GfP()
|
||||
gfpMul(t1, a.x, a.x)
|
||||
gfpMul(t2, a.y, a.y)
|
||||
gfpAdd(t1, t1, t2)
|
||||
|
||||
val inv = GfP()
|
||||
inv.invert(t1)
|
||||
|
||||
gfpNeg(t1, a.x)
|
||||
|
||||
gfpMul(this.x, t1, inv)
|
||||
gfpMul(this.y, a.y, inv)
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun decode(input: GfP2): GfP2 {
|
||||
val output = GfP2(GfP(), GfP())
|
||||
montDecode(output.x, input.x)
|
||||
montDecode(output.y, input.y)
|
||||
return output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
@file:OptIn(ExperimentalUnsignedTypes::class)
|
||||
|
||||
package net.agorise.library.crypto.bn256
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
* twistPoint implements the elliptic curve y²=x³+3/ξ over GF(p²). Points are
|
||||
* kept in Jacobian form and t=z² when valid. The group G₂ is the set of
|
||||
* n-torsion points of this curve over GF(p²) (where n = Order)
|
||||
*
|
||||
* Ported over from https://github.com/deroproject/derohe/blob/main/cryptography/bn256/twist.go
|
||||
*/
|
||||
class TwistPoint(
|
||||
private var x: GfP2,
|
||||
private var y: GfP2,
|
||||
private var z: GfP2,
|
||||
private var t: GfP2,
|
||||
) {
|
||||
|
||||
constructor() : this(GfP2(), GfP2(), GfP2(), GfP2())
|
||||
|
||||
override fun toString(): String {
|
||||
makeAffine()
|
||||
val xDecoded = GfP2.decode(this.x)
|
||||
val yDecoded = GfP2.decode(this.y)
|
||||
return "($xDecoded, $yDecoded)"
|
||||
}
|
||||
|
||||
fun set(a: TwistPoint) {
|
||||
this.x.set(a.x)
|
||||
this.y.set(a.y)
|
||||
this.z.set(a.z)
|
||||
this.t.set(a.t)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true iff c is on the curve.
|
||||
*/
|
||||
fun isOnCurve(): Boolean {
|
||||
makeAffine()
|
||||
if (isInfinity()) {
|
||||
return true
|
||||
}
|
||||
|
||||
val y2 = GfP2()
|
||||
val x3 = GfP2()
|
||||
y2.square(this.y)
|
||||
x3.square(this.x).mul(x3, this.x).add(x3, twistB)
|
||||
|
||||
if (y2 != x3) {
|
||||
return false
|
||||
}
|
||||
|
||||
val cneg = TwistPoint()
|
||||
cneg.mul(this, Constants.Order)
|
||||
return cneg.z.isZero()
|
||||
}
|
||||
|
||||
fun setInfinity() {
|
||||
this.x.setZero()
|
||||
this.y.setOne()
|
||||
this.z.setZero()
|
||||
this.t.setZero()
|
||||
}
|
||||
|
||||
fun isInfinity(): Boolean {
|
||||
return this.z.isZero()
|
||||
}
|
||||
|
||||
fun add(a: TwistPoint, b: TwistPoint) {
|
||||
// For additional comments, see the same function in CurvePoint.kt
|
||||
if (a.isInfinity()) {
|
||||
set(b)
|
||||
return
|
||||
}
|
||||
if (b.isInfinity()) {
|
||||
set(a)
|
||||
return
|
||||
}
|
||||
|
||||
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
|
||||
val z12 = GfP2().square(a.z)
|
||||
val z22 = GfP2().square(b.z)
|
||||
val u1 = GfP2().mul(a.x, z22)
|
||||
val u2 = GfP2().mul(b.x, z12)
|
||||
|
||||
val t = GfP2().mul(b.z, z22)
|
||||
val s1 = GfP2().mul(a.y, t)
|
||||
|
||||
t.mul(a.z, z12)
|
||||
val s2 = GfP2().mul(b.y, t)
|
||||
|
||||
val h = GfP2().sub(u2, u1)
|
||||
val xEqual = h.isZero()
|
||||
|
||||
t.add(h, h)
|
||||
val i = GfP2().square(t)
|
||||
val j = GfP2().mul(h, i)
|
||||
|
||||
t.sub(s2, s1)
|
||||
val yEqual = t.isZero()
|
||||
if (xEqual && yEqual) {
|
||||
double(a)
|
||||
return
|
||||
}
|
||||
val r = GfP2().add(t, t)
|
||||
|
||||
val v = GfP2().mul(u1, i)
|
||||
|
||||
val t4 = GfP2().square(r)
|
||||
t.add(v, v)
|
||||
val t6 = GfP2().sub(t4, j)
|
||||
this.x.sub(t6, t)
|
||||
|
||||
t.sub(v, this.x) // t7
|
||||
t4.mul(s1, j) // t8
|
||||
t6.add(t4, t4) // t9
|
||||
t4.mul(r, t) // t10
|
||||
this.y.sub(t4, t6)
|
||||
|
||||
t.add(a.z, b.z) // t11
|
||||
t4.square(t) // t12
|
||||
t.sub(t4, z12) // t13
|
||||
t4.sub(t, z22) // t14
|
||||
this.z.mul(t4, h)
|
||||
}
|
||||
|
||||
fun double(a: TwistPoint) {
|
||||
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
|
||||
val A = GfP2().square(a.x)
|
||||
val B = GfP2().square(a.y)
|
||||
val C = GfP2().square(B)
|
||||
|
||||
val t = GfP2().add(a.x, B)
|
||||
val t2 = GfP2().square(t)
|
||||
t.sub(t2, A)
|
||||
t2.sub(t, C)
|
||||
val d = GfP2().add(t2, t2)
|
||||
t.add(A, A)
|
||||
val e = GfP2().add(t, A)
|
||||
val f = GfP2().square(e)
|
||||
|
||||
t.add(d, d)
|
||||
this.x.sub(f, t)
|
||||
|
||||
this.z.mul(a.y, a.z)
|
||||
this.z.add(this.z, this.z)
|
||||
|
||||
t.add(C, C)
|
||||
t2.add(t, t)
|
||||
t.add(t2, t2)
|
||||
this.y.sub(d, this.x)
|
||||
t2.mul(e, this.y)
|
||||
this.y.sub(t2, t)
|
||||
}
|
||||
|
||||
fun mul(a: TwistPoint, scalar: BigInteger) {
|
||||
val sum = TwistPoint()
|
||||
val t = TwistPoint()
|
||||
|
||||
for (i in scalar.bitLength() downTo 0) {
|
||||
t.double(sum)
|
||||
if (scalar.testBit(i)) {
|
||||
sum.add(t, a)
|
||||
} else {
|
||||
sum.set(t)
|
||||
}
|
||||
}
|
||||
|
||||
set(sum)
|
||||
}
|
||||
|
||||
fun makeAffine() {
|
||||
if (this.z.isOne()) {
|
||||
return
|
||||
} else if (this.z.isZero()) {
|
||||
this.x.setZero()
|
||||
this.y.setOne()
|
||||
this.t.setZero()
|
||||
return
|
||||
}
|
||||
|
||||
val zInv = GfP2().invert(this.z)
|
||||
val t = GfP2().mul(this.y, zInv)
|
||||
val zInv2 = GfP2().square(zInv)
|
||||
this.y.mul(t, zInv2)
|
||||
t.mul(this.x, zInv2)
|
||||
this.x.set(t)
|
||||
this.z.setOne()
|
||||
this.t.setOne()
|
||||
}
|
||||
|
||||
fun neg(a: TwistPoint) {
|
||||
this.x.set(a.x)
|
||||
this.y.neg(a.y)
|
||||
this.z.set(a.z)
|
||||
this.t.setZero()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val twistB = GfP2(
|
||||
GfP(ulongArrayOf(0x38e7ecccd1dcff67UL, 0x65f0b37d93ce0d3eUL, 0xd749d0dd22ac00aaUL, 0x0141b9ce4a688d4dUL)),
|
||||
GfP(ulongArrayOf(0x3bf938e377b802a8UL, 0x020b1b273633535dUL, 0x26b7edf049755260UL, 0x2514c6324384a86dUL))
|
||||
)
|
||||
|
||||
val twistGen = TwistPoint(
|
||||
GfP2(
|
||||
GfP(ulongArrayOf(0xafb4737da84c6140UL, 0x6043dd5a5802d8c4UL, 0x09e950fc52a02f86UL, 0x14fef0833aea7b6bUL)),
|
||||
GfP(ulongArrayOf(0x8e83b5d102bc2026UL, 0xdceb1935497b0172UL, 0xfbb8264797811adfUL, 0x19573841af96503bUL))
|
||||
),
|
||||
GfP2(
|
||||
GfP(ulongArrayOf(0x64095b56c71856eeUL, 0xdc57f922327d3cbbUL, 0x55f935be33351076UL, 0x0da4a0e693fd6482UL)),
|
||||
GfP(ulongArrayOf(0x619dfa9d886be9f6UL, 0xfe7fd297f59e9b78UL, 0xff9e1a62231b7dfeUL, 0x28fd7eebae9e4206UL))
|
||||
),
|
||||
GfP2(GfP.newGfP(0), GfP.newGfP(1)),
|
||||
GfP2(GfP.newGfP(0), GfP.newGfP(1))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue