Skip to content

bls12: replacing big.Int by fiat-crypto arithmetic. #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions ecc/bls12381/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package bls12381

import "github.com/cloudflare/circl/ecc/bls12381/ff"

// Scalar represents positive integers such that 0 <= x < Order, where
// Order = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
type Scalar = ff.Scalar

const ScalarSize = ff.ScalarSize

var (
g1ParamB = ff.Fp{ /*4*/ } // 4
g1Param3B = ff.Fp{ /*12*/ } // 3*G1ParamB
Expand All @@ -13,27 +19,24 @@ var (
// 0xcaa232946c5e7e1, 0xd03cc744a2888ae4, 0xdb18cb2c04b3ed,
// 0xfcf5e095d5d00af6, 0xa09e30ed741d8ae4, 0x8b3f481e3aaa0f1,
}
g2ParamB = ff.Fp2{}
g2Param3B = ff.Fp2{}
g2GenX = ff.Fp2{}
g2GenY = ff.Fp2{}
primeOrder Scalar
g2ParamB = ff.Fp2{}
g2Param3B = ff.Fp2{}
g2GenX = ff.Fp2{}
g2GenY = ff.Fp2{}
)

func init() {
g1ParamB.SetUint64(4)
g1Param3B.SetUint64(12)
g1GenX.SetString("0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb")
g1GenY.SetString("0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1")
_ = g1GenX.SetString("0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb")
_ = g1GenY.SetString("0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1")

g2ParamB[0].SetUint64(4)
g2ParamB[1].SetUint64(4)
g2Param3B[0].SetUint64(12)
g2Param3B[1].SetUint64(12)
g2GenX[0].SetString("0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8")
g2GenX[1].SetString("0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e")
g2GenY[0].SetString("0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801")
g2GenY[1].SetString("0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be")

primeOrder.SetString("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")
_ = g2GenX[0].SetString("0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8")
_ = g2GenX[1].SetString("0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e")
_ = g2GenY[0].SetString("0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801")
_ = g2GenY[1].SetString("0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be")
}
80 changes: 80 additions & 0 deletions ecc/bls12381/ff/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Package ff provides finite fields of characteristic P381.
package ff

import (
"crypto/rand"
"crypto/subtle"
"encoding/binary"
"errors"
"io"
"math/big"

"github.com/cloudflare/circl/internal/conv"
)

func errFirst(e ...error) error {
for _, err := range e {
if err != nil {
return err
}
}
return nil
}

func setString(out []uint64, in string, order []byte) error {
inBig, ok := new(big.Int).SetString(in, 0)
if !ok {
return errors.New("invalid string")
}
inBytes := make([]byte, len(order))
conv.BigInt2BytesLe(inBytes, inBig)
return setBytes(out, inBytes, order)
}

func setBytes(out []uint64, in []byte, order []byte) error {
if len(in) != len(order) {
return errors.New("input length incorrect")
}
if !isLessThan(in, order) {
return errors.New("value out of [0,order)")
}

for i := range out {
out[i] = binary.LittleEndian.Uint64(in[i*8:])
}
return nil
}

// isLessThan returns true if 0 <= x < y, and assumes that slices have the same length.
func isLessThan(x, y []byte) bool {
i := len(x) - 1
for i > 0 && x[i] == y[i] {
i--
}
return x[i] < y[i]
}

func randomInt(out []uint64, rnd io.Reader, order []byte) error {
r, err := rand.Int(rnd, conv.BytesLe2BigInt(order))
if err == nil {
conv.BigInt2Uint64Le(out, r)
}
return err
}

// ctUint64Eq returns 1 if the two slices have equal contents and 0 otherwise.
func ctUint64Eq(x, y []uint64) int {
if len(x) != len(y) {
return 0
}
l := len(x)
var v uint64
for i := 0; i < l; i++ {
v |= x[i] ^ y[i]
}

v8 := byte(v) | byte(v>>8) | byte(v>>16) | byte(v>>24) |
byte(v>>32) | byte(v>>40) | byte(v>>48) | byte(v>>56)

return subtle.ConstantTimeByteEq(v8, 0)
}
28 changes: 0 additions & 28 deletions ecc/bls12381/ff/const.go

This file was deleted.

90 changes: 48 additions & 42 deletions ecc/bls12381/ff/cyclo6.go
Original file line number Diff line number Diff line change
@@ -1,59 +1,39 @@
package ff

import "fmt"

// Cyclo6 represents an element of the 6th cyclotomic group.
//
// References: https://eprint.iacr.org/2009/565
type Cyclo6 [2]Fp6
type Cyclo6 Fp12

