From 618dae47a5837dbf19e2ddca047496bd475f7989 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 4 Sep 2020 02:13:53 +0300 Subject: [PATCH] implement binary EEA inversion --- crypto/bn256/cloudflare/bn256_test.go | 58 ++++++++++++ crypto/bn256/cloudflare/curve.go | 3 +- crypto/bn256/cloudflare/gfp.go | 128 ++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 1 deletion(-) diff --git a/crypto/bn256/cloudflare/bn256_test.go b/crypto/bn256/cloudflare/bn256_test.go index 0c8016d86c..73843e5cd0 100644 --- a/crypto/bn256/cloudflare/bn256_test.go +++ b/crypto/bn256/cloudflare/bn256_test.go @@ -92,6 +92,64 @@ func TestTripartiteDiffieHellman(t *testing.T) { } } +func TestBinaryEAA(t *testing.T) { + for i := 0; i < 10000; i++ { + _, Ga, err := RandomG1(rand.Reader) + if err != nil { + t.Fatal(err) + } + tmpLittleFermat := &gfP{} + tmpLittleFermat.Invert(&Ga.p.x) + + tmpBinaryEAA := &gfP{} + tmpBinaryEAA.EaaInvert(&Ga.p.x) + + eq := equals(tmpLittleFermat, tmpBinaryEAA) + if eq == false { + t.Fatalf("results of different inversion do not agree") + } + } +} + +func BenchmarkLittleFermatInversion(b *testing.B) { + el := gfP{0x0, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029} + + b.ResetTimer() + + tmp := &gfP{} + for i := 0; i < b.N; i++ { + tmp.Invert(&el) + } +} + +func BenchmarkBinaryEEAInversion(b *testing.B) { + el := gfP{0x0, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029} + + b.ResetTimer() + + tmp := &gfP{} + for i := 0; i < b.N; i++ { + tmp.EaaInvert(&el) + } +} + +func BenchmarkG1AddAndMakeAffine(b *testing.B) { + _, Ga, err := RandomG1(rand.Reader) + if err != nil { + b.Fatal(err) + } + _, Gb, err := RandomG1(rand.Reader) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + + for i := 0; i < b.N; i++ { + e := new(G1).Add(Ga, Gb) + e.p.MakeAffine() + } +} + func BenchmarkG1(b *testing.B) { x, _ := rand.Int(rand.Reader, Order) b.ResetTimer() diff --git a/crypto/bn256/cloudflare/curve.go b/crypto/bn256/cloudflare/curve.go index 18e9b38f3f..35e2d88e79 100644 --- a/crypto/bn256/cloudflare/curve.go +++ b/crypto/bn256/cloudflare/curve.go @@ -217,7 +217,8 @@ func (c *curvePoint) MakeAffine() { } zInv := &gfP{} - zInv.Invert(&c.z) + zInv.EaaInvert(&c.z) + // zInv.Invert(&c.z) t, zInv2 := &gfP{}, &gfP{} gfpMul(t, &c.y, zInv) diff --git a/crypto/bn256/cloudflare/gfp.go b/crypto/bn256/cloudflare/gfp.go index e8e84e7b3b..41b4f7f821 100644 --- a/crypto/bn256/cloudflare/gfp.go +++ b/crypto/bn256/cloudflare/gfp.go @@ -3,6 +3,7 @@ package bn256 import ( "errors" "fmt" + "math/bits" ) type gfP [4]uint64 @@ -79,3 +80,130 @@ func (e *gfP) Unmarshal(in []byte) error { func montEncode(c, a *gfP) { gfpMul(c, a, r2) } func montDecode(c, a *gfP) { gfpMul(c, a, &gfP{1}) } + +func isZero(a *gfP) bool { + return a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 +} + +func isEven(a *gfP) bool { + return bits.TrailingZeros64((a[0])) > 0 +} + +func div2(a *gfP) { + a[0] = a[0]>>1 | a[1]<<63 + a[1] = a[1]>>1 | a[2]<<63 + a[2] = a[2]>>1 | a[3]<<63 + a[3] = a[3] >> 1 +} + +func (e *gfP) addNocarry(f *gfP) { + carry := uint64(0) + e[0], carry = bits.Add64(e[0], f[0], carry) + e[1], carry = bits.Add64(e[1], f[1], carry) + e[2], carry = bits.Add64(e[2], f[2], carry) + e[3], _ = bits.Add64(e[3], f[3], carry) +} + +func (e *gfP) subNoborrow(f *gfP) { + borrow := uint64(0) + e[0], borrow = bits.Sub64(e[0], f[0], borrow) + e[1], borrow = bits.Sub64(e[1], f[1], borrow) + e[2], borrow = bits.Sub64(e[2], f[2], borrow) + e[3], _ = bits.Sub64(e[3], f[3], borrow) +} + +func gte(a, b *gfP) bool { + // subtract b from a. If no borrow occures then a >= b + borrow := uint64(0) + _, borrow = bits.Sub64(a[0], b[0], borrow) + _, borrow = bits.Sub64(a[1], b[1], borrow) + _, borrow = bits.Sub64(a[2], b[2], borrow) + _, borrow = bits.Sub64(a[3], b[3], borrow) + + return borrow == 0 +} + +func equals(a, b *gfP) bool { + return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] +} + +// Performs inversion of the field element using binary EEA. +// If element is zero (no inverse exists) then set `e` to zero +func (e *gfP) EaaInvert(f *gfP) { + if isZero(f) { + e.Set(&gfP{0, 0, 0, 0}) + return + } + + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + one := gfP{1, 0, 0, 0} + + u, b := gfP{}, gfP{} + u.Set(f) + b.Set(r2) + + v := gfP{p2[0], p2[1], p2[2], p2[3]} + c := gfP{0, 0, 0, 0} + modulus := gfP{p2[0], p2[1], p2[2], p2[3]} + + for { + if equals(&u, &one) || equals(&v, &one) { + break + } + + // while u is even + for { + if !isEven(&u) { + break + } + + div2(&u) + if isEven(&b) { + div2(&b) + } else { + // we will not overflow a modulus here, + // so we can use specialized function + // do perform addition without reduction + b.addNocarry(&modulus) + div2(&b) + } + } + + // while v is even + for { + if !isEven(&v) { + break + } + + div2(&v) + if isEven(&c) { + div2(&c) + } else { + // we will not overflow a modulus here, + // so we can use specialized function + // do perform addition without reduction + c.addNocarry(&modulus) + div2(&c) + } + } + + if gte(&v, &u) { + // v >= u + v.subNoborrow(&u) + gfpSub(&c, &c, &b) + } else { + // if v < u + u.subNoborrow(&v) + gfpSub(&b, &b, &c) + } + } + + if equals(&u, &one) { + e.Set(&b) + } else { + e.Set(&c) + } +}