diff --git a/pairing/bn256/constants.go b/pairing/bn256/constants.go index 943751a07..8057dd38a 100644 --- a/pairing/bn256/constants.go +++ b/pairing/bn256/constants.go @@ -19,6 +19,10 @@ var p = bigFromBase10("650005496956466037327964387423599057428253581076230035718 // order-1 = (2**5) * 3 * 5743 * 280941149 * 130979359433191 * 491513138693455212421542731357 * 6518589491078791937 var Order = bigFromBase10("65000549695646603732796438742359905742570406053903786389881062969044166799969") +// g2Cofactor is the order of the twist group divided by the order of +// the G₂ subgroup +var g2Cofactor = bigFromBase10("65000549695646603732796438742359905743080310161342220753873227084684201343597") + // xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+3. var xiToPMinus1Over6 = &gfP2{gfP{0x25af52988477cdb7, 0x3d81a455ddced86a, 0x227d012e872c2431, 0x179198d3ea65d05}, gfP{0x7407634dd9cca958, 0x36d5bd6c7afb8f26, 0xf4b1c32cebd880fa, 0x6aa7869306f455f}} diff --git a/pairing/bn256/gfp.go b/pairing/bn256/gfp.go index f17d44657..ae1593180 100644 --- a/pairing/bn256/gfp.go +++ b/pairing/bn256/gfp.go @@ -1,6 +1,7 @@ package bn256 import ( + "crypto/subtle" "fmt" ) @@ -29,6 +30,13 @@ func (e *gfP) Set(f *gfP) { e[3] = f[3] } +func (e *gfP) Equals(f *gfP) bool { + var ebin, fbin [32]byte + e.Marshal(ebin[:]) + f.Marshal(fbin[:]) + return subtle.ConstantTimeCompare(ebin[:], fbin[:]) == 1 +} + func (e *gfP) Invert(f *gfP) { bits := [4]uint64{0x185cac6c5e089665, 0xee5b88d120b5b59e, 0xaa6fecb86184dc21, 0x8fb501e34aa387f9} diff --git a/pairing/bn256/gfp2.go b/pairing/bn256/gfp2.go index 5fa0ae2b4..969080a99 100644 --- a/pairing/bn256/gfp2.go +++ b/pairing/bn256/gfp2.go @@ -1,5 +1,9 @@ package bn256 +import ( + "crypto/subtle" +) + // For details of the algorithms used, see "Multiplication and Squaring on // Pairing-Friendly Fields, Devegili et al. // http://eprint.iacr.org/2006/471.pdf. @@ -10,6 +14,13 @@ type gfP2 struct { x, y gfP // value is xi+y. } +func gfP2Encode(in *gfP2) *gfP2 { + out := &gfP2{} + montEncode(&out.x, &in.x) + montEncode(&out.y, &in.y) + return out +} + func gfP2Decode(in *gfP2) *gfP2 { out := &gfP2{} montDecode(&out.x, &in.x) @@ -157,3 +168,84 @@ func (e *gfP2) Clone() gfP2 { return n } + +// Compute the norm in gfP of the element a in gfP2 +func (e *gfP) Norm(a *gfP2) *gfP { + t1, t2 := &gfP{}, &gfP{} + gfpMul(t1, &a.x, &a.x) + gfpMul(t2, &a.y, &a.y) + gfpAdd(t1, t1, t2) + + e.Set(t1) + return e +} + +// Compute a to the power of r, where r is expressed as a [4]uint64 +func (e *gfP2) Power(a *gfP2, r [4]uint64) *gfP2 { + sum := &gfP2{gfP{0}, *newGFp(1)} + power := &gfP2{} + power.Set(a) + for word := 0; word < 4; word++ { + for bit := uint(0); bit < 64; bit++ { + if (r[word]>>bit)&1 == 1 { + sum.Mul(sum, power) + } + power.Mul(power, power) + } + } + e.Set(sum) + return e +} + +func (e *gfP2) Equals(f *gfP2) bool { + var ebin, fbin [64]byte + e.x.Marshal(ebin[:32]) + e.y.Marshal(ebin[32:]) + f.x.Marshal(fbin[:32]) + f.y.Marshal(fbin[32:]) + return subtle.ConstantTimeCompare(ebin[:], fbin[:]) == 1 +} + +// Compute the square root of the element a in gfP2 +// Uses Algorithm 9 of https://eprint.iacr.org/2012/685.pdf +// In our case, n=1, and so q=p +// Returns nil if a is not a quadratic residue +func (e *gfP2) Sqrt(a *gfP2) *gfP2 { + if a.IsZero() { + e.SetZero() + return e + } + // (p-3)/4 + pm34 := [4]uint64{0x86172b1b17822599, 0x7b96e234482d6d67, + 0x6a9bfb2e18613708, 0x23ed4078d2a8e1fe} + // (p-1)/2 + pm12 := [4]uint64{0x0c2e56362f044b33, 0xf72dc468905adacf, + 0xd537f65c30c26e10, 0x47da80f1a551c3fc} + + a1, x0, alpha := &gfP2{}, &gfP2{}, &gfP2{} + norm := &gfP{} + minus1gfP := newGFp(-1) + minus1 := &gfP2{gfP{0}, *newGFp(-1)} + one := &gfP2{gfP{0}, *newGFp(1)} + + a1.Power(a, pm34) + x0.Mul(a1, a) + alpha.Mul(a1, x0) + norm.Norm(alpha) + if norm.Equals(minus1gfP) { + return nil + } + if alpha.Equals(minus1) { + // Set e = i * x0 + copy(e.x[:], x0.y[:]) + gfpNeg(&e.y, &x0.x) + } else { + b := &gfP2{} + // Compute b = (1+alpha)^((p-1)/2) + b.Add(alpha, one) + b.Power(b, pm12) + // e = b * x0 + e.Mul(b, x0) + } + return e +} diff --git a/pairing/bn256/gfp2_test.go b/pairing/bn256/gfp2_test.go new file mode 100644 index 000000000..c4ee58a9e --- /dev/null +++ b/pairing/bn256/gfp2_test.go @@ -0,0 +1,95 @@ +package bn256 + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func TestGfP2Norm(t *testing.T) { + p := &gfP2{*newGFp(7), *newGFp(18)} + expectednorm := *newGFp(7*7 + 18*18) + var norm gfP + norm.Norm(p) + if norm != expectednorm { + t.Error("Norm mismatch") + } +} + +func TestGfP2Power(t *testing.T) { + a := &gfP2{gfP{2}, gfP{3}} + ap := &gfP2{} + a = gfP2Encode(a) + + // The characteristic p + p := [4]uint64{0x185cac6c5e089667, 0xee5b88d120b5b59e, 0xaa6fecb86184dc21, 0x8fb501e34aa387f9} + + // Verify that a^p is the conjugate of a; that is, that a^p + a = 2 * Re(a) + ap.Power(a, p) + ap.Add(ap, a) + apd := gfP2Decode(ap) + require.Equal(t, apd, &gfP2{gfP{0}, gfP{6}}) + + // Two arbitrary exponents + r := [4]uint64{0x123456789abcdef0, 0x2468ace013579bdf, 0x89abcdef01234567, 0x13579bdf2468ace0} + s := [4]uint64{0x1122334455667788, 0x99aabbccddeeff00, 0x159d26ae37bf48c0, 0x0c84fb73ea62d951} + + // Their sum r+s + rps := [4]uint64{0x235689bcf0235678, 0xbe1368acf1469adf, 0x9f48f49d38e28e27, 0x1fdc97530ecb8631} + + // Verify that (a^r)*(a^s) = a^(r+s) + ar := &gfP2{} + as := &gfP2{} + arps := &gfP2{} + aras := &gfP2{} + ar.Power(a, r) + as.Power(a, s) + arps.Power(a, rps) + aras.Mul(ar, as) + require.Equal(t, aras, arps) + + // Verify that (a^r)^s = (a^s)^r + ars := &gfP2{} + asr := &gfP2{} + ars.Power(ar, s) + asr.Power(as, r) + require.Equal(t, ars, asr) +} + +func testsqrt(t *testing.T, a *gfP2) { + // A quadratic nonresidue + nonresidue := &gfP2{*newGFp(1), *newGFp(2)} + s := &gfP2{} + + r := s.Sqrt(a) + if r == nil { + // Not a quadratic residue, but then nonresidue*a will be one + a.Mul(a, nonresidue) + r = s.Sqrt(a) + } + + if r == nil { + t.Error("Neither a nor nonresidue*a is a quadratic residue") + } else { + s2 := &gfP2{} + s2.Mul(s, s) + if !s2.Equals(a) { + t.Error("Sqrt mismatch") + } + } +} + +func TestGfP2Sqrt(t *testing.T) { + // Simple cases + testsqrt(t, &gfP2{*newGFp(0), *newGFp(0)}) + testsqrt(t, &gfP2{*newGFp(0), *newGFp(1)}) + testsqrt(t, &gfP2{*newGFp(1), *newGFp(0)}) + testsqrt(t, &gfP2{*newGFp(0), *newGFp(-1)}) + + // Two tests of the alpha = -1 branch + testsqrt(t, &gfP2{*newGFp(0), *newGFp(-1)}) + testsqrt(t, &gfP2{*newGFp(0), *newGFp(13)}) + + // Some other arbitrary tests, including a nonresidue + testsqrt(t, &gfP2{*newGFp(2), *newGFp(-41)}) + testsqrt(t, &gfP2{*newGFp(2), *newGFp(-10)}) +} diff --git a/pairing/bn256/point.go b/pairing/bn256/point.go index 28316b6a2..998728b0d 100644 --- a/pairing/bn256/point.go +++ b/pairing/bn256/point.go @@ -435,6 +435,63 @@ func (p *pointG2) String() string { return "bn256.G2:" + p.g.String() } +func (p *pointG2) Hash(m []byte) kyber.Point { + // Hash the data to get the "real" and "imaginary" parts of the + // initial attempt at an x coordinate. The real component + // becomes SHA256("r" || m) and the imaginary component becomes + // SHA256("i" || m). + x := &gfP2{*newGFp(0), *newGFp(0)} + realh := sha256.New() + realh.Write([]byte("r")) + realh.Write(m) + realhash := realh.Sum(nil) + imagh := sha256.New() + imagh.Write([]byte("i")) + imagh.Write(m) + imaghash := imagh.Sum(nil) + x.x.Unmarshal(imaghash[:]) + x.y.Unmarshal(realhash[:]) + montEncode(&x.x, &x.x) + montEncode(&x.y, &x.y) + + // See if there's a corresponding y coordinate for this x. If not, + // increment (the "real" part of) x and keep trying. + y := &gfP2{} + one := &gfP2{*newGFp(0), *newGFp(1)} + + for { + // Compute y2 = x^3 + 3/xi + y2 := &gfP2{} + y2.Mul(x, x) + y2.Mul(y2, x) + y2.Add(y2, twistB) + + // Take the square root, if possible + if y.Sqrt(y2) != nil { + break + } + + // Add 1 to the "real" part of x + x.Add(x, one) + } + + // Now we have a point on the twist curve + rawhashpoint := &twistPoint{*x, *y, + gfP2{*newGFp(0), *newGFp(1)}, + gfP2{*newGFp(0), *newGFp(1)}} + + // Multiply by the cofactor to get a point in G2 + rawhashpoint.Mul(rawhashpoint, g2Cofactor) + + // Create a pointG2 out of the twistPoint + if p.g == nil { + p.g = new(twistPoint) + } + p.g.Set(rawhashpoint) + + return p +} + type pointGT struct { g *gfP12 } diff --git a/pairing/bn256/point_test.go b/pairing/bn256/point_test.go index 93450852c..14a81c1ae 100644 --- a/pairing/bn256/point_test.go +++ b/pairing/bn256/point_test.go @@ -39,3 +39,63 @@ func TestPointG1_HashToPoint(t *testing.T) { t.Error("hash does not match reference") } } + +// Regression tests for hash-to-G2 +func TestPointG2HashToPointRegression(t *testing.T) { + // regression test 1 + p := new(pointG2).Hash([]byte("abc")) + pBuf, err := p.MarshalBinary() + if err != nil { + t.Error(err) + } + refBuf, err := hex.DecodeString("80dfe6c1dc83487bb7b72886e69934775b552f5db41fab6de00dcc9c3a59a14e836b8267e7a13e5afa904f9c011ad27de45af14c44b4dfeedf7c8e7d290dacd95f04d5463c622ce60b0c8ab9ae96d1f9ffb8d69d0207d6e3605372eb15f5a5530c0d64e7e8b6fedce3bd2993230bab11a43aec8bbb0153d461c8f9168e244c76") + if err != nil { + t.Error(err) + } + if !bytes.Equal(pBuf, refBuf) { + t.Error("hash does not match expected value") + } + + // regression test 2 + buf2, err := hex.DecodeString("e0a05cbb37fd6c159732a8c57b981773f7480695328b674d8a9cc083377f1811") + if err != nil { + t.Error(err) + } + p2 := new(pointG2).Hash(buf2) + p2Buf, err := p2.MarshalBinary() + if err != nil { + t.Error(err) + } + refBuf2, err := hex.DecodeString("0dfe629e88ab4afbaa36a415bad296f7329c39160b1232df1f0a2be393e19a4d0275b0223514724acf8f7f833202444da83a91e58db73eb37bd4def713e4bdf202a31171ba8753908126809fbb3ad266e959b4061755f405d12d90fbdbec8b10431ed85153b245f7788745255206c032caf8fbdd6432154a0d77dd24bc4d5937") + if err != nil { + t.Error(err) + } + if !bytes.Equal(p2Buf, refBuf2) { + t.Error("hash does not match expected value") + } +} + +func testhashg2(t *testing.T, b []byte) { + p := new(pointG2) + p.Hash(b) + if !p.g.IsOnCurve() { + t.Error("hash to G2 yielded point not on curve") + } + // the order of the group, minus 1 + orderm1 := bigFromBase10("65000549695646603732796438742359905742570406053903786389881062969044166799968") + G2 := new(groupG2) + orderm1scalar := G2.Scalar().SetBytes(orderm1.Bytes()) + pmul := newPointG2() + pmul.Mul(orderm1scalar, p) + // Now add p one more time, and we should get O + pmul.Add(pmul, p) + if !pmul.g.z.IsZero() { + t.Error("hash to G2 yielded point of wrong order") + } +} + +func TestPointG2HashtoPoint(t *testing.T) { + testhashg2(t, []byte("")) + testhashg2(t, []byte("abc")) + testhashg2(t, []byte("test hash string")) +} diff --git a/pairing/bn256/suite_test.go b/pairing/bn256/suite_test.go index 66fa5bb60..f00070f7a 100644 --- a/pairing/bn256/suite_test.go +++ b/pairing/bn256/suite_test.go @@ -239,6 +239,36 @@ func TestBilinearity(t *testing.T) { require.Equal(t, pc, pd) } +type hashablePoint interface { + Hash([]byte) kyber.Point +} + +func TestHashBilinearity(t *testing.T) { + suite := NewSuite() + + // Compute hash to G1 + A := suite.G1().Point().(hashablePoint).Hash([]byte("1234")) + + // Compute hash to G2 + B := suite.G2().Point().(hashablePoint).Hash([]byte("1234")) + + // Choose a random scalar s + s := suite.G1().Scalar().Pick(random.New()) + + // Compute As = A^s + As := suite.G1().Point().Mul(s, A) + + // Compute Bs = B^s + Bs := suite.G2().Point().Mul(s, B) + + // Compute e(As, B) and e(A, Bs) + res1 := suite.Pair(As, B) + res2 := suite.Pair(A, Bs) + + // Check they are equal + require.Equal(t, res1, res2) +} + func TestTripartiteDiffieHellman(t *testing.T) { suite := NewSuite() a := suite.G1().Scalar().Pick(random.New())