func (z Cyclo6) String() string { return fmt.Sprintf("\n0: %v\n1: %v", z[0], z[1]) }
func (z *Cyclo6) Set(x *Cyclo6) { z[0].Set(&x[0]); z[1].Set(&x[1]) }
func (z *Cyclo6) Inv(x *Cyclo6) { z.Set(x); z[1].Neg() }
func (z *Cyclo6) SetIdentity() { z[0].SetOne(); z[1].SetZero() }
func (z *Cyclo6) IsIdentity() bool { i := new(Cyclo6); i.SetIdentity(); return z.IsEqual(i) }
func (z *Cyclo6) IsEqual(x *Cyclo6) bool { return z[0].IsEqual(&x[0]) && z[1].IsEqual(&x[1]) }
func (z *Cyclo6) Frob(x *Cyclo6) { z[0].Frob(&x[0]); z[1].Frob(&x[1]); z[1].Mul(&z[1], &frob12W1) }
func (z *Cyclo6) Mul(x, y *Cyclo6) {
var x0y0, x1y1, sx, sy, k Fp6
x0y0.Mul(&x[0], &y[0])
x1y1.Mul(&x[1], &y[1])
sx.Add(&x[0], &x[1])
sy.Add(&y[0], &y[1])
k.Mul(&sx, &sy)
z[1].Sub(&k, &x0y0)
z[1].Sub(&z[1], &x1y1)
x1y1.MulBeta()
z[0].Add(&x0y0, &x1y1)
}
func (z *Cyclo6) Sqr(x *Cyclo6) {
var x02, x12, k Fp6
x02.Sqr(&x[0])
x12.Sqr(&x[1])
x12.MulBeta()
k.Mul(&x[0], &x[1])
z[0].Add(&x02, &x12)
z[1].Add(&k, &k)
}
func (z Cyclo6) String() string { return (Fp12)(z).String() }

func (z *Cyclo6) Set(x *Cyclo6) { (*Fp12)(z).Set((*Fp12)(x)) }
func (z Cyclo6) IsEqual(x *Cyclo6) bool { return (Fp12)(z).IsEqual((*Fp12)(x)) }
func (z Cyclo6) IsIdentity() bool { i := &Fp12{}; i.SetOne(); return z.IsEqual((*Cyclo6)(i)) }
func (z *Cyclo6) Frob(x *Cyclo6) { (*Fp12)(z).Frob((*Fp12)(x)) }
func (z *Cyclo6) Mul(x, y *Cyclo6) { (*Fp12)(z).Mul((*Fp12)(x), (*Fp12)(y)) }
func (z *Cyclo6) Sqr(x *Cyclo6) { (*Fp12)(z).Sqr((*Fp12)(x)) }
func (z *Cyclo6) Inv(x *Cyclo6) { z.Set(x); z[1].Neg() }
func (z *Cyclo6) expVarTime(x *Cyclo6, n []byte) { (*Fp12)(z).ExpVarTime((*Fp12)(x), n) }

// PowToX computes z = x^paramX, where paramX is the parameter of the BLS curve.
func (z *Cyclo6) PowToX(x *Cyclo6) {
var t Cyclo6
t := new(Cyclo6)
t.Set(x)
const lenX = 64
for i := lenX - 2; i >= 0; i-- {
t.Sqr(&t)
t.Sqr(t)
// paramX is -2 ^ 63 - 2 ^ 62 - 2 ^ 60 - 2 ^ 57 - 2 ^ 48 - 2 ^ 16
if (i == 62) || (i == 60) || (i == 57) || (i == 48) || (i == 16) {
t.Mul(&t, x)
t.Mul(t, x)
}
}
z.Inv(&t)
z.Inv(t)
}

