From 231a8e2258bd7e3bbf7d7bbdb17124e8201d3a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 1 Mar 2018 12:31:01 +0200 Subject: [PATCH] crypto/bn256: unmarshal constraint + start pure go impl --- .travis.yml | 13 ++++ crypto/bn256/bn256.go | 86 +++++++++++++++++-------- crypto/bn256/gfp.go | 30 ++++----- crypto/bn256/gfp_amd64.go | 15 +++++ crypto/bn256/{gfp.s => gfp_amd64.s} | 2 + crypto/bn256/gfp_pure.go | 98 +++++++++++++++++++++++++++++ crypto/bn256/gfp_test.go | 60 ++++++++++++++++++ 7 files changed, 265 insertions(+), 39 deletions(-) create mode 100644 crypto/bn256/gfp_amd64.go rename crypto/bn256/{gfp.s => gfp_amd64.s} (97%) create mode 100644 crypto/bn256/gfp_pure.go create mode 100644 crypto/bn256/gfp_test.go diff --git a/.travis.yml b/.travis.yml index a76a78954d53..dadb62aad45b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,19 @@ matrix: - go run build/ci.go install - go run build/ci.go test -coverage + - os: linux + dist: trusty + sudo: required + go: 1.9.x + env: + - 386 + script: + - sudo modprobe fuse + - sudo chmod 666 /dev/fuse + - sudo chown root:$USER /etc/fuse.conf + - GOARCH=386 go run build/ci.go install + - GOARCH=386 go run build/ci.go test -coverage + - os: osx go: 1.9.x script: diff --git a/crypto/bn256/bn256.go b/crypto/bn256/bn256.go index 01590008e2fb..c6ea2d07e01c 100644 --- a/crypto/bn256/bn256.go +++ b/crypto/bn256/bn256.go @@ -120,19 +120,23 @@ func (e *G1) Marshal() []byte { func (e *G1) Unmarshal(m []byte) ([]byte, error) { // Each value is a 256-bit number. const numBytes = 256 / 8 - if len(m) < 2*numBytes { return nil, errors.New("bn256: not enough data") } - + // Unmarshal the points and check their caps if e.p == nil { e.p = &curvePoint{} } else { e.p.x, e.p.y = gfP{0}, gfP{0} } - - e.p.x.Unmarshal(m) - e.p.y.Unmarshal(m[numBytes:]) + var err error + if err = e.p.x.Unmarshal(m); err != nil { + return nil, err + } + if err = e.p.y.Unmarshal(m[numBytes:]); err != nil { + return nil, err + } + // Encode into Montgomery form and ensure it's on the curve montEncode(&e.p.x, &e.p.x) montEncode(&e.p.y, &e.p.y) @@ -252,19 +256,27 @@ func (e *G2) Marshal() []byte { func (e *G2) Unmarshal(m []byte) ([]byte, error) { // Each value is a 256-bit number. const numBytes = 256 / 8 - if len(m) < 4*numBytes { return nil, errors.New("bn256: not enough data") } - + // Unmarshal the points and check their caps if e.p == nil { e.p = &twistPoint{} } - - e.p.x.x.Unmarshal(m) - e.p.x.y.Unmarshal(m[numBytes:]) - e.p.y.x.Unmarshal(m[2*numBytes:]) - e.p.y.y.Unmarshal(m[3*numBytes:]) + var err error + if err = e.p.x.x.Unmarshal(m); err != nil { + return nil, err + } + if err = e.p.x.y.Unmarshal(m[numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.x.Unmarshal(m[2*numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.y.Unmarshal(m[3*numBytes:]); err != nil { + return nil, err + } + // Encode into Montgomery form and ensure it's on the curve montEncode(&e.p.x.x, &e.p.x.x) montEncode(&e.p.x.y, &e.p.x.y) montEncode(&e.p.y.x, &e.p.y.x) @@ -283,7 +295,6 @@ func (e *G2) Unmarshal(m []byte) ([]byte, error) { return nil, errors.New("bn256: malformed point") } } - return m[4*numBytes:], nil } @@ -416,18 +427,43 @@ func (e *GT) Unmarshal(m []byte) ([]byte, error) { e.p = &gfP12{} } - e.p.x.x.x.Unmarshal(m) - e.p.x.x.y.Unmarshal(m[numBytes:]) - e.p.x.y.x.Unmarshal(m[2*numBytes:]) - e.p.x.y.y.Unmarshal(m[3*numBytes:]) - e.p.x.z.x.Unmarshal(m[4*numBytes:]) - e.p.x.z.y.Unmarshal(m[5*numBytes:]) - e.p.y.x.x.Unmarshal(m[6*numBytes:]) - e.p.y.x.y.Unmarshal(m[7*numBytes:]) - e.p.y.y.x.Unmarshal(m[8*numBytes:]) - e.p.y.y.y.Unmarshal(m[9*numBytes:]) - e.p.y.z.x.Unmarshal(m[10*numBytes:]) - e.p.y.z.y.Unmarshal(m[11*numBytes:]) + var err error + if err = e.p.x.x.x.Unmarshal(m); err != nil { + return nil, err + } + if err = e.p.x.x.y.Unmarshal(m[numBytes:]); err != nil { + return nil, err + } + if err = e.p.x.y.x.Unmarshal(m[2*numBytes:]); err != nil { + return nil, err + } + if err = e.p.x.y.y.Unmarshal(m[3*numBytes:]); err != nil { + return nil, err + } + if err = e.p.x.z.x.Unmarshal(m[4*numBytes:]); err != nil { + return nil, err + } + if err = e.p.x.z.y.Unmarshal(m[5*numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.x.x.Unmarshal(m[6*numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.x.y.Unmarshal(m[7*numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.y.x.Unmarshal(m[8*numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.y.y.Unmarshal(m[9*numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.z.x.Unmarshal(m[10*numBytes:]); err != nil { + return nil, err + } + if err = e.p.y.z.y.Unmarshal(m[11*numBytes:]); err != nil { + return nil, err + } montEncode(&e.p.x.x.x, &e.p.x.x.x) montEncode(&e.p.x.x.y, &e.p.x.x.y) montEncode(&e.p.x.y.x, &e.p.x.y.x) diff --git a/crypto/bn256/gfp.go b/crypto/bn256/gfp.go index a033a24529ef..e8e84e7b3b58 100644 --- a/crypto/bn256/gfp.go +++ b/crypto/bn256/gfp.go @@ -1,6 +1,9 @@ package bn256 -import "fmt" +import ( + "errors" + "fmt" +) type gfP [4]uint64 @@ -55,25 +58,24 @@ func (e *gfP) Marshal(out []byte) { } } -func (e *gfP) Unmarshal(in []byte) { +func (e *gfP) Unmarshal(in []byte) error { + // Unmarshal the bytes into little endian form for w := uint(0); w < 4; w++ { for b := uint(0); b < 8; b++ { e[3-w] += uint64(in[8*w+b]) << (56 - 8*b) } } + // Ensure the point respects the curve modulus + for i := 3; i >= 0; i-- { + if e[i] < p2[i] { + return nil + } + if e[i] > p2[i] { + return errors.New("bn256: coordinate exceeds modulus") + } + } + return errors.New("bn256: coordinate equals modulus") } func montEncode(c, a *gfP) { gfpMul(c, a, r2) } func montDecode(c, a *gfP) { gfpMul(c, a, &gfP{1}) } - -// go:noescape -func gfpNeg(c, a *gfP) - -//go:noescape -func gfpAdd(c, a, b *gfP) - -//go:noescape -func gfpSub(c, a, b *gfP) - -//go:noescape -func gfpMul(c, a, b *gfP) diff --git a/crypto/bn256/gfp_amd64.go b/crypto/bn256/gfp_amd64.go new file mode 100644 index 000000000000..ac4f1a9c62f5 --- /dev/null +++ b/crypto/bn256/gfp_amd64.go @@ -0,0 +1,15 @@ +// +build amd64,!appengine,!gccgo + +package bn256 + +// go:noescape +func gfpNeg(c, a *gfP) + +//go:noescape +func gfpAdd(c, a, b *gfP) + +//go:noescape +func gfpSub(c, a, b *gfP) + +//go:noescape +func gfpMul(c, a, b *gfP) diff --git a/crypto/bn256/gfp.s b/crypto/bn256/gfp_amd64.s similarity index 97% rename from crypto/bn256/gfp.s rename to crypto/bn256/gfp_amd64.s index ebd1a34dd820..2d0176f2ec16 100644 --- a/crypto/bn256/gfp.s +++ b/crypto/bn256/gfp_amd64.s @@ -1,3 +1,5 @@ +// +build amd64,!appengine,!gccgo + #include "gfp.h" #include "mul.h" #include "mul_bmi2.h" diff --git a/crypto/bn256/gfp_pure.go b/crypto/bn256/gfp_pure.go new file mode 100644 index 000000000000..a02f63362546 --- /dev/null +++ b/crypto/bn256/gfp_pure.go @@ -0,0 +1,98 @@ +// +build !amd64 appengine gccgo + +package bn256 + +func gfpNeg(c, a *gfP) { + // c = p - a + c[0] = p2[0] - a[0] + c[1] = p2[1] - a[1] + if p2[0] < a[0] { + c[1]-- + } + c[2] = p2[2] - a[2] + if p2[1] < a[1] { + c[2]-- + } + c[3] = p2[3] - a[3] + if p2[2] < a[2] { + c[3]-- + } + // c < p ? c : c-p + gfpCarry(c, p2[3] < a[3]) +} + +func gfpAdd(c, a, b *gfP) { + // c = a + b + c[0] = a[0] + b[0] + c[1] = a[1] + b[1] + if c[0] < a[0] { + c[1]++ + } + c[2] = a[2] + b[2] + if c[1] < a[1] { + c[2]++ + } + c[3] = a[3] + b[3] + if c[2] < a[2] { + c[3]++ + } + // c < p ? c : c-p + gfpCarry(c, c[3] < a[3]) +} + +func gfpSub(c, a, b *gfP) { + // c = a - b + c[0] = a[0] - b[0] + c[1] = a[1] - b[1] + if c[0] > a[0] { + c[1]-- + } + c[2] = a[2] - b[2] + if c[1] > a[1] { + c[2]-- + } + c[3] = a[3] - b[3] + if c[2] > a[2] { + c[3]-- + } + // c < p ? c : c-p + gfpCarry(c, c[3] < a[3]) +} + +func gfpMul(c, a, b *gfP) { + +} + +func gfpCarry(c *gfP, carry bool) { + if c[3] < p2[3] { + return + } else if c[3] == p2[3] { + if c[2] < p2[3] { + return + } else if c[2] == p2[2] { + if c[1] < p2[1] { + return + } else if c[1] == p2[1] { + if c[0] < p2[0] { + return + } + } + } + } + if c[0] < p2[0] { + c[1]-- + } + c[0] -= p2[0] + + if c[1] < p2[1] { + c[2]-- + } + c[1] -= p2[1] + + if c[2] < p2[2] { + c[3]-- + } + c[2] -= p2[2] + + c[3] -= p2[3] +} diff --git a/crypto/bn256/gfp_test.go b/crypto/bn256/gfp_test.go new file mode 100644 index 000000000000..16ab2a8410c0 --- /dev/null +++ b/crypto/bn256/gfp_test.go @@ -0,0 +1,60 @@ +package bn256 + +import ( + "testing" +) + +// Tests that negation works the same way on both assembly-optimized and pure Go +// implementation. +func TestGFpNeg(t *testing.T) { + n := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed} + w := &gfP{0xfedcba9876543211, 0x0123456789abcdef, 0x2152411021524110, 0x0114251201142512} + h := &gfP{} + + gfpNeg(h, n) + if *h != *w { + t.Errorf("negation mismatch: have %#x, want %#x", *h, *w) + } +} + +// Tests that addition works the same way on both assembly-optimized and pure Go +// implementation. +func TestGFpAdd(t *testing.T) { + a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed} + b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef} + w := &gfP{0xc3df73e9278302b8, 0x687e956e978e3572, 0x254954275c18417f, 0xad354b6afc67f9b4} + h := &gfP{} + + gfpAdd(h, a, b) + if *h != *w { + t.Errorf("addition mismatch: have %#x, want %#x", *h, *w) + } +} + +// Tests that subtraction works the same way on both assembly-optimized and pure Go +// implementation. +func TestGFpSub(t *testing.T) { + a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed} + b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef} + w := &gfP{0x02468acf13579bdf, 0xfdb97530eca86420, 0xdfc1e401dfc1e402, 0x203e1bfe203e1bfd} + h := &gfP{} + + gfpSub(h, a, b) + if *h != *w { + t.Errorf("subtraction mismatch: have %#x, want %#x", *h, *w) + } +} + +// Tests that multiplication works the same way on both assembly-optimized and pure Go +// implementation. +func TestGFpMul(t *testing.T) { + a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed} + b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef} + w := &gfP{0xcbcbd377f7ad22d3, 0x3b89ba5d849379bf, 0x87b61627bd38b6d2, 0xc44052a2a0e654b2} + h := &gfP{} + + gfpMul(h, a, b) + if *h != *w { + t.Errorf("multiplication mismatch: have %#x, want %#x", *h, *w) + } +}