From 41d1314ff3fd23cba2fad46c824a89ec456a6663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 20 Mar 2018 00:01:15 +0900 Subject: [PATCH] crypto/bn256/cloudflare: enable curve mul lattice optimization --- crypto/bn256/bn256_fuzz.go | 12 +++ crypto/bn256/cloudflare/curve.go | 21 +++-- crypto/bn256/cloudflare/lattice.go | 115 ++++++++++++++++++++++++ crypto/bn256/cloudflare/lattice_test.go | 29 ++++++ 4 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 crypto/bn256/cloudflare/lattice.go create mode 100644 crypto/bn256/cloudflare/lattice_test.go diff --git a/crypto/bn256/bn256_fuzz.go b/crypto/bn256/bn256_fuzz.go index bd00df7b6f..f360b0541f 100644 --- a/crypto/bn256/bn256_fuzz.go +++ b/crypto/bn256/bn256_fuzz.go @@ -14,6 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +// +build gofuzz + package bn256 import ( @@ -39,6 +41,8 @@ func FuzzAdd(data []byte) int { if (errc == nil) != (errg == nil) { panic("parse mismatch") + } else if errc != nil { + return 0 } // Ensure both libs can parse the second curve point yc := new(cloudflare.G1) @@ -49,6 +53,8 @@ func FuzzAdd(data []byte) int { if (errc == nil) != (errg == nil) { panic("parse mismatch") + } else if errc != nil { + return 0 } // Add the two points and ensure they result in the same output rc := new(cloudflare.G1) @@ -79,6 +85,8 @@ func FuzzMul(data []byte) int { if (errc == nil) != (errg == nil) { panic("parse mismatch") + } else if errc != nil { + return 0 } // Add the two points and ensure they result in the same output rc := new(cloudflare.G1) @@ -107,6 +115,8 @@ func FuzzPair(data []byte) int { if (errc == nil) != (errg == nil) { panic("parse mismatch") + } else if errc != nil { + return 0 } // Ensure both libs can parse the twist point tc := new(cloudflare.G2) @@ -117,6 +127,8 @@ func FuzzPair(data []byte) int { if (errc == nil) != (errg == nil) { panic("parse mismatch") + } else if errc != nil { + return 0 } // Pair the two points and ensure thet result in the same output if cloudflare.PairingCheck([]*cloudflare.G1{pc}, []*cloudflare.G2{tc}) != google.PairingCheck([]*google.G1{pg}, []*google.G2{tg}) { diff --git a/crypto/bn256/cloudflare/curve.go b/crypto/bn256/cloudflare/curve.go index b6aecc0a68..18e9b38f3f 100644 --- a/crypto/bn256/cloudflare/curve.go +++ b/crypto/bn256/cloudflare/curve.go @@ -183,15 +183,24 @@ func (c *curvePoint) Double(a *curvePoint) { } func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int) { - sum, t := &curvePoint{}, &curvePoint{} - sum.SetInfinity() + precomp := [1 << 2]*curvePoint{nil, {}, {}, {}} + precomp[1].Set(a) + precomp[2].Set(a) + gfpMul(&precomp[2].x, &precomp[2].x, xiTo2PSquaredMinus2Over3) + precomp[3].Add(precomp[1], precomp[2]) - for i := scalar.BitLen(); i >= 0; i-- { + multiScalar := curveLattice.Multi(scalar) + + sum := &curvePoint{} + sum.SetInfinity() + t := &curvePoint{} + + for i := len(multiScalar) - 1; i >= 0; i-- { t.Double(sum) - if scalar.Bit(i) != 0 { - sum.Add(t, a) - } else { + if multiScalar[i] == 0 { sum.Set(t) + } else { + sum.Add(t, precomp[multiScalar[i]]) } } c.Set(sum) diff --git a/crypto/bn256/cloudflare/lattice.go b/crypto/bn256/cloudflare/lattice.go new file mode 100644 index 0000000000..f9ace4d9fc --- /dev/null +++ b/crypto/bn256/cloudflare/lattice.go @@ -0,0 +1,115 @@ +package bn256 + +import ( + "math/big" +) + +var half = new(big.Int).Rsh(Order, 1) + +var curveLattice = &lattice{ + vectors: [][]*big.Int{ + {bigFromBase10("147946756881789319000765030803803410728"), bigFromBase10("147946756881789319010696353538189108491")}, + {bigFromBase10("147946756881789319020627676272574806254"), bigFromBase10("-147946756881789318990833708069417712965")}, + }, + inverse: []*big.Int{ + bigFromBase10("147946756881789318990833708069417712965"), + bigFromBase10("147946756881789319010696353538189108491"), + }, + det: bigFromBase10("43776485743678550444492811490514550177096728800832068687396408373151616991234"), +} + +var targetLattice = &lattice{ + vectors: [][]*big.Int{ + {bigFromBase10("9931322734385697761"), bigFromBase10("9931322734385697761"), bigFromBase10("9931322734385697763"), bigFromBase10("9931322734385697764")}, + {bigFromBase10("4965661367192848881"), bigFromBase10("4965661367192848881"), bigFromBase10("4965661367192848882"), bigFromBase10("-9931322734385697762")}, + {bigFromBase10("-9931322734385697762"), bigFromBase10("-4965661367192848881"), bigFromBase10("4965661367192848881"), bigFromBase10("-4965661367192848882")}, + {bigFromBase10("9931322734385697763"), bigFromBase10("-4965661367192848881"), bigFromBase10("-4965661367192848881"), bigFromBase10("-4965661367192848881")}, + }, + inverse: []*big.Int{ + bigFromBase10("734653495049373973658254490726798021314063399421879442165"), + bigFromBase10("147946756881789319000765030803803410728"), + bigFromBase10("-147946756881789319005730692170996259609"), + bigFromBase10("1469306990098747947464455738335385361643788813749140841702"), + }, + det: new(big.Int).Set(Order), +} + +type lattice struct { + vectors [][]*big.Int + inverse []*big.Int + det *big.Int +} + +// decompose takes a scalar mod Order as input and finds a short, positive decomposition of it wrt to the lattice basis. +func (l *lattice) decompose(k *big.Int) []*big.Int { + n := len(l.inverse) + + // Calculate closest vector in lattice to with Babai's rounding. + c := make([]*big.Int, n) + for i := 0; i < n; i++ { + c[i] = new(big.Int).Mul(k, l.inverse[i]) + round(c[i], l.det) + } + + // Transform vectors according to c and subtract . + out := make([]*big.Int, n) + temp := new(big.Int) + + for i := 0; i < n; i++ { + out[i] = new(big.Int) + + for j := 0; j < n; j++ { + temp.Mul(c[j], l.vectors[j][i]) + out[i].Add(out[i], temp) + } + + out[i].Neg(out[i]) + out[i].Add(out[i], l.vectors[0][i]).Add(out[i], l.vectors[0][i]) + } + out[0].Add(out[0], k) + + return out +} + +func (l *lattice) Precompute(add func(i, j uint)) { + n := uint(len(l.vectors)) + total := uint(1) << n + + for i := uint(0); i < n; i++ { + for j := uint(0); j < total; j++ { + if (j>>i)&1 == 1 { + add(i, j) + } + } + } +} + +func (l *lattice) Multi(scalar *big.Int) []uint8 { + decomp := l.decompose(scalar) + + maxLen := 0 + for _, x := range decomp { + if x.BitLen() > maxLen { + maxLen = x.BitLen() + } + } + + out := make([]uint8, maxLen) + for j, x := range decomp { + for i := 0; i < maxLen; i++ { + out[i] += uint8(x.Bit(i)) << uint(j) + } + } + + return out +} + +// round sets num to num/denom rounded to the nearest integer. +func round(num, denom *big.Int) { + r := new(big.Int) + num.DivMod(num, denom, r) + + if r.Cmp(half) == 1 { + num.Add(num, big.NewInt(1)) + } +} diff --git a/crypto/bn256/cloudflare/lattice_test.go b/crypto/bn256/cloudflare/lattice_test.go new file mode 100644 index 0000000000..4d52ad9b27 --- /dev/null +++ b/crypto/bn256/cloudflare/lattice_test.go @@ -0,0 +1,29 @@ +package bn256 + +import ( + "crypto/rand" + + "testing" +) + +func TestLatticeReduceCurve(t *testing.T) { + k, _ := rand.Int(rand.Reader, Order) + ks := curveLattice.decompose(k) + + if ks[0].BitLen() > 130 || ks[1].BitLen() > 130 { + t.Fatal("reduction too large") + } else if ks[0].Sign() < 0 || ks[1].Sign() < 0 { + t.Fatal("reduction must be positive") + } +} + +func TestLatticeReduceTarget(t *testing.T) { + k, _ := rand.Int(rand.Reader, Order) + ks := targetLattice.decompose(k) + + if ks[0].BitLen() > 66 || ks[1].BitLen() > 66 || ks[2].BitLen() > 66 || ks[3].BitLen() > 66 { + t.Fatal("reduction too large") + } else if ks[0].Sign() < 0 || ks[1].Sign() < 0 || ks[2].Sign() < 0 || ks[3].Sign() < 0 { + t.Fatal("reduction must be positive") + } +}