// EasyExponentiation raises f^(p^6-1)(p^2+1) and returns an element in the
// 6-th cyclotomic group.
func EasyExponentiation(f *Fp12) *Cyclo6 {
// EasyExponentiation calculates g = f^(p^6-1)(p^2+1), where g becomes an
// element of the 6-th cyclotomic group.
func EasyExponentiation(g *Cyclo6, f *Fp12) {
var t0, t1, p Fp12
p.Frob(f) // p = f^(p)
p.Frob(&p) // p = f^(p^2)
Expand All @@ -67,8 +47,34 @@ func EasyExponentiation(f *Fp12) *Cyclo6 {
t0.Inv(&t0) // t0 = f^-(p^2 + 1)
t0.Mul(&t0, &t1) // t0 = f^(p^2 + 1)*(p^6 - 1)

var g Cyclo6
g[0].Set(&t0[0])
g[1].Set(&t0[1])
return &g
*g = (Cyclo6)(t0)
}

// HardExponentiation calculates u = g^(Cy_6(p)/r), where u is a root of unity.
func HardExponentiation(u *URoot, g *Cyclo6) {
var t0, t1, _g, g3 Cyclo6
var c, a0, a1, a2, a3 Cyclo6
_g.Inv(g) // _g = g^-1
g3.Sqr(g) // g3 = g^2
g3.Mul(&g3, g) // g3 = g^3
t0.PowToX(g) // t0 = g^x
t0.Mul(&t0, &_g) // t0 = g^(x-1)
t1.PowToX(&t0) // t1 = g^(x-1)*x
t0.Inv(&t0) // t0 = g^-(x-1)
a3.Mul(&t1, &t0) // a3 = g^(x-1)*(x-1)
a2.Frob(&a3) // a2 = a3*p
a1.Frob(&a2) // a1 = a2*p = a3*p^2
t0.Inv(&a3) // t0 = -a3
a1.Mul(&a1, &t0) // a1 = a3*p^2-a3
a0.Frob(&a1) // a0 = a3*p^3-a3*p
a0.Mul(&a0, &g3) // a0 = a3*p^3-a3*p+3

c.PowToX(&a3) // c = g^(a3*x)
c.Mul(&c, &a2) // c = g^(a3*x+a2)
c.PowToX(&c) // c = g^(a3*x+a2)*x = g^(a3*x^2+a2*x)
c.Mul(&c, &a1) // c = g^(a3*x^2+a2*x+a1)
c.PowToX(&c) // c = g^(a3*x^2+a2*x+a1)*x = g^(a3*x^3+a2*x^2+a1*x)
c.Mul(&c, &a0) // c = g^(a3*x^3+a2*x^2+a1*x+a0)

*u = (URoot)(c)
}
61 changes: 40 additions & 21 deletions ecc/bls12381/ff/cyclo6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,28 @@ import (
"github.com/cloudflare/circl/internal/test"
)

func randomCyclo6() *Cyclo6 { return EasyExponentiation(randomFp12()) }
func randomCyclo6(t testing.TB) *Cyclo6 {
c := &Cyclo6{}
EasyExponentiation(c, randomFp12(t))
return c
}

// phi6primeSq evaluates the 6-th cyclotomic polynomial, \phi_6(x) = x^2-x+1, at p^2.
func phi6primeSq() *big.Int {
one := new(big.Int).SetUint64(1)
p2 := new(big.Int).Mul(blsPrime, blsPrime) // p^2
p := new(big.Int).Sub(p2, one) // p^2 - 1
p.Mul(p, p2) // p^4 - p^2
p.Add(p, one) // p^4 - p^2 + 1
return p
one := big.NewInt(1)
p := conv.BytesLe2BigInt(fpOrder[:]) // p
p2 := new(big.Int).Mul(p, p) // p^2
p4 := new(big.Int).Sub(p2, one) // p^2 - 1
p4.Mul(p4, p2) // p^4 - p^2
p4.Add(p4, one) // p^4 - p^2 + 1
return p4
}

func TestCyclo6(t *testing.T) {
const testTimes = 1 << 10
t.Run("no_alias", func(t *testing.T) {
var want, got Cyclo6
x := randomCyclo6()
x := randomCyclo6(t)
got.Set(x)
got.Sqr(&got)
want.Set(x)
Expand All @@ -35,29 +40,27 @@ func TestCyclo6(t *testing.T) {
})
t.Run("order", func(t *testing.T) {
cyclo6Order := phi6primeSq()
l := (cyclo6Order.BitLen() + 7) >> 3
cyclo6OrderBytes := make([]byte, l)
cyclo6OrderBytes := make([]byte, (cyclo6Order.BitLen()+7)/8)
conv.BigInt2BytesLe(cyclo6OrderBytes, cyclo6Order)

var z12 Fp12
var z Cyclo6
for i := 0; i < 16; i++ {
x := randomCyclo6()
z12.Exp((*Fp12)(x), cyclo6OrderBytes)
z := Cyclo6(z12)
x := randomCyclo6(t)
z.expVarTime(x, cyclo6OrderBytes)

// x^phi6primeSq = 1
got := z.IsIdentity()
want := true
if got != want {
test.ReportError(t, got, want, x)
test.ReportError(t, got, want, x, z)
}
}
})
t.Run("mul_inv", func(t *testing.T) {
var z Cyclo6
for i := 0; i < testTimes; i++ {
x := randomCyclo6()
y := randomCyclo6()
x := randomCyclo6(t)
y := randomCyclo6(t)

// x*y*x^1 = y
z.Inv(x)
Expand All @@ -73,7 +76,7 @@ func TestCyclo6(t *testing.T) {
t.Run("mul_sqr", func(t *testing.T) {
var want, got Cyclo6
for i := 0; i < testTimes; i++ {
x := randomCyclo6()
x := randomCyclo6(t)

// x*x = x^2
got.Mul(x, x)
Expand All @@ -83,12 +86,28 @@ func TestCyclo6(t *testing.T) {
}
}
})

t.Run("invFp12_vs_invCyclo6", func(t *testing.T) {
var want, got Fp12
var y Cyclo6
for i := 0; i < testTimes; i++ {
x := randomCyclo6(t)

y.Inv(x)
got = (Fp12)(y)
want.Inv((*Fp12)(x))

if !got.IsEqual(&want) {
test.ReportError(t, got, want, x)
}
}
})
}

func BenchmarkCyclo6(b *testing.B) {
x := randomCyclo6()
y := randomCyclo6()
z := randomCyclo6()
x := randomCyclo6(b)
y := randomCyclo6(b)
z := randomCyclo6(b)
b.Run("Mul", func(b *testing.B) {
for i := 0; i < b.N; i++ {
z.Mul(x, y)
Expand Down
Loading