From e8f8fcd199a4069d0704c0b683c45683f9c4eda3 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 8 Jul 2026 06:36:58 +0200 Subject: [PATCH 1/2] replace kilic with gnarc Signed-off-by: Angelo De Caro --- Makefile | 2 +- README.md | 28 +- driver/gurvy/custom.go | 153 ++++++++-- driver/kilic/bls12-381.go | 510 --------------------------------- driver/kilic/custom.go | 353 ----------------------- driver/kilic/custom_amd64.go | 30 -- driver/kilic/custom_generic.go | 175 ----------- go.mod | 3 +- go.sum | 7 - golden_test.go | 86 ++++++ math.go | 52 ++-- math_test.go | 34 ++- perf_test.go | 203 ------------- 13 files changed, 278 insertions(+), 1358 deletions(-) delete mode 100644 driver/kilic/bls12-381.go delete mode 100644 driver/kilic/custom.go delete mode 100644 driver/kilic/custom_amd64.go delete mode 100644 driver/kilic/custom_generic.go create mode 100644 golden_test.go diff --git a/Makefile b/Makefile index 2a6b380..c49f245 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ lint-auto-fix: # install golangci-lint install-linter-tool: @echo "Installing golangci Linter" - @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(HOME)/go/bin v2.10.1 + @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(HOME)/go/bin v2.12.2 .PHONY: fmt fmt: ## Run gofmt on the entire project diff --git a/README.md b/README.md index d4cd8a2..e3a9cf2 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The library abstracts the complexity of pairing operations while providing flexi ## Features - **Multiple Curve Support**: FP256BN, BN254, BLS12-381, BLS12-377, and BBS+ variants -- **Pluggable Backends**: Support for AMCL, Gurvy, and Kilic implementations +- **Pluggable Backends**: Support for AMCL and Gurvy implementations - **Type-Safe API**: Strongly-typed group elements (G1, G2, Gt, Zr) - **Efficient Operations**: Optimized pairing computations and multi-scalar multiplications - **Serialization**: Support for both compressed and uncompressed point representations @@ -76,12 +76,17 @@ func main() { | `FP256BN_AMCL` | 256-bit Barreto-Naehrig curve | AMCL | General-purpose pairing operations | | `FP256BN_AMCL_MIRACL` | 256-bit BN curve (MIRACL variant) | AMCL | Legacy compatibility | | `BN254` | 254-bit Barreto-Naehrig curve | Gurvy | High-performance applications | -| `BLS12_381` | BLS12-381 curve | Kilic | Modern protocols, BLS signatures | +| `BLS12_381` | BLS12-381 curve | Gurvy | Modern protocols, BLS signatures | | `BLS12_381_GURVY` | BLS12-381 curve | Gurvy | Performance-optimized BLS12-381 | | `BLS12_377_GURVY` | BLS12-377 curve | Gurvy | Recursive proof systems | -| `BLS12_381_BBS` | BLS12-381 for BBS+ signatures | Kilic | Anonymous credentials | +| `BLS12_381_BBS` | BLS12-381 for BBS+ signatures | Gurvy | Anonymous credentials | | `BLS12_381_BBS_GURVY` | BLS12-381 for BBS+ signatures | Gurvy | High-performance BBS+ | +> **Note:** `BLS12_381` and `BLS12_381_BBS` were previously backed by the Kilic +> implementation. They are now backed by Gurvy (gnark-crypto) and remain +> byte-compatible with both their prior output and their explicit `_GURVY` +> siblings. + ### Choosing a Curve - **BLS12-381**: Recommended for new projects, widely standardized, excellent security margins @@ -243,11 +248,11 @@ fmt.Printf("Multi-scalar multiplication result: %s\n", result.String()) │ Curve, G1, G2, Gt, Zr interfaces │ └─────────────────┬───────────────────┘ │ - ┌─────────┼─────────┐ - ▼ ▼ ▼ - ┌──────┐ ┌──────┐ ┌──────┐ - │ AMCL │ │Gurvy │ │Kilic │ - └──────┘ └──────┘ └──────┘ + ┌─────────┴─────────┐ + ▼ ▼ + ┌──────┐ ┌──────┐ + │ AMCL │ │Gurvy │ + └──────┘ └──────┘ ``` This design allows: @@ -258,8 +263,7 @@ This design allows: ### Backend Implementations - **AMCL (Apache Milagro Crypto Library)**: Mature, well-tested implementation -- **Gurvy**: High-performance Go-native implementation with assembly optimizations -- **Kilic**: Optimized BLS12-381 implementation with focus on BLS signatures +- **Gurvy** (gnark-crypto): High-performance Go-native implementation with assembly optimizations; backs all BLS12-381, BLS12-377, and BN254 curves ## Performance Considerations @@ -324,5 +328,5 @@ This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENS This library builds upon the excellent work of: - Apache Milagro Crypto Library (AMCL) -- ConsenSys Gurvy library -- Kilic BLS12-381 implementation \ No newline at end of file +- ConsenSys gnark-crypto (Gurvy) library +- Kilic BLS12-381 implementation (basis for the original BLS12-381 backend and the big-endian-sign hash-to-curve now reimplemented natively) \ No newline at end of file diff --git a/driver/gurvy/custom.go b/driver/gurvy/custom.go index b72e011..a4c1769 100644 --- a/driver/gurvy/custom.go +++ b/driver/gurvy/custom.go @@ -10,9 +10,8 @@ package gurvy import ( "errors" "hash" - "unsafe" + "math/bits" - "github.com/IBM/mathlib/driver/kilic" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark-crypto/ecc/bls12-381/hash_to_curve" @@ -21,22 +20,132 @@ import ( const Bits = 381 // number of bits needed to represent a Element -type Element [6]uint64 +// modulusBits is the base field modulus p in normal (non-Montgomery) form, +// little-endian limbs. Used by signBE. +var modulusBits = [6]uint64{ + 0xb9feffffffffaaab, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a, +} -type G1Affine struct { - X, Y fp.Element +// SWU map parameters for G1, in Montgomery form. These limbs are identical to +// the ones used by the original github.com/kilic/bls12-381 implementation and +// are valid gnark-crypto fp.Element values because both libraries use the same +// modulus and Montgomery radix (verified: fp.One() == kilic r1). +var swuParamsForG1 = struct { + z, zInv, a, b, minusBOverA fp.Element +}{ + a: fp.Element{0x2f65aa0e9af5aa51, 0x86464c2d1e8416c3, 0xb85ce591b7bd31e2, 0x27e11c91b5f24e7c, 0x28376eda6bfc1835, 0x155455c3e5071d85}, + b: fp.Element{0xfb996971fe22a1e0, 0x9aa93eb35b742d6f, 0x8c476013de99c5c4, 0x873e27c3a221e571, 0xca72b5e45a52d888, 0x06824061418a386b}, + z: fp.Element{0x886c00000023ffdc, 0x0f70008d3090001d, 0x77672417ed5828c3, 0x9dac23e943dc1740, 0x50553f1b9c131521, 0x078c712fbe0ab6e8}, + zInv: fp.Element{0x0e8a2e8ba2e83e10, 0x5b28ba2ca4d745d1, 0x678cd5473847377a, 0x4c506dd8a8076116, 0x9bcb227d79284139, 0x0e8d3154b0ba099a}, + minusBOverA: fp.Element{0x052583c93555a7fe, 0x3b40d72430f93c82, 0x1b75faa0105ec983, 0x2527e7dc63851767, 0x99fffd1f34fc181d, 0x097cab54770ca0d3}, } -func toKilicElement(p *fp.Element) *kilic.Fe { - return (*kilic.Fe)(unsafe.Pointer(p)) +// signBE reports the "big-endian" sign of e, matching the semantics of the +// original kilic implementation: it compares the field negation of e against e +// in normal (non-Montgomery) form and returns true iff (p - e) >= e. This is +// the non-standard sign convention used by the BBS+ hash-to-curve variant. +func signBE(e *fp.Element) bool { + z := e.Bits() // normal form, little-endian limbs + negZ := feNeg(z) + + return feCmp(&negZ, &z) > -1 } -func toGurvyElement(p *kilic.Fe) *fp.Element { - return (*fp.Element)(unsafe.Pointer(p)) +// feNeg returns p - z (mod p) for the normal-form element z, with 0 mapped to 0, +// matching kilic's neg on field elements. +func feNeg(z [6]uint64) [6]uint64 { + if z == ([6]uint64{}) { + return [6]uint64{} + } + var out [6]uint64 + var borrow uint64 + out[0], borrow = bits.Sub64(modulusBits[0], z[0], 0) + out[1], borrow = bits.Sub64(modulusBits[1], z[1], borrow) + out[2], borrow = bits.Sub64(modulusBits[2], z[2], borrow) + out[3], borrow = bits.Sub64(modulusBits[3], z[3], borrow) + out[4], borrow = bits.Sub64(modulusBits[4], z[4], borrow) + out[5], _ = bits.Sub64(modulusBits[5], z[5], borrow) + + return out } -func toGurvyAffine(p *G1Affine) *bls12381.G1Affine { - return (*bls12381.G1Affine)(unsafe.Pointer(p)) +// feCmp compares two normal-form little-endian field elements: 1 if a > b, +// -1 if a < b, 0 if equal (big-endian magnitude comparison). +func feCmp(a, b *[6]uint64) int { + for i := 5; i >= 0; i-- { + if a[i] > b[i] { + return 1 + } else if a[i] < b[i] { + return -1 + } + } + + return 0 +} + +// swuMapG1Pre is the Simplified Shallue-van de Woestijne-Ulas map (pre sign +// correction), ported from the kilic implementation onto gnark fp.Element. +func swuMapG1Pre(u *fp.Element) (x, y fp.Element) { + params := swuParamsForG1 + + var tv0, tv1 fp.Element + tv0.Square(u) + tv0.Mul(&tv0, ¶ms.z) + tv1.Square(&tv0) + + var x1 fp.Element + x1.Add(&tv0, &tv1) + e1 := x1.IsZero() + x1.Inverse(&x1) // Inverse(0) == 0 in gnark, matching kilic + var one fp.Element + one.SetOne() + x1.Add(&x1, &one) + if e1 { + x1.Set(¶ms.zInv) + } + x1.Mul(&x1, ¶ms.minusBOverA) + + var gx1 fp.Element + gx1.Square(&x1) + gx1.Add(&gx1, ¶ms.a) + gx1.Mul(&gx1, &x1) + gx1.Add(&gx1, ¶ms.b) + + var x2 fp.Element + x2.Mul(&tv0, &x1) + tv1.Mul(&tv0, &tv1) + var gx2 fp.Element + gx2.Mul(&gx1, &tv1) + + // e2 is true iff gx1 is a (nonzero) quadratic residue. + e2 := gx1.Legendre() == 1 + + var y2 fp.Element + if e2 { + x.Set(&x1) + y2.Set(&gx1) + } else { + x.Set(&x2) + y2.Set(&gx2) + } + // y2 is guaranteed to be a square here; Sqrt returns one of its roots. + y.Sqrt(&y2) + + return x, y +} + +// SwuMapG1BE is the big-endian-sign variant of the Simplified SWU map, following +// draft-irtf-cfrg-hash-to-curve-06 section 4.1.1. It reproduces, bit-for-bit, +// the output of the former kilic implementation. +func SwuMapG1BE(u *fp.Element) (fp.Element, fp.Element) { + x, y := swuMapG1Pre(u) + + if signBE(&y) != signBE(u) { + y.Neg(&y) + } + + return x, y } // ExpandMsgXmd expands msg to a slice of lenInBytes bytes. @@ -157,29 +266,23 @@ func HashToG1GenericBESwu(msg, dst []byte, hashFunc func() hash.Hash) (bls12381. return bls12381.G1Affine{}, err } - xQ0, yQ0 := kilic.SwuMapG1BE(toKilicElement(&u[0])) - xQ1, yQ1 := kilic.SwuMapG1BE(toKilicElement(&u[1])) - - _xq0 := toGurvyElement(xQ0) - _yq0 := toGurvyElement(yQ0) - _xq1 := toGurvyElement(xQ1) - _yq1 := toGurvyElement(yQ1) + xQ0, yQ0 := SwuMapG1BE(&u[0]) + xQ1, yQ1 := SwuMapG1BE(&u[1]) - Q0 := G1Affine{*_xq0, *_yq0} - Q1 := G1Affine{*_xq1, *_yq1} + Q0 := bls12381.G1Affine{X: xQ0, Y: yQ0} + Q1 := bls12381.G1Affine{X: xQ1, Y: yQ1} // TODO (perf): Add in E' first, then apply isogeny hash_to_curve.G1Isogeny(&Q0.X, &Q0.Y) hash_to_curve.G1Isogeny(&Q1.X, &Q1.Y) var _Q0, _Q1 bls12381.G1Jac - _Q0.FromAffine(toGurvyAffine(&Q0)) - _Q1.FromAffine(toGurvyAffine(&Q1)).AddAssign(&_Q0) + _Q0.FromAffine(&Q0) + _Q1.FromAffine(&Q1).AddAssign(&_Q0) _Q1.ClearCofactor(&_Q1) - toGurvyAffine(&Q1).FromJacobian(&_Q1) - res := toGurvyAffine(&Q1) + Q1.FromJacobian(&_Q1) - return *res, nil + return Q1, nil } diff --git a/driver/kilic/bls12-381.go b/driver/kilic/bls12-381.go deleted file mode 100644 index a69c14e..0000000 --- a/driver/kilic/bls12-381.go +++ /dev/null @@ -1,510 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package kilic - -import ( - "fmt" - "math/big" - - "github.com/IBM/mathlib/driver" - "github.com/IBM/mathlib/driver/common" - bls12381 "github.com/kilic/bls12-381" -) - -/*********************************************************************/ - -type bls12_381G1 struct { - bls12381.PointG1 - bls12381.G1 -} - -func (g *bls12_381G1) Clone(a driver.G1) { - g.Set(&a.(*bls12_381G1).PointG1) -} - -func (e *bls12_381G1) Copy() driver.G1 { - c := &bls12_381G1{G1: *bls12381.NewG1()} - c.Set(&e.PointG1) - - return c -} - -func (g *bls12_381G1) Add(a driver.G1) { - g.G1.Add(&g.PointG1, &g.PointG1, &a.(*bls12_381G1).PointG1) -} - -func (g *bls12_381G1) Mul(a driver.Zr) driver.G1 { - g1 := bls12381.NewG1() - res := g1.New() - - g1.MulScalarBig(res, &g.PointG1, &a.(*common.BaseZr).Int) - - return &bls12_381G1{ - G1: *g1, - PointG1: *res, - } -} - -func (g *bls12_381G1) Mul2(e driver.Zr, Q driver.G1, f driver.Zr) driver.G1 { - a := g.Mul(e) - b := Q.Mul(f) - a.Add(b) - - return a -} - -func (g *bls12_381G1) Mul2InPlace(e driver.Zr, Q driver.G1, f driver.Zr) { - a := g.Mul(e) - b := Q.Mul(f) - a.Add(b) - - g.Set(&a.(*bls12_381G1).PointG1) -} - -func (g *bls12_381G1) Equals(a driver.G1) bool { - g1 := bls12381.NewG1() - - return g1.Equal(&a.(*bls12_381G1).PointG1, &g.PointG1) -} - -func (g *bls12_381G1) Bytes() []byte { - g1 := bls12381.NewG1() - raw := g1.ToUncompressed(&g.PointG1) - - return raw[:] -} - -func (g *bls12_381G1) Compressed() []byte { - raw := g.ToCompressed(&g.PointG1) - - return raw[:] -} - -func (g *bls12_381G1) Sub(a driver.G1) { - g.G1.Sub(&g.PointG1, &g.PointG1, &a.(*bls12_381G1).PointG1) -} - -func (g *bls12_381G1) IsInfinity() bool { - return g.IsZero(&g.PointG1) -} - -func (g *bls12_381G1) String() string { - gb := g.Bytes() - x := new(big.Int).SetBytes(gb[:len(gb)/2]) - y := new(big.Int).SetBytes(gb[len(gb)/2:]) - - return "(" + x.String() + "," + y.String() + ")" -} - -func (g *bls12_381G1) Neg() { - g.G1.Neg(&g.PointG1, &g.PointG1) -} - -/*********************************************************************/ - -type bls12_381G2 struct { - bls12381.PointG2 - bls12381.G2 -} - -func (g *bls12_381G2) Clone(a driver.G2) { - g.Set(&a.(*bls12_381G2).PointG2) -} - -func (e *bls12_381G2) Copy() driver.G2 { - c := &bls12_381G2{ - G2: *bls12381.NewG2(), - } - c.Set(&e.PointG2) - - return c -} - -func (g *bls12_381G2) Mul(a driver.Zr) driver.G2 { - g2 := bls12381.NewG2() - res := g2.New() - - g2.MulScalarBig(res, &g.PointG2, &a.(*common.BaseZr).Int) - - return &bls12_381G2{ - G2: *g2, - PointG2: *res, - } -} - -func (g *bls12_381G2) Add(a driver.G2) { - g.G2.Add(&g.PointG2, &g.PointG2, &a.(*bls12_381G2).PointG2) -} - -func (g *bls12_381G2) Sub(a driver.G2) { - g.G2.Sub(&g.PointG2, &g.PointG2, &a.(*bls12_381G2).PointG2) -} - -func (g *bls12_381G2) Affine() { - g2 := bls12381.NewG2() - g.PointG2 = *g2.Affine(&g.PointG2) -} - -func (g *bls12_381G2) Bytes() []byte { - g2 := bls12381.NewG2() - raw := g2.ToUncompressed(&g.PointG2) - - return raw[:] -} - -func (g *bls12_381G2) Compressed() []byte { - g2 := bls12381.NewG2() - raw := g2.ToCompressed(&g.PointG2) - - return raw[:] -} - -func (g *bls12_381G2) String() string { - // FIXME - return "" -} - -func (g *bls12_381G2) Equals(a driver.G2) bool { - g2 := bls12381.NewG2() - - return g2.Equal(&a.(*bls12_381G2).PointG2, &g.PointG2) -} - -/*********************************************************************/ - -type bls12_381Gt struct { - bls12381.E - bls12381.GT - GTInitialised bool -} - -func (g *bls12_381Gt) Exp(x driver.Zr) driver.Gt { - gt := bls12381.NewGT() - res := gt.New() - gt.Exp(res, &g.E, &x.(*common.BaseZr).Int) - - return &bls12_381Gt{ - E: *res, - GT: *gt, - GTInitialised: true, - } -} - -func (g *bls12_381Gt) Equals(a driver.Gt) bool { - return g.Equal(&a.(*bls12_381Gt).E) -} - -func (g *bls12_381Gt) Inverse() { - if !g.GTInitialised { - g.GT = *bls12381.NewGT() - } - g.GT.Inverse(&g.E, &g.E) -} - -func (g *bls12_381Gt) Mul(a driver.Gt) { - if !g.GTInitialised { - g.GT = *bls12381.NewGT() - } - g.GT.Mul(&g.E, &g.E, &a.(*bls12_381Gt).E) -} - -func (g *bls12_381Gt) IsUnity() bool { - return g.IsOne() -} - -func (g *bls12_381Gt) ToString() string { - // FIXME - return "" -} - -func (g *bls12_381Gt) Bytes() []byte { - if !g.GTInitialised { - g.GT = *bls12381.NewGT() - } - raw := g.ToBytes(&g.E) - - return raw[:] -} - -/*********************************************************************/ - -func NewBls12_381() *Bls12_381 { - return &Bls12_381{common.CurveBase{Modulus: *bls12381.NewG1().Q()}} -} - -func NewBls12_381BBS() *Bls12_381BBS { - return &Bls12_381BBS{*NewBls12_381()} -} - -type Bls12_381 struct { - common.CurveBase -} - -func (c *Bls12_381) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { - g1 := c.NewG1() - for i := range a { - g1.Add(a[i].Mul(b[i])) - } - - return g1 -} - -type Bls12_381BBS struct { - Bls12_381 -} - -func (c *Bls12_381) Pairing(p2 driver.G2, p1 driver.G1) driver.Gt { - bls := bls12381.NewEngine() - bls.AddPair(&p1.(*bls12_381G1).PointG1, &p2.(*bls12_381G2).PointG2) - - return &bls12_381Gt{ - E: *bls.Result(), - } -} - -func (c *Bls12_381) Pairing2(p2a, p2b driver.G2, p1a, p1b driver.G1) driver.Gt { - bls := bls12381.NewEngine() - bls.AddPair(&p1a.(*bls12_381G1).PointG1, &p2a.(*bls12_381G2).PointG2) - bls.AddPair(&p1b.(*bls12_381G1).PointG1, &p2b.(*bls12_381G2).PointG2) - - return &bls12_381Gt{ - E: *bls.Result(), - } -} - -func (c *Bls12_381) FExp(a driver.Gt) driver.Gt { - return a -} - -func (c *Bls12_381) GenG1() driver.G1 { - g := bls12381.NewG1() - g1 := g.One() - - return &bls12_381G1{ - G1: *g, - PointG1: *g1, - } -} - -func (c *Bls12_381) GenG2() driver.G2 { - g := bls12381.NewG2() - g2 := g.One() - - return &bls12_381G2{ - G2: *g, - PointG2: *g2, - } -} - -func (c *Bls12_381) GenGt() driver.Gt { - g1 := c.GenG1() - g2 := c.GenG2() - gengt := c.Pairing(g2, g1) - gengt = c.FExp(gengt) - - return gengt -} - -func (c *Bls12_381) CoordinateByteSize() int { - return fpByteSize -} - -func (c *Bls12_381) G1ByteSize() int { - return 2 * fpByteSize -} - -func (c *Bls12_381) CompressedG1ByteSize() int { - return fpByteSize -} - -func (c *Bls12_381) G2ByteSize() int { - return 4 * fpByteSize -} - -func (c *Bls12_381) CompressedG2ByteSize() int { - return 2 * fpByteSize -} - -func (c *Bls12_381) ScalarByteSize() int { - return common.ScalarByteSize -} - -func (c *Bls12_381) NewG1() driver.G1 { - return &bls12_381G1{G1: *bls12381.NewG1()} -} - -func (c *Bls12_381) NewG2() driver.G2 { - return &bls12_381G2{G2: *bls12381.NewG2()} -} - -func (c *Bls12_381) NewG1FromBytes(b []byte) driver.G1 { - g1 := bls12381.NewG1() - p, err := g1.FromUncompressed(b) - if err != nil { - panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) - } - - return &bls12_381G1{ - PointG1: *p, - G1: *g1, - } -} - -func (c *Bls12_381) NewG2FromBytes(b []byte) driver.G2 { - g2 := bls12381.NewG2() - p, err := g2.FromUncompressed(b) - if err != nil { - panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) - } - - return &bls12_381G2{ - G2: *g2, - PointG2: *p, - } -} - -func (c *Bls12_381) NewG1FromCompressed(b []byte) driver.G1 { - g1 := bls12381.NewG1() - p, err := g1.FromCompressed(b) - if err != nil { - panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) - } - - return &bls12_381G1{ - PointG1: *p, - G1: *g1, - } -} - -func (c *Bls12_381) NewG2FromCompressed(b []byte) driver.G2 { - g2 := bls12381.NewG2() - p, err := g2.FromCompressed(b) - if err != nil { - panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) - } - - return &bls12_381G2{ - G2: *g2, - PointG2: *p, - } -} - -func (c *Bls12_381) NewGtFromBytes(b []byte) driver.Gt { - gt := bls12381.NewGT() - p, err := gt.FromBytes(b) - if err != nil { - panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) - } - - return &bls12_381Gt{ - E: *p, - GT: *gt, - GTInitialised: true, - } -} - -func (c *Bls12_381) HashToG1(data []byte) driver.G1 { - g1 := bls12381.NewG1() - p, err := g1.HashToCurve(data, []byte{}) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G1{ - PointG1: *p, - G1: *g1, - } -} - -func (c *Bls12_381) HashToG2(data []byte) driver.G2 { - g2 := bls12381.NewG2() - p, err := g2.HashToCurve(data, []byte{}) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G2{ - PointG2: *p, - G2: *g2, - } -} - -func (c *Bls12_381) HashToG1WithDomain(data, domain []byte) driver.G1 { - g1 := bls12381.NewG1() - p, err := g1.HashToCurve(data, domain) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G1{ - PointG1: *p, - G1: *g1, - } -} - -func (c *Bls12_381) HashToG2WithDomain(data, domain []byte) driver.G2 { - g2 := bls12381.NewG2() - p, err := g2.HashToCurve(data, domain) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G2{ - PointG2: *p, - G2: *g2, - } -} - -func (c *Bls12_381BBS) HashToG1(data []byte) driver.G1 { - p, err := HashToG1GenericBESwu(data, []byte{}) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G1{ - PointG1: *p, - G1: *bls12381.NewG1(), - } -} - -func (c *Bls12_381BBS) HashToG2(data []byte) driver.G2 { - g2 := bls12381.NewG2() - p, err := g2.HashToCurve(data, []byte{}) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G2{ - PointG2: *p, - G2: *g2, - } -} - -func (c *Bls12_381BBS) HashToG1WithDomain(data, domain []byte) driver.G1 { - p, err := HashToG1GenericBESwu(data, domain) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G1{ - PointG1: *p, - G1: *bls12381.NewG1(), - } -} - -func (c *Bls12_381BBS) HashToG2WithDomain(data, domain []byte) driver.G2 { - g2 := bls12381.NewG2() - p, err := g2.HashToCurve(data, domain) - if err != nil { - panic(fmt.Sprintf("HashToCurve failed [%s]", err.Error())) - } - - return &bls12_381G2{ - PointG2: *p, - G2: *g2, - } -} diff --git a/driver/kilic/custom.go b/driver/kilic/custom.go deleted file mode 100644 index de86a90..0000000 --- a/driver/kilic/custom.go +++ /dev/null @@ -1,353 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package kilic - -import ( - "errors" - "hash" - "unsafe" - _ "unsafe" - - bls12381 "github.com/kilic/bls12-381" - "golang.org/x/crypto/blake2b" -) - -const fpByteSize = 48 - -const fpNumberOfLimbs = 6 - -type Fe [fpNumberOfLimbs]uint64 - -var modulus = Fe{0xb9feffffffffaaab, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a} - -// r1 = r mod p -var r1 = &Fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493} - -var swuParamsForG1 = struct { - z *Fe - zInv *Fe - a *Fe - b *Fe - minusBOverA *Fe -}{ - a: &Fe{0x2f65aa0e9af5aa51, 0x86464c2d1e8416c3, 0xb85ce591b7bd31e2, 0x27e11c91b5f24e7c, 0x28376eda6bfc1835, 0x155455c3e5071d85}, - b: &Fe{0xfb996971fe22a1e0, 0x9aa93eb35b742d6f, 0x8c476013de99c5c4, 0x873e27c3a221e571, 0xca72b5e45a52d888, 0x06824061418a386b}, - z: &Fe{0x886c00000023ffdc, 0x0f70008d3090001d, 0x77672417ed5828c3, 0x9dac23e943dc1740, 0x50553f1b9c131521, 0x078c712fbe0ab6e8}, - zInv: &Fe{0x0e8a2e8ba2e83e10, 0x5b28ba2ca4d745d1, 0x678cd5473847377a, 0x4c506dd8a8076116, 0x9bcb227d79284139, 0x0e8d3154b0ba099a}, - minusBOverA: &Fe{0x052583c93555a7fe, 0x3b40d72430f93c82, 0x1b75faa0105ec983, 0x2527e7dc63851767, 0x99fffd1f34fc181d, 0x097cab54770ca0d3}, -} - -func (fe *Fe) setBytes(in []byte) *Fe { - l := min(len(in), fpByteSize) - padded := make([]byte, fpByteSize) - copy(padded[fpByteSize-l:], in[:]) - var a int - for i := range fpNumberOfLimbs { - a = fpByteSize - i*8 - fe[i] = uint64(padded[a-1]) | uint64(padded[a-2])<<8 | - uint64(padded[a-3])<<16 | uint64(padded[a-4])<<24 | - uint64(padded[a-5])<<32 | uint64(padded[a-6])<<40 | - uint64(padded[a-7])<<48 | uint64(padded[a-8])<<56 - } - - return fe -} - -func (fe *Fe) isValid() bool { - return fe.cmp(&modulus) == -1 -} - -func (fe *Fe) cmp(fe2 *Fe) int { - for i := fpNumberOfLimbs - 1; i >= 0; i-- { - if fe[i] > fe2[i] { - return 1 - } else if fe[i] < fe2[i] { - return -1 - } - } - - return 0 -} - -func (fe *Fe) isZero() bool { - return (fe[5] | fe[4] | fe[3] | fe[2] | fe[1] | fe[0]) == 0 -} - -func (fe *Fe) one() *Fe { - return fe.set(r1) -} - -func (fe *Fe) set(fe2 *Fe) *Fe { - fe[0] = fe2[0] - fe[1] = fe2[1] - fe[2] = fe2[2] - fe[3] = fe2[3] - fe[4] = fe2[4] - fe[5] = fe2[5] - - return fe -} - -func (e *Fe) signBE() bool { - negZ, z := new(Fe), new(Fe) - fromMont(z, e) - neg(negZ, z) - - return negZ.cmp(z) > -1 -} - -//go:linkname add github.com/kilic/bls12-381.add -func add(c, a, b *Fe) - -//go:linkname toMont github.com/kilic/bls12-381.toMont -func toMont(a, b *Fe) - -//go:linkname inverse github.com/kilic/bls12-381.inverse -func inverse(inv, e *Fe) - -//go:linkname isQuadraticNonResidue github.com/kilic/bls12-381.isQuadraticNonResidue -func isQuadraticNonResidue(a *Fe) bool - -//go:linkname sqrt github.com/kilic/bls12-381.sqrt -func sqrt(c, a *Fe) bool - -//go:linkname square github.com/kilic/bls12-381.square -func square(c, a *Fe) - -//go:linkname fromMont github.com/kilic/bls12-381.fromMont -func fromMont(c, a *Fe) - -//go:linkname neg github.com/kilic/bls12-381.neg -func neg(c, a *Fe) - -//go:linkname isogenyMapG1 github.com/kilic/bls12-381.isogenyMapG1 -func isogenyMapG1(x, y *Fe) - -func swuMapG1Pre(u *Fe) (*Fe, *Fe, *Fe) { - var params = swuParamsForG1 - var tv [4]*Fe - for i := range 4 { - tv[i] = new(Fe) - } - square(tv[0], u) - mul(tv[0], tv[0], params.z) - square(tv[1], tv[0]) - x1 := new(Fe) - add(x1, tv[0], tv[1]) - inverse(x1, x1) - e1 := x1.isZero() - one := new(Fe).one() - add(x1, x1, one) - if e1 { - x1.set(params.zInv) - } - mul(x1, x1, params.minusBOverA) - gx1 := new(Fe) - square(gx1, x1) - add(gx1, gx1, params.a) - mul(gx1, gx1, x1) - add(gx1, gx1, params.b) - x2 := new(Fe) - mul(x2, tv[0], x1) - mul(tv[1], tv[0], tv[1]) - gx2 := new(Fe) - mul(gx2, gx1, tv[1]) - e2 := !isQuadraticNonResidue(gx1) - x, y2 := new(Fe), new(Fe) - if e2 { - x.set(x1) - y2.set(gx1) - } else { - x.set(x2) - y2.set(gx2) - } - y := new(Fe) - sqrt(y, y2) - - // This function is modified to perform the sign correction outside. - return x, y, u -} - -// SwuMapG1BE is implementation of Simplified Shallue-van de Woestijne-Ulas Method -// follows the implementation at draft-irtf-cfrg-hash-to-curve-06. -// uses big-endian variant: https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-4.1.1 -func SwuMapG1BE(u *Fe) (*Fe, *Fe) { - x, y, u := swuMapG1Pre(u) - - if y.signBE() != u.signBE() { - neg(y, y) - } - - return x, y -} - -type PointG1 [3]Fe - -func pointG1tobls12381PointG1(p *PointG1) *bls12381.PointG1 { - return (*bls12381.PointG1)(unsafe.Pointer(p)) -} - -func feAtPos(pos int, p *bls12381.PointG1) *Fe { - return (*Fe)(unsafe.Pointer(&(p[pos]))) -} - -func HashToG1GenericBESwu(data, domain []byte) (*bls12381.PointG1, error) { - hashFunc := func() hash.Hash { - // We pass a null key so error is impossible here. - h, _ := blake2b.New512(nil) - - return h - } - - p, err := HashToCurveGenericBESwu(data, domain, hashFunc) - if err != nil { - return nil, err - } - - return p, nil -} - -func HashToCurveGenericBESwu(msg, domain []byte, hashFunc func() hash.Hash) (*bls12381.PointG1, error) { - g := bls12381.NewG1() - hashRes, err := hashToFpXMD(hashFunc, msg, domain, 2) - if err != nil { - return nil, err - } - u0, u1 := hashRes[0], hashRes[1] - - x0, y0 := SwuMapG1BE(u0) - x1, y1 := SwuMapG1BE(u1) - one := new(Fe).one() - p0, p1 := pointG1tobls12381PointG1(&PointG1{*x0, *y0, *one}), pointG1tobls12381PointG1(&PointG1{*x1, *y1, *one}) - - g.Add(p0, p0, p1) - g.Affine(p0) - isogenyMapG1(feAtPos(0, p0), feAtPos(1, p0)) - g.ClearCofactor(p0) - - return g.Affine(p0), nil -} - -func hashToFpXMD(f func() hash.Hash, msg []byte, domain []byte, count int) ([]*Fe, error) { - randBytes, err := expandMsgXMD(f, msg, domain, count*64) - if err != nil { - return nil, err - } - - els := make([]*Fe, count) - for i := range count { - var err error - - els[i], err = from64Bytes(randBytes[i*64 : (i+1)*64]) - if err != nil { - return nil, err - } - } - - return els, nil -} - -func expandMsgXMD(f func() hash.Hash, msg []byte, domain []byte, outLen int) ([]byte, error) { - h := f() - if len(domain) > 255 { - return nil, errors.New("invalid domain length") - } - if outLen > 65535 { - return nil, errors.New("invalid outLen") - } - domainLen := uint8(len(domain)) // #nosec G115 - - // DST_prime = DST || I2OSP(len(DST), 1) - // b_0 = H(Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime) - _, _ = h.Write(make([]byte, h.BlockSize())) - _, _ = h.Write(msg) - _, _ = h.Write([]byte{uint8(outLen >> 8), uint8(outLen)}) // #nosec G115 - _, _ = h.Write([]byte{0}) - _, _ = h.Write(domain) - _, _ = h.Write([]byte{domainLen}) - b0 := h.Sum(nil) - - // b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) - h.Reset() - _, _ = h.Write(b0) - _, _ = h.Write([]byte{1}) - _, _ = h.Write(domain) - _, _ = h.Write([]byte{domainLen}) - b1 := h.Sum(nil) - - // b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) - ell := (outLen + h.Size() - 1) / h.Size() - bi := b1 - out := make([]byte, outLen) - for i := 1; i < ell; i++ { - h.Reset() - // b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) - tmp := make([]byte, h.Size()) - for j := range h.Size() { - tmp[j] = b0[j] ^ bi[j] - } - _, _ = h.Write(tmp) - _, _ = h.Write([]byte{1 + uint8(i)}) - _, _ = h.Write(domain) - _, _ = h.Write([]byte{domainLen}) - - // b_1 || ... || b_(ell - 1) - copy(out[(i-1)*h.Size():i*h.Size()], bi[:]) - bi = h.Sum(nil) - } - // b_ell - copy(out[(ell-1)*h.Size():], bi[:]) - - return out[:outLen], nil -} - -func from64Bytes(in []byte) (*Fe, error) { - if len(in) != 32*2 { - return nil, errors.New("input string must be equal 64 bytes") - } - a0 := make([]byte, fpByteSize) - copy(a0[fpByteSize-32:fpByteSize], in[:32]) - a1 := make([]byte, fpByteSize) - copy(a1[fpByteSize-32:fpByteSize], in[32:]) - e0, err := fromBytes(a0) - if err != nil { - return nil, err - } - e1, err := fromBytes(a1) - if err != nil { - return nil, err - } - // F = 2 ^ 256 * R - F := Fe{ - 0x75b3cd7c5ce820f, - 0x3ec6ba621c3edb0b, - 0x168a13d82bff6bce, - 0x87663c4bf8c449d2, - 0x15f34c83ddc8d830, - 0xf9628b49caa2e85, - } - - mul(e0, e0, &F) - add(e1, e1, e0) - - return e1, nil -} - -func fromBytes(in []byte) (*Fe, error) { - fe := &Fe{} - if len(in) != fpByteSize { - return nil, errors.New("input string must be equal 48 bytes") - } - fe.setBytes(in) - if !fe.isValid() { - return nil, errors.New("must be less than modulus") - } - toMont(fe, fe) - - return fe, nil -} diff --git a/driver/kilic/custom_amd64.go b/driver/kilic/custom_amd64.go deleted file mode 100644 index 6149881..0000000 --- a/driver/kilic/custom_amd64.go +++ /dev/null @@ -1,30 +0,0 @@ -//go:build amd64 && !generic - -/* -Copyright IBM Corp. All Rights Reserved. -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package kilic - -import ( - _ "unsafe" - - "golang.org/x/sys/cpu" -) - -func init() { - if !cpu.X86.HasADX || !cpu.X86.HasBMI2 { - mul = mulNoADX - } -} - -var mul func(c, a, b *Fe) = mulADX - -//go:linkname mulADX github.com/kilic/bls12-381.mulADX -func mulADX(c, a, b *Fe) - -//go:linkname mulNoADX github.com/kilic/bls12-381.mulNoADX -func mulNoADX(c, a, b *Fe) diff --git a/driver/kilic/custom_generic.go b/driver/kilic/custom_generic.go deleted file mode 100644 index 6fa670b..0000000 --- a/driver/kilic/custom_generic.go +++ /dev/null @@ -1,175 +0,0 @@ -//go:build !amd64 || generic - -/* -Copyright IBM Corp. All Rights Reserved. -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package kilic - -import "math/bits" - -// madd0 hi = a*b + c (discards lo bits) -func madd0(a, b, c uint64) (hi uint64) { - var carry, lo uint64 - hi, lo = bits.Mul64(a, b) - _, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - - return -} - -// madd1 hi, lo = a*b + c -func madd1(a, b, c uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - - return -} - -// madd2 hi, lo = a*b + c + d -func madd2(a, b, c, d uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - - return -} - -func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, e, carry) - - return -} - -func mul(z, x, y *Fe) { - var t [6]uint64 - var c [3]uint64 - { - // round 0 - v := x[0] - c[1], c[0] = bits.Mul64(v, y[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd1(v, y[1], c[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd1(v, y[2], c[1]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd1(v, y[3], c[1]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd1(v, y[4], c[1]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd1(v, y[5], c[1]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 1 - v := x[1] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 2 - v := x[2] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 3 - v := x[3] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 4 - v := x[4] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - { - // round 5 - v := x[5] - c[1], c[0] = madd1(v, y[0], t[0]) - m := c[0] * 9940570264628428797 - c[2] = madd0(m, 13402431016077863595, c[0]) - c[1], c[0] = madd2(v, y[1], c[1], t[1]) - c[2], z[0] = madd2(m, 2210141511517208575, c[2], c[0]) - c[1], c[0] = madd2(v, y[2], c[1], t[2]) - c[2], z[1] = madd2(m, 7435674573564081700, c[2], c[0]) - c[1], c[0] = madd2(v, y[3], c[1], t[3]) - c[2], z[2] = madd2(m, 7239337960414712511, c[2], c[0]) - c[1], c[0] = madd2(v, y[4], c[1], t[4]) - c[2], z[3] = madd2(m, 5412103778470702295, c[2], c[0]) - c[1], c[0] = madd2(v, y[5], c[1], t[5]) - z[5], z[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) - } - - // if z > q --> z -= q - // note: this is NOT constant time - //nolint:staticcheck - if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { - var b uint64 - z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) - z[1], b = bits.Sub64(z[1], 2210141511517208575, b) - z[2], b = bits.Sub64(z[2], 7435674573564081700, b) - z[3], b = bits.Sub64(z[3], 7239337960414712511, b) - z[4], b = bits.Sub64(z[4], 5412103778470702295, b) - z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) - } -} diff --git a/go.mod b/go.mod index a02e6ac..0976862 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,8 @@ go 1.26.3 require ( github.com/consensys/gnark-crypto v0.20.1 github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 - github.com/kilic/bls12-381 v0.1.0 github.com/stretchr/testify v1.11.1 golang.org/x/crypto v0.52.0 - golang.org/x/sys v0.45.0 ) require ( @@ -17,5 +15,6 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect + golang.org/x/sys v0.45.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1eaff0d..314e347 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 h1:B1Nt8hKb//KvgGRprk0h1t4lCnwhE9/ryb1WqfZbV+M= github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE= -github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= -github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -23,11 +21,6 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= -golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/golden_test.go b/golden_test.go new file mode 100644 index 0000000..cb29b6e --- /dev/null +++ b/golden_test.go @@ -0,0 +1,86 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package math + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Golden vectors captured from the original github.com/kilic/bls12-381 backend +// before it was replaced by the gnark-crypto (gurvy) backend. They pin the byte +// representation of the BLS12_381 and BLS12_381_BBS curves so the migration is +// provably output-compatible. The only operations that differ between the two +// curve IDs are HashToG1 / HashToG1WithDomain (the BBS variant uses the +// big-endian-sign Simplified SWU map). +// +// The fixed scalar below is used for the group-element multiplications. +var goldenScalar = []byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44} + +type goldenVec struct { + genG1Mul string + genG2Mul string + genGtExp string + hashToG1 string + hashToG1WithDomain string + hashToG1CompDomain string + hashToG2 string +} + +var golden = map[CurveID]goldenVec{ + BLS12_381: { + genG1Mul: "091a87a08856fc7d22775e13cf6d7472c327189422a5cf1e3c75cf11a80f63e87ab51a2678fc49b73d3489339743f01c073388aa8847dea668fbb393f34c2d89a2bdfec536eb898eca87c64bc324d61e7ec323ae37a5189e14d918136f5adaf7", + genG2Mul: "087d66f44564f5d109f024146d2c1f2a8e8d3a61092e79412a740f8cc7ee51994578bd228844d265ab2fdc1eda9ee052063069ec05711ca84a4f58eeedee779f85e0d03021aac92bc855e42abccdbc135b2df215bab0ccc6190fad3905b5a5b2108b14e74e788fcfe12a6d26b72f7f0468861ddf6b90138f39105c062c201fd08dcdf1b86c1ef1023441e2972e3a2e87068d5ae2f1e19dd5a8a8aaf45a410a9af22989b5eff0be66c8e37498cae3575d0e1c0e124a4dce05098b35e6f327bdf9", + genGtExp: "031c4404a493b639fda9e6dcce1f15e2f972ae3ef6fdda302c9ad1c04ede609ae5c3cbaa532cf75b643931a56e756ceb16ce1dcebd47fb1585a519e2ebe3965c1bdc33a5ece1dd02f716903b3498c284c1ddcaa7a56bbf4e4fa0e0645e899b9214c6b4927ad130cd74dd8758849eb9a6ebfb01f82b53bf2f9f5f768725cc7a18dd1debbb699e192a9facebb546d4f1c107686aa37bac547bf8aff86fad7f76e01dde85c1be37e879248059952cc8d6e310dffcc89ddb596b53504380c2cee69a04fc1442ac76d799b5e145faa29641d4ed74479d6a1ee241a0340c4d9d43dadd4032c9f5f36a2b80e86021dee4e22f4e16b55c6801cc71877674cdb4a9a4370dcae197ee1a7620b8aa4c48035fe487ea8150981a20d104c1ae31e6b5402d288817f034523ecd57165a170f37eb584a581441d24798d06fbeb9b18c2359fafe8f9d0595dc8d12681993481b43d6fe39de16fb64b8c9a3ed5f183b9f83c2bd4b769784c1262cb9564086edf169aaf554f192f73b3ccf0b8a99cca5fe0dbf56e27d078314f3923b1d7076f780bf3269a37e916a8e4effff186a2e59a82a8eed165bd7ea21d95e49479e1af841037786a5f50a8fb585f5d35616690827861c79e60169829b62680145b766035a2473c7c441a12f0569868771d62d6e3d5dfaf8b84403a5772182532b4dd796347608e67f26f7acea7ba6e55bf75c9525924e9ee03f69e88558f3f8174049f1293f26f27da018666a67fc23e1bfdeb219cc19d8db4fec7f4d7b89437320ed920e13ae45ca3adfacde37f68397ad20007bc85208c5ee", + hashToG1: "02a54fad2d2ce3d6d993ba0f64c8e4b7ac3f5f34f118e8f42a24470486e0987282d89c16508f7b21cda6432ab19ba8fa180cff3784fb7fe6a78a53e430a4151be844abc07bb911d9a393c9131dd8216d1e79f254b15792fbaefa9f5a96e7afd6", + hashToG1WithDomain: "073e516c8d035a9768fce91a4429e0cb4b2aa8286ef3e7cb20e5b008e6415346c069ac8e42e91b0c85f55ade80c5592c060dbaf70a32dcb8dc1fd7550f8bca2f237a56e4490e414a86fb9348c7048d6e3d4ac52ac8c42ec3767f5d0ed4f40228", + hashToG1CompDomain: "873e516c8d035a9768fce91a4429e0cb4b2aa8286ef3e7cb20e5b008e6415346c069ac8e42e91b0c85f55ade80c5592c", + hashToG2: "152a5103cb577d1b5c01be1621ecb3b9d8fdd095c5b6cb2efe53667001cd389e5631af2568bc7185831764bb4b8828da0468a3c807fb0e2262bc1a6c0024c3e2e51c5ad160c5a43c4de769bd9d61857ac3b83dae0a860328b6f42f6b621453530e8522a85b79654fd83b79609ef52b9ad4be13bd5f55e0711f577a50704e3e549bbe4257b3c717175c6dbc3f23e031081674c02a1ff7114b9e810a8dd76744f0df559801981f6a8386716a90d15c6573fb562f91da83e8a843912e5d673399e7", + }, + BLS12_381_BBS: { + genG1Mul: "091a87a08856fc7d22775e13cf6d7472c327189422a5cf1e3c75cf11a80f63e87ab51a2678fc49b73d3489339743f01c073388aa8847dea668fbb393f34c2d89a2bdfec536eb898eca87c64bc324d61e7ec323ae37a5189e14d918136f5adaf7", + genG2Mul: "087d66f44564f5d109f024146d2c1f2a8e8d3a61092e79412a740f8cc7ee51994578bd228844d265ab2fdc1eda9ee052063069ec05711ca84a4f58eeedee779f85e0d03021aac92bc855e42abccdbc135b2df215bab0ccc6190fad3905b5a5b2108b14e74e788fcfe12a6d26b72f7f0468861ddf6b90138f39105c062c201fd08dcdf1b86c1ef1023441e2972e3a2e87068d5ae2f1e19dd5a8a8aaf45a410a9af22989b5eff0be66c8e37498cae3575d0e1c0e124a4dce05098b35e6f327bdf9", + genGtExp: "031c4404a493b639fda9e6dcce1f15e2f972ae3ef6fdda302c9ad1c04ede609ae5c3cbaa532cf75b643931a56e756ceb16ce1dcebd47fb1585a519e2ebe3965c1bdc33a5ece1dd02f716903b3498c284c1ddcaa7a56bbf4e4fa0e0645e899b9214c6b4927ad130cd74dd8758849eb9a6ebfb01f82b53bf2f9f5f768725cc7a18dd1debbb699e192a9facebb546d4f1c107686aa37bac547bf8aff86fad7f76e01dde85c1be37e879248059952cc8d6e310dffcc89ddb596b53504380c2cee69a04fc1442ac76d799b5e145faa29641d4ed74479d6a1ee241a0340c4d9d43dadd4032c9f5f36a2b80e86021dee4e22f4e16b55c6801cc71877674cdb4a9a4370dcae197ee1a7620b8aa4c48035fe487ea8150981a20d104c1ae31e6b5402d288817f034523ecd57165a170f37eb584a581441d24798d06fbeb9b18c2359fafe8f9d0595dc8d12681993481b43d6fe39de16fb64b8c9a3ed5f183b9f83c2bd4b769784c1262cb9564086edf169aaf554f192f73b3ccf0b8a99cca5fe0dbf56e27d078314f3923b1d7076f780bf3269a37e916a8e4effff186a2e59a82a8eed165bd7ea21d95e49479e1af841037786a5f50a8fb585f5d35616690827861c79e60169829b62680145b766035a2473c7c441a12f0569868771d62d6e3d5dfaf8b84403a5772182532b4dd796347608e67f26f7acea7ba6e55bf75c9525924e9ee03f69e88558f3f8174049f1293f26f27da018666a67fc23e1bfdeb219cc19d8db4fec7f4d7b89437320ed920e13ae45ca3adfacde37f68397ad20007bc85208c5ee", + hashToG1: "15f112904d3f2d00e89660a94c7159c1ba4063c0bb2f60d844c64becafc99d4885c22d3796b9043e6579e83bb233e2c00e67dada768a94b3052df7925908bf5ecbb4a0f9cf11c1130b89ab8e5523a116af195e887b0e6f75d51b8bd25f16aa10", + hashToG1WithDomain: "16022c85ce70d7133c3b7fa532b3f73b2875f367d7a1715c0d69eff0852372e67a8cee6ec58b92fdff906c18e446a30214492bc6380b98d0d22b0b50853cc13d60e1adcf01ba48502cfedf51b9e0b2834a1fc2f177e9e715d2d358e87a96a917", + hashToG1CompDomain: "b6022c85ce70d7133c3b7fa532b3f73b2875f367d7a1715c0d69eff0852372e67a8cee6ec58b92fdff906c18e446a302", + hashToG2: "152a5103cb577d1b5c01be1621ecb3b9d8fdd095c5b6cb2efe53667001cd389e5631af2568bc7185831764bb4b8828da0468a3c807fb0e2262bc1a6c0024c3e2e51c5ad160c5a43c4de769bd9d61857ac3b83dae0a860328b6f42f6b621453530e8522a85b79654fd83b79609ef52b9ad4be13bd5f55e0711f577a50704e3e549bbe4257b3c717175c6dbc3f23e031081674c02a1ff7114b9e810a8dd76744f0df559801981f6a8386716a90d15c6573fb562f91da83e8a843912e5d673399e7", + }, +} + +// TestGoldenKilicCompat verifies that the gnark-crypto backends now wired to the +// BLS12_381 and BLS12_381_BBS curve IDs reproduce, byte-for-byte, the outputs of +// the original kilic backend they replaced. +func TestGoldenKilicCompat(t *testing.T) { + for id, want := range golden { + t.Run(CurveIDToString(id), func(t *testing.T) { + c := Curves[id] + z := c.NewZrFromBytes(goldenScalar) + + assert.Equal(t, want.genG1Mul, hex.EncodeToString(c.GenG1.Mul(z).Bytes()), "GenG1.Mul") + assert.Equal(t, want.genG2Mul, hex.EncodeToString(c.GenG2.Mul(z).Bytes()), "GenG2.Mul") + assert.Equal(t, want.genGtExp, hex.EncodeToString(c.GenGt.Exp(z).Bytes()), "GenGt.Exp") + assert.Equal(t, want.hashToG1, hex.EncodeToString(c.HashToG1([]byte("Chase!")).Bytes()), "HashToG1") + + h := c.HashToG1WithDomain([]byte("CD"), []byte("EF")) + assert.Equal(t, want.hashToG1WithDomain, hex.EncodeToString(h.Bytes()), "HashToG1WithDomain") + assert.Equal(t, want.hashToG1CompDomain, hex.EncodeToString(h.Compressed()), "HashToG1WithDomain compressed") + + assert.Equal(t, want.hashToG2, hex.EncodeToString(c.HashToG2([]byte("Chase!")).Bytes()), "HashToG2") + }) + } + + // Sanity: the BBS HashToG1 output must differ from the standard one, + // confirming the big-endian-sign SWU map is actually in effect. + require.NotEqual(t, + golden[BLS12_381].hashToG1, + golden[BLS12_381_BBS].hashToG1, + "BBS HashToG1 should differ from standard HashToG1") +} diff --git a/math.go b/math.go index d797ff6..9440b22 100644 --- a/math.go +++ b/math.go @@ -35,10 +35,10 @@ SPDX-License-Identifier: Apache-2.0 // - FP256BN_AMCL: 256-bit Barreto-Naehrig curve (AMCL backend) // - BN254: 254-bit Barreto-Naehrig curve (Gurvy backend) // - FP256BN_AMCL_MIRACL: 256-bit BN curve MIRACL variant (AMCL backend) -// - BLS12_381: BLS12-381 curve (Kilic backend) +// - BLS12_381: BLS12-381 curve (Gurvy backend) // - BLS12_377_GURVY: BLS12-377 curve (Gurvy backend) // - BLS12_381_GURVY: BLS12-381 curve (Gurvy backend) -// - BLS12_381_BBS: BLS12-381 optimized for BBS+ signatures (Kilic backend) +// - BLS12_381_BBS: BLS12-381 optimized for BBS+ signatures (Gurvy backend) // - BLS12_381_BBS_GURVY: BLS12-381 for BBS+ (Gurvy backend) // // # Thread Safety @@ -59,7 +59,6 @@ import ( "github.com/IBM/mathlib/driver/amcl" "github.com/IBM/mathlib/driver/gurvy" "github.com/IBM/mathlib/driver/gurvy/bls12381" - "github.com/IBM/mathlib/driver/kilic" ) // CurveID identifies a specific elliptic curve configuration and its backend implementation. @@ -80,9 +79,10 @@ const ( // Provided for legacy compatibility with MIRACL-based systems. FP256BN_AMCL_MIRACL - // BLS12_381 represents the BLS12-381 curve using the Kilic backend. + // BLS12_381 represents the BLS12-381 curve using the Gurvy (gnark-crypto) backend. // Recommended for new projects due to excellent security margins and wide adoption. // Suitable for BLS signatures and modern cryptographic protocols. + // Byte-compatible with the former Kilic backend and with BLS12_381_GURVY. BLS12_381 // BLS12_377_GURVY represents the BLS12-377 curve using the Gurvy backend. @@ -183,17 +183,17 @@ var Curves []*Curve = []*Curve{ curveID: FP256BN_AMCL_MIRACL, }, { - c: kilic.NewBls12_381(), - GenG1: NewG1((&kilic.Bls12_381{}).GenG1(), BLS12_381), - GenG2: NewG2((&kilic.Bls12_381{}).GenG2(), BLS12_381), - GenGt: NewGt((&kilic.Bls12_381{}).GenGt(), BLS12_381), - GroupOrder: NewZr(kilic.NewBls12_381().GroupOrder(), BLS12_381), - CoordByteSize: (&kilic.Bls12_381{}).CoordinateByteSize(), - G1ByteSize: (&kilic.Bls12_381{}).G1ByteSize(), - CompressedG1ByteSize: (&kilic.Bls12_381{}).CompressedG1ByteSize(), - G2ByteSize: (&kilic.Bls12_381{}).G2ByteSize(), - CompressedG2ByteSize: (&kilic.Bls12_381{}).CompressedG2ByteSize(), - ScalarByteSize: (&kilic.Bls12_381{}).ScalarByteSize(), + c: bls12381.NewCurve(), + GenG1: NewG1((&bls12381.Curve{}).GenG1(), BLS12_381), + GenG2: NewG2((&bls12381.Curve{}).GenG2(), BLS12_381), + GenGt: NewGt((&bls12381.Curve{}).GenGt(), BLS12_381), + GroupOrder: NewZr(bls12381.NewCurve().GroupOrder(), BLS12_381), + CoordByteSize: (&bls12381.Curve{}).CoordinateByteSize(), + G1ByteSize: (&bls12381.Curve{}).G1ByteSize(), + CompressedG1ByteSize: (&bls12381.Curve{}).CompressedG1ByteSize(), + G2ByteSize: (&bls12381.Curve{}).G2ByteSize(), + CompressedG2ByteSize: (&bls12381.Curve{}).CompressedG2ByteSize(), + ScalarByteSize: (&bls12381.Curve{}).ScalarByteSize(), curveID: BLS12_381, }, { @@ -225,17 +225,17 @@ var Curves []*Curve = []*Curve{ curveID: BLS12_381_GURVY, }, { - c: kilic.NewBls12_381BBS(), - GenG1: NewG1(kilic.NewBls12_381BBS().GenG1(), BLS12_381_BBS), - GenG2: NewG2(kilic.NewBls12_381BBS().GenG2(), BLS12_381_BBS), - GenGt: NewGt(kilic.NewBls12_381BBS().GenGt(), BLS12_381_BBS), - GroupOrder: NewZr(kilic.NewBls12_381().GroupOrder(), BLS12_381_BBS), - CoordByteSize: kilic.NewBls12_381BBS().CoordinateByteSize(), - G1ByteSize: kilic.NewBls12_381BBS().G1ByteSize(), - CompressedG1ByteSize: kilic.NewBls12_381BBS().CompressedG1ByteSize(), - G2ByteSize: kilic.NewBls12_381BBS().G2ByteSize(), - CompressedG2ByteSize: kilic.NewBls12_381BBS().CompressedG2ByteSize(), - ScalarByteSize: kilic.NewBls12_381BBS().ScalarByteSize(), + c: bls12381.NewBBSCurve(), + GenG1: NewG1(bls12381.NewBBSCurve().GenG1(), BLS12_381_BBS), + GenG2: NewG2(bls12381.NewBBSCurve().GenG2(), BLS12_381_BBS), + GenGt: NewGt(bls12381.NewBBSCurve().GenGt(), BLS12_381_BBS), + GroupOrder: NewZr(bls12381.NewCurve().GroupOrder(), BLS12_381_BBS), + CoordByteSize: bls12381.NewBBSCurve().CoordinateByteSize(), + G1ByteSize: bls12381.NewBBSCurve().G1ByteSize(), + CompressedG1ByteSize: bls12381.NewBBSCurve().CompressedG1ByteSize(), + G2ByteSize: bls12381.NewBBSCurve().G2ByteSize(), + CompressedG2ByteSize: bls12381.NewBBSCurve().CompressedG2ByteSize(), + ScalarByteSize: bls12381.NewBBSCurve().ScalarByteSize(), curveID: BLS12_381_BBS, }, { diff --git a/math_test.go b/math_test.go index aab51de..aa300c8 100644 --- a/math_test.go +++ b/math_test.go @@ -876,70 +876,76 @@ func TestCurves(t *testing.T) { } } +// Test381Compat verifies that the canonical BLS12_381 curve ID (now backed by +// the gnark-crypto/gurvy implementation, formerly by kilic) is byte-compatible +// with its explicit BLS12_381_GURVY sibling. func Test381Compat(t *testing.T) { rng, err := Curves[BLS12_381].Rand() require.NoError(t, err) - kilic := Curves[BLS12_381] + canonical := Curves[BLS12_381] gurvy := Curves[BLS12_381_GURVY] - rk := kilic.NewRandomZr(rng) + rk := canonical.NewRandomZr(rng) rg := gurvy.NewZrFromBytes(rk.Bytes()) assert.Equal(t, rk.Bytes(), rg.Bytes()) g1g := gurvy.GenG1.Mul(rg) - g1k := kilic.GenG1.Mul(rk) + g1k := canonical.GenG1.Mul(rk) assert.Equal(t, g1g.Bytes(), g1k.Bytes()) assert.Equal(t, g1g.Compressed(), g1k.Compressed()) g2g := gurvy.GenG2.Mul(rg) - g2k := kilic.GenG2.Mul(rk) + g2k := canonical.GenG2.Mul(rk) assert.Equal(t, g2g.Bytes(), g2k.Bytes()) assert.Equal(t, g2g.Compressed(), g2k.Compressed()) gtg := gurvy.GenGt.Exp(rg) - gtk := kilic.GenGt.Exp(rk) + gtk := canonical.GenGt.Exp(rk) assert.Equal(t, gtg.Bytes(), gtk.Bytes()) hg := gurvy.HashToG1([]byte("Chase!")) - hk := kilic.HashToG1([]byte("Chase!")) + hk := canonical.HashToG1([]byte("Chase!")) assert.Equal(t, hg.Bytes(), hk.Bytes()) hg = gurvy.HashToG1WithDomain([]byte("CD"), []byte("EF")) - hk = kilic.HashToG1WithDomain([]byte("CD"), []byte("EF")) + hk = canonical.HashToG1WithDomain([]byte("CD"), []byte("EF")) assert.Equal(t, hg.Bytes(), hk.Bytes()) } +// Test381BBSCompat verifies that the canonical BLS12_381_BBS curve ID (now +// backed by the gnark-crypto/gurvy implementation, formerly by kilic) is +// byte-compatible with its explicit BLS12_381_BBS_GURVY sibling. func Test381BBSCompat(t *testing.T) { rng, err := Curves[BLS12_381_BBS].Rand() require.NoError(t, err) - kilic := Curves[BLS12_381_BBS] + canonical := Curves[BLS12_381_BBS] gurvy := Curves[BLS12_381_BBS_GURVY] - rk := kilic.NewRandomZr(rng) + rk := canonical.NewRandomZr(rng) rg := gurvy.NewZrFromBytes(rk.Bytes()) assert.Equal(t, rk.Bytes(), rg.Bytes()) g1g := gurvy.GenG1.Mul(rg) - g1k := kilic.GenG1.Mul(rk) + g1k := canonical.GenG1.Mul(rk) assert.Equal(t, g1g.Bytes(), g1k.Bytes()) assert.Equal(t, g1g.Compressed(), g1k.Compressed()) g2g := gurvy.GenG2.Mul(rg) - g2k := kilic.GenG2.Mul(rk) + g2k := canonical.GenG2.Mul(rk) assert.Equal(t, g2g.Bytes(), g2k.Bytes()) assert.Equal(t, g2g.Compressed(), g2k.Compressed()) gtg := gurvy.GenGt.Exp(rg) - gtk := kilic.GenGt.Exp(rk) + gtk := canonical.GenGt.Exp(rk) assert.Equal(t, gtg.Bytes(), gtk.Bytes()) hg := gurvy.HashToG1([]byte("Chase!")) - hk := kilic.HashToG1([]byte("Chase!")) + hk := canonical.HashToG1([]byte("Chase!")) assert.Equal(t, hg.Bytes(), hk.Bytes()) hg = gurvy.HashToG1WithDomain([]byte("CD"), []byte("EF")) - hk = kilic.HashToG1WithDomain([]byte("CD"), []byte("EF")) + hk = canonical.HashToG1WithDomain([]byte("CD"), []byte("EF")) assert.Equal(t, hg.Bytes(), hk.Bytes()) } diff --git a/perf_test.go b/perf_test.go index b820f56..8dd1b21 100644 --- a/perf_test.go +++ b/perf_test.go @@ -14,7 +14,6 @@ import ( bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" - kilic "github.com/kilic/bls12-381" ) func newRandZr(rng io.Reader, m *big.Int) *big.Int { @@ -51,20 +50,6 @@ func blsInitGurvy(b *testing.B) (*bls12381.G2Affine, *big.Int) { return g, x } -func blsInitKilic(b *testing.B) (*kilic.PointG2, *big.Int) { - b.Helper() - rng := rand.Reader - - _g := kilic.NewG2() - g2 := _g.One() - g := _g.New() - - _g.MulScalarBig(g, g2, newRandZr(rng, fr.Modulus())) - x := newRandZr(rng, fr.Modulus()) - - return g, x -} - func pokPedersenCommittmentInit(b *testing.B, curve *Curve) (io.Reader, *G1, *G1, *Zr, error) { b.Helper() rng, err := curve.Rand() @@ -92,66 +77,6 @@ func pokPedersenCommittmentInitGurvy(b *testing.B) (io.Reader, *bls12381.G1Affin return rng, g, h, x } -func pokPedersenCommittmentInitKilic(b *testing.B) (io.Reader, *kilic.PointG1, *kilic.PointG1, *big.Int) { - b.Helper() - rng := rand.Reader - - _g := kilic.NewG1() - g1 := _g.One() - g := _g.New() - h := _g.New() - - _g.MulScalarBig(g, g1, newRandZr(rng, fr.Modulus())) - _g.MulScalarBig(h, g1, newRandZr(rng, fr.Modulus())) - x := newRandZr(rng, fr.Modulus()) - - return rng, g, h, x -} - -func Benchmark_Sequential_PedersenCommitmentPoKKilic(b *testing.B) { - rng, g, h, x := pokPedersenCommittmentInitKilic(b) - _g := kilic.NewG1() - tmp := _g.New() - mod := fr.Modulus() - - b.ResetTimer() - - b.Run("curve BLS12_381 (direct)", func(b *testing.B) { - for range b.N { - r := newRandZr(rng, mod) - c := _g.New() - _g.MulScalarBig(c, g, x) - _g.MulScalarBig(tmp, h, r) - _g.Add(c, c, tmp) - - x_tilde := newRandZr(rng, mod) - r_tilde := newRandZr(rng, mod) - t := _g.New() - _g.MulScalarBig(t, g, x_tilde) - _g.MulScalarBig(tmp, h, r_tilde) - _g.Add(t, t, tmp) - - chal := newRandZr(rng, mod) - - x_hat := new(big.Int).Add(x_tilde, new(big.Int).Mul(chal, x)) - r_hat := new(big.Int).Add(r_tilde, new(big.Int).Mul(chal, r)) - - v1 := _g.New() - _g.MulScalarBig(v1, g, x_hat) - _g.MulScalarBig(tmp, h, r_hat) - _g.Add(v1, v1, tmp) - - v2 := _g.New() - _g.MulScalarBig(v2, c, chal) - _g.Add(v2, v2, t) - - if !_g.Equal(v1, v2) { - panic("invalid PoK") - } - } - }) -} - func Benchmark_Sequential_PedersenCommitmentPoKGurvy(b *testing.B) { rng, g, h, x := pokPedersenCommittmentInitGurvy(b) @@ -311,56 +236,6 @@ func Benchmark_Parallel_BLSGurvy(b *testing.B) { }) } -func Benchmark_Parallel_BLSKilic(b *testing.B) { - g, x := blsInitKilic(b) - _g := kilic.NewG2() - pk := _g.New() - _g.MulScalarBig(pk, g, x) - - b.ResetTimer() - - var sig *kilic.PointG1 - - b.Run("sign curve BLS12_381_GURVY (direct)", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - _g := kilic.NewG1() - for pb.Next() { - g1 := kilic.NewG1() - h, err := g1.HashToCurve([]byte("msg"), []byte("context")) - if err != nil { - panic(err) - } - - sig = _g.New() - _g.MulScalarBig(sig, h, x) - } - }) - }) - - kilic.NewG1().Neg(sig, sig) - - b.ResetTimer() - - b.Run("verify curve BLS12_381_GURVY (direct)", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - g1 := kilic.NewG1() - h, err := g1.HashToCurve([]byte("msg"), []byte("context")) - if err != nil { - panic(err) - } - bls := kilic.NewEngine() - bls.AddPair(sig, g) - bls.AddPair(h, pk) - - if !bls.Check() { - panic("invalid signature") - } - } - }) - }) -} - func Benchmark_Parallel_BLS(b *testing.B) { for _, curve := range Curves { if curve.curveID != BLS12_381 && curve.curveID != BLS12_381_GURVY { @@ -481,81 +356,3 @@ func Benchmark_Parallel_IndividualOpsGurvy(b *testing.B) { }) }) } - -func Benchmark_Parallel_IndividualOpsKilic(b *testing.B) { - curve := Curves[BLS12_381] - g_kili, x_kili := blsInitKilic(b) - g_math, x_math, err := blsInit(b, curve) - if err != nil { - panic(err) - } - - _g := kilic.NewG2() - pk_kili := _g.New() - _g.MulScalarBig(pk_kili, g_kili, x_kili) - pk_math := g_math.Mul(x_math) - - var h_kili *kilic.PointG1 - var h_math *G1 - - var sig_kili *kilic.PointG1 - var sig_math *G1 - - var t_math *Gt - - b.Run("hash/kilic", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - g1 := kilic.NewG1() - h_kili, _ = g1.HashToCurve([]byte("msg"), []byte("context")) - } - }) - }) - - b.Run("hash/mathlib", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - h_math = curve.HashToG1WithDomain([]byte("msg"), []byte("context")) - } - }) - }) - - b.Run("sign/kilic", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - _g := kilic.NewG1() - for pb.Next() { - sig_kili = _g.New() - _g.MulScalarBig(sig_kili, h_kili, x_kili) - } - }) - }) - - b.Run("sign/mathlib", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - sig_math = h_math.Mul(x_math) - } - }) - }) - - b.Run("pairing2/kilic", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - bls := kilic.NewEngine() - bls.AddPair(sig_kili, g_kili) - bls.AddPair(h_kili, pk_kili) - bls.Result() - } - }) - }) - - b.Run("pairing2/mathlib", func(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - t_math = curve.Pairing2(g_math, sig_math, pk_math, h_math) - - t_math = curve.FExp(t_math) - } - }) - }) -} From 7f149c84f795a911bbed6644dd3c94da96d75cdb Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 8 Jul 2026 06:53:23 +0200 Subject: [PATCH 2/2] linter conf fix Signed-off-by: Angelo De Caro --- .golangci.yml | 136 ++++++++++---------------------------------------- 1 file changed, 27 insertions(+), 109 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index fd5402c..088cd6a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,87 +1,42 @@ version: "2" linters: enable: + - asasalint # Check for pass []any as any in variadic func(...any). - canonicalheader # Canonicalheader checks whether net/http.Header uses canonical header. [auto-fix] + - copyloopvar # A linter detects places where loop variables are copied. [fast, auto-fix] + - depguard # Go linter that checks if package imports are in a list of acceptable packages. [fast] - dupword # Checks for duplicate words in the source code. [fast, auto-fix] + - durationcheck # Check for two durations multiplied together. - errorlint # Find code that can cause problems with the error wrapping scheme introduced in Go 1.13. [auto-fix] - exptostd # Detects functions from golang.org/x/exp/ that can be replaced by std functions. [auto-fix] - ginkgolinter # Enforces standards of using ginkgo and gomega. [auto-fix] - goheader # Check if file header matches to pattern. [fast, auto-fix] + - gosec # Inspects source code for security problems. - govet # Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. [auto-fix] - importas # Enforces consistent import aliases. [auto-fix] + - intrange # Intrange is a linter to find places where for loops could make use of an integer range. [auto-fix] + - loggercheck # Checks key value pairs for common logger libraries (kitlog,klog,logr,slog,zap). - mirror # Reports wrong mirror patterns of bytes/strings usage. [auto-fix] - misspell # Finds commonly misspelled English words. [fast, auto-fix] - nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). [fast, auto-fix] + - nilerr # Find the code that returns nil even if it checks that the error is not nil. + - nilnesserr # Reports constructs that checks for err != nil, but returns a different nil value error. + - nlreturn # Checks for a new line before return and branch statements to increase code clarity. [fast, auto-fix] - nolintlint # Reports ill-formed or insufficient nolint directives. [fast, auto-fix] + - perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [auto-fix] + - reassign # Checks that package variables are not reassigned. - sloglint # Ensure consistent code style when using log/slog. [auto-fix] + - spancheck # Checks for mistakes with OpenTelemetry/Census spans. - staticcheck # It's the set of rules from staticcheck. [auto-fix] + - tagalign # Check that struct tags are well aligned. [fast, auto-fix] + - testifylint # Checks usage of github.com/stretchr/testify. [auto-fix] + - thelper # Thelper detects tests helpers which do not start with the t.Helper() method. + - unconvert # Remove unnecessary type conversions. - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast, auto-fix] - usetesting # Reports uses of functions with replacement inside the testing package. [auto-fix] + - wastedassign # Finds wasted assignment statements. - whitespace # Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc. [fast, auto-fix] - - copyloopvar # A linter detects places where loop variables are copied. [fast, auto-fix] -# - err113 # Check errors handling expressions. [auto-fix] -# - fatcontext # Detects nested contexts in loops and function literals. [auto-fix] -# - gocritic # Provides diagnostics that check for bugs, performance and style issues. [auto-fix] -# - iface # Detect the incorrect use of interfaces, helping developers avoid interface pollution. [auto-fix] - - intrange # Intrange is a linter to find places where for loops could make use of an integer range. [auto-fix] - - nlreturn # Checks for a new line before return and branch statements to increase code clarity. [fast, auto-fix] - - perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [auto-fix] -# - protogetter # Reports direct reads from proto message fields when getters should be used. [auto-fix] -# - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [auto-fix] - - tagalign # Check that struct tags are well aligned. [fast, auto-fix] - - testifylint # Checks usage of github.com/stretchr/testify. [auto-fix] -# - wsl_v5 # Add or remove empty lines. [auto-fix] -# - containedctx -# - contextcheck - - depguard -# - dupl -# - errname - - errorlint -# - fatcontext -# - forcetypeassert - - ginkgolinter -# - gocognit -# - goconst -# - gocritic -# - godot - - gosec -# - intrange -# - ireturn -# - lll -# - maintidx - - mirror - - misspell - - nilerr - - nilnesserr - - nolintlint -# - paralleltest -# - prealloc -# - promlinter -# - revive -# - rowserrcheck -# - sqlclosecheck -# - testifylint - - thelper - - unconvert -# - unparam - - usetesting - - wastedassign - - whitespace - - govet -# - staticcheck - - canonicalheader -# - copyloopvar - - dupword -# - err113 - - exptostd - - goheader - - importas -# - intrange - - nakedret - - sloglint - - usestdlibvars - # wrapcheck is not working as expected. It is categorizing internal packages as external. - #- wrapcheck + - zerologlint # Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg`. settings: depguard: rules: @@ -91,19 +46,13 @@ linters: desc: github.com/pkg/errors is no longer maintained errcheck: # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. - # Such cases aren't reported by default. -# Default: false check-type-assertions: false errorlint: # Check whether fmt.Errorf uses the %w verb for formatting errors. - # See the https://github.com/polyfloyd/go-errorlint for caveats. - # Default: true errorf: true # Check for plain type assertions and type switches. - # Default: true asserts: false # Check for plain error comparisons. - # Default: true comparison: true fatcontext: check-struct-pointers: true @@ -118,7 +67,6 @@ linters: disable: - fieldalignment # Enable all analyzers. - # Default: false enable-all: false iface: enable: @@ -141,8 +89,6 @@ linters: - github.com/prometheus/client_golang/prometheus.Histogram lll: # Max line length, lines longer will be reported. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option. - # Default: 120. line-length: 240 maintidx: under: 20 @@ -191,18 +137,11 @@ linters: - github.com/jackc/pgx/v4 wrapcheck: # An array of strings specifying additional substrings of signatures to ignore. - # Unlike 'ignoreSigs', this option extends the default set (or the set specified in 'ignoreSigs') without replacing it entirely. - # This allows you to add specific signatures to the ignore list - # while retaining the defaults or any items in 'ignoreSigs'. - # Default: [] extra-ignore-sigs: - .CustomError( - .SpecificWrap( # An array of strings that specify substrings of signatures to ignore. - # If this set, it will override the default set of ignored signatures. - # See https://github.com/tomarrell/wrapcheck#configuration for more information. - # Default: [".Errorf(", "errors.New(", "errors.Unwrap(", "errors.Join(", ".Wrap(", ".Wrapf(", ".WithMessage(", ".WithMessagef(", ".WithStack("] ignore-sigs: - errors.New( - errors.Newf( @@ -211,36 +150,15 @@ linters: - .Wrap( - .Wrapf( - status.Error( - - .Wait() # The error reported by errorgroup.Wait() (external) actually - # originates from the internal code. + - .Wait() # The error reported by errorgroup.Wait() (external) actually originates from the internal code. # An array of strings that specify regular expressions of signatures to ignore. - # Default: [] ignore-sig-regexps: - \.New.*Error\( - # An array of strings that specify globs of packages to ignore. - # Default: [] - ignore-package-globs: - - github.ibm.com/decentralized-trust-research/scalable-committer/utils/grpcerror - # An array of strings that specify regular expressions of interfaces to ignore. - # Default: [] - # ignoreInterfaceRegexps: - # - ^(?i)c(?-i)ach(ing|e) exclusions: - # Mode of the generated files analysis. - # - # - `strict`: sources are excluded by strictly following the Go generated file convention. - # Source files that have lines matching only the following regular expression will be excluded: `^// Code generated .* DO NOT EDIT\.$` - # This line must appear before the first non-comment, non-blank text in the file. - # https://go.dev/s/generatedcode - # - `lax`: sources are excluded if they contain lines like `autogenerated file`, `code generated`, `do not edit`, etc. - # - `disable`: disable the generated files exclusion. - # - # Default: lax - generated: lax - # Log a warning if an exclusion rule is unused. - # Default: false - warn-unused: true - # Predefined exclusion rules. - # Default: [] - presets: - - common-false-positives \ No newline at end of file + # Mode of the generated files analysis. + generated: lax + # Log a warning if an exclusion rule is unused. + warn-unused: true + # Predefined exclusion rules. + presets: + - common-false-positives