Implement GpF2 and TwistPoint functionality.

- Ported over the GpF2 and TwistPoint functionality form the derohe project to Kotlin.
This commit is contained in:
Severiano Jaramillo 2024-05-13 21:56:59 -07:00
parent e42a2acc66
commit ad1d535752
2 changed files with 389 additions and 0 deletions

View file

@ -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 as a quadratic extension of the base field where =-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
}
}
}

View file

@ -0,0 +1,221 @@
@file:OptIn(ExperimentalUnsignedTypes::class)
package net.agorise.library.crypto.bn256
import java.math.BigInteger
/**
* twistPoint implements the elliptic curve =+3/ξ over GF(). Points are
* kept in Jacobian form and t= when valid. The group G₂ is the set of
* n-torsion points of this curve over GF() (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))
)
}
}