Skip to content

Commit

Permalink
Update bn256 from cloudflare's changes (#518)
Browse files Browse the repository at this point in the history
* Moved scheme.go and threshold.go into internals and uncommented bls_test.go
* Removed nerr++ in favor of len(errors)
* Changed the path for test
* Sorting imports
* Update bn256 from cloudflare's changes
* Added the bn256/hash.go and its tests
  • Loading branch information
Robingoumaz authored May 24, 2024
1 parent 1f8934d commit c97576b
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 8 deletions.
2 changes: 1 addition & 1 deletion pairing/bn256/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ The basis for this package is [Cloudflare's bn256 implementation](https://github
which itself is an improved version of the [official bn256 package](https://golang.org/x/crypto/bn256).
The package at hand maintains compatibility to Cloudflare's library. The biggest difference is the replacement of their
[public API](https://github.com/cloudflare/bn256/blob/master/bn256.go) by a new
one that is compatible to Kyber's scalar, point, group, and suite interfaces.
one that is compatible to Kyber's scalar, point, group, and suite interfaces. *Last update 05.2024*
15 changes: 15 additions & 0 deletions pairing/bn256/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,18 @@ var r2 = &gfP{0x9c21c3ff7e444f56, 0x409ed151b2efb0c2, 0xc6dc37b80fb1651, 0x7c36e

// r3 is R^3 where R = 2^256 mod p.
var r3 = &gfP{0x2af2dfb9324a5bb8, 0x388f899054f538a4, 0xdf2ff66396b107a7, 0x24ebbbb3a2529292}

// pPlus1Over4 is (p+1)/4.
var pPlus1Over4 = [4]uint64{0x86172b1b1782259a, 0x7b96e234482d6d67, 0x6a9bfb2e18613708, 0x23ed4078d2a8e1fe}

// pMinus2 is p-2.
var pMinus2 = [4]uint64{0x185cac6c5e089665, 0xee5b88d120b5b59e, 0xaa6fecb86184dc21, 0x8fb501e34aa387f9}

// pMinus1Over2 is (p-1)/2.
var pMinus1Over2 = [4]uint64{0x0c2e56362f044b33, 0xf72dc468905adacf, 0xd537f65c30c26e10, 0x47da80f1a551c3fc}

// s is the Montgomery encoding of the square root of -3. Then, s = sqrt(-3) * 2^256 mod p.
var s = &gfP{0x236e675956be783b, 0x053957e6f379ab64, 0xe60789a768f4a5c4, 0x04f8979dd8bad754}

// sMinus1Over2 is the Montgomery encoding of (s-1)/2. Then, sMinus1Over2 = ( (s-1) / 2) * 2^256 mod p.
var sMinus1Over2 = &gfP{0x3642364f386c1db8, 0xe825f92d2acd661f, 0xf2aba7e846c19d14, 0x5a0bcea3dc52b7a0}
66 changes: 63 additions & 3 deletions pairing/bn256/gfp.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package bn256

import (
"crypto/sha256"
"encoding/binary"
"fmt"
"golang.org/x/crypto/hkdf"
"math/big"
)

Expand Down Expand Up @@ -36,6 +39,29 @@ func newGFpFromBigInt(bigInt *big.Int) *gfP {
return out
}

func hashToBase(msg, dst []byte) *gfP {
var t [48]byte
info := []byte{'H', '2', 'C', byte(0), byte(1)}
r := hkdf.New(sha256.New, msg, dst, info)
if _, err := r.Read(t[:]); err != nil {
panic(err)
}
var x big.Int
v := x.SetBytes(t[:]).Mod(&x, p).Bytes()
v32 := [32]byte{}
for i := len(v) - 1; i >= 0; i-- {
v32[len(v)-1-i] = v[i]
}
u := &gfP{
binary.LittleEndian.Uint64(v32[0*8 : 1*8]),
binary.LittleEndian.Uint64(v32[1*8 : 2*8]),
binary.LittleEndian.Uint64(v32[2*8 : 3*8]),
binary.LittleEndian.Uint64(v32[3*8 : 4*8]),
}
montEncode(u, u)
return u
}

func (e *gfP) String() string {
return fmt.Sprintf("%16.16x%16.16x%16.16x%16.16x", e[3], e[2], e[1], e[0])
}
Expand All @@ -47,9 +73,7 @@ func (e *gfP) Set(f *gfP) {
e[3] = f[3]
}

func (e *gfP) Invert(f *gfP) {
bits := [4]uint64{0x185cac6c5e089665, 0xee5b88d120b5b59e, 0xaa6fecb86184dc21, 0x8fb501e34aa387f9}

func (e *gfP) exp(f *gfP, bits [4]uint64) {
sum, power := &gfP{}, &gfP{}
sum.Set(rN1)
power.Set(f)
Expand All @@ -67,6 +91,15 @@ func (e *gfP) Invert(f *gfP) {
e.Set(sum)
}

func (e *gfP) Invert(f *gfP) {
e.exp(f, pMinus2)
}

func (e *gfP) Sqrt(f *gfP) {
// Since p = 4k+3, then e = f^(k+1) is a root of f.
e.exp(f, pPlus1Over4)
}

func (e *gfP) Marshal(out []byte) {
for w := uint(0); w < 4; w++ {
for b := uint(0); b < 8; b++ {
Expand Down Expand Up @@ -96,3 +129,30 @@ func (e *gfP) BigInt() *big.Int {

func montEncode(c, a *gfP) { gfpMul(c, a, r2) }
func montDecode(c, a *gfP) { gfpMul(c, a, &gfP{1}) }

func sign0(e *gfP) int {
x := &gfP{}
montDecode(x, e)
for w := 3; w >= 0; w-- {
if x[w] > pMinus1Over2[w] {
return 1
} else if x[w] < pMinus1Over2[w] {
return -1
}
}
return 1
}

func legendre(e *gfP) int {
f := &gfP{}
// Since p = 4k+3, then e^(2k+1) is the Legendre symbol of e.
f.exp(e, pMinus1Over2)

montDecode(f, f)

if *f != [4]uint64{} {
return 2*int(f[0]&1) - 1
}

return 0
}
1 change: 0 additions & 1 deletion pairing/bn256/gfp_decl.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build (amd64 && !generic) || (arm64 && !generic)
// +build amd64,!generic arm64,!generic

package bn256

Expand Down
1 change: 0 additions & 1 deletion pairing/bn256/gfp_generic.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build (!amd64 && !arm64) || generic
// +build !amd64,!arm64 generic

package bn256

Expand Down
166 changes: 166 additions & 0 deletions pairing/bn256/gfp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package bn256

import (
"crypto/rand"
"encoding/binary"
"io"
"math/big"
"testing"
)

// randomGF returns a random integer between 0 and p-1.
func randomGF(r io.Reader) *big.Int {
k, err := rand.Int(r, p)
if err != nil {
panic(err)
}
return k
}

// toBigInt converts a field element into its reduced (mod p)
// integer representation.
func toBigInt(a *gfP) *big.Int {
v := &gfP{}
montDecode(v, a)
c := new(big.Int)
for i := len(v) - 1; i >= 0; i-- {
c.Lsh(c, 64)
c.Add(c, new(big.Int).SetUint64(v[i]))
}
return c
}

// togfP converts an integer into a field element (in
// Montgomery representation). This function assumes the
// input is between 0 and p-1; otherwise it panics.
func togfP(k *big.Int) *gfP {
if k.Cmp(p) >= 0 {
panic("not in the range 0 to p-1")
}
v := k.Bytes()
v32 := [32]byte{}
for i := len(v) - 1; i >= 0; i-- {
v32[len(v)-1-i] = v[i]
}
u := &gfP{
binary.LittleEndian.Uint64(v32[0*8 : 1*8]),
binary.LittleEndian.Uint64(v32[1*8 : 2*8]),
binary.LittleEndian.Uint64(v32[2*8 : 3*8]),
binary.LittleEndian.Uint64(v32[3*8 : 4*8]),
}
montEncode(u, u)
return u
}

func TestGFp(t *testing.T) {
const testTimes = 1 << 8

t.Run("add", func(t *testing.T) {
c := &gfP{}
bigC := new(big.Int)
for i := 0; i < testTimes; i++ {
bigA := randomGF(rand.Reader)
bigB := randomGF(rand.Reader)
want := bigC.Add(bigA, bigB).Mod(bigC, p)

a := togfP(bigA)
b := togfP(bigB)
gfpAdd(c, a, b)
got := toBigInt(c)

if got.Cmp(want) != 0 {
t.Errorf("got: %v want:%v", got, want)
}
}
})

t.Run("sub", func(t *testing.T) {
c := &gfP{}
bigC := new(big.Int)
for i := 0; i < testTimes; i++ {
bigA := randomGF(rand.Reader)
bigB := randomGF(rand.Reader)
want := bigC.Sub(bigA, bigB).Mod(bigC, p)

a := togfP(bigA)
b := togfP(bigB)
gfpSub(c, a, b)
got := toBigInt(c)

if got.Cmp(want) != 0 {
t.Errorf("got: %v want:%v", got, want)
}
}
})

t.Run("mul", func(t *testing.T) {
c := &gfP{}
bigC := new(big.Int)
for i := 0; i < testTimes; i++ {
bigA := randomGF(rand.Reader)
bigB := randomGF(rand.Reader)
want := bigC.Mul(bigA, bigB).Mod(bigC, p)

a := togfP(bigA)
b := togfP(bigB)
gfpMul(c, a, b)
got := toBigInt(c)

if got.Cmp(want) != 0 {
t.Errorf("got: %v want:%v", got, want)
}
}
})

t.Run("neg", func(t *testing.T) {
c := &gfP{}
bigC := new(big.Int)
for i := 0; i < testTimes; i++ {
bigA := randomGF(rand.Reader)
want := bigC.Neg(bigA).Mod(bigC, p)

a := togfP(bigA)
gfpNeg(c, a)
got := toBigInt(c)

if got.Cmp(want) != 0 {
t.Errorf("got: %v want:%v", got, want)
}
}
})

t.Run("inv", func(t *testing.T) {
c := &gfP{}
bigC := new(big.Int)
for i := 0; i < testTimes; i++ {
bigA := randomGF(rand.Reader)
want := bigC.ModInverse(bigA, p)

a := togfP(bigA)
c.Invert(a)
got := toBigInt(c)

if got.Cmp(want) != 0 {
t.Errorf("got: %v want:%v", got, want)
}
}
})

t.Run("sqrt", func(t *testing.T) {
c := &gfP{}
bigC := new(big.Int)
for i := 0; i < testTimes; i++ {
bigA := randomGF(rand.Reader)
bigA.Mul(bigA, bigA).Mod(bigA, p)
want := bigC.ModSqrt(bigA, p)

a := togfP(bigA)
c.Sqrt(a)
got := toBigInt(c)

if got.Cmp(want) != 0 {
t.Errorf("got: %v want:%v", got, want)
}
}
})
}
Loading

0 comments on commit c97576b

Please sign in to comment.