Skip to content
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

G2hash pr #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions pairing/bn256/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}}

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

import (
"crypto/subtle"
"fmt"
)

Expand Down Expand Up @@ -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}

Expand Down
92 changes: 92 additions & 0 deletions pairing/bn256/gfp2.go
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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)
Expand Down Expand Up @@ -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
}
95 changes: 95 additions & 0 deletions pairing/bn256/gfp2_test.go
Original file line number Diff line number Diff line change
@@ -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)})
}
57 changes: 57 additions & 0 deletions pairing/bn256/point.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
60 changes: 60 additions & 0 deletions pairing/bn256/point_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
}
Loading