Skip to content

Commit

Permalink
crypto/bn256: unmarshal constraint + start pure go impl
Browse files Browse the repository at this point in the history
  • Loading branch information
karalabe committed Mar 1, 2018
1 parent cfe82ab commit 231a8e2
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 39 deletions.
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
86 changes: 61 additions & 25 deletions crypto/bn256/bn256.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -283,7 +295,6 @@ func (e *G2) Unmarshal(m []byte) ([]byte, error) {
return nil, errors.New("bn256: malformed point")
}
}

return m[4*numBytes:], nil
}

Expand Down Expand Up @@ -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)
Expand Down
30 changes: 16 additions & 14 deletions crypto/bn256/gfp.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package bn256

import "fmt"
import (
"errors"
"fmt"
)

type gfP [4]uint64

Expand Down Expand Up @@ -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)
15 changes: 15 additions & 0 deletions crypto/bn256/gfp_amd64.go
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 2 additions & 0 deletions crypto/bn256/gfp.s → crypto/bn256/gfp_amd64.s
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build amd64,!appengine,!gccgo

#include "gfp.h"
#include "mul.h"
#include "mul_bmi2.h"
Expand Down
98 changes: 98 additions & 0 deletions crypto/bn256/gfp_pure.go
Original file line number Diff line number Diff line change
@@ -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]
}
60 changes: 60 additions & 0 deletions crypto/bn256/gfp_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit 231a8e2

Please sign in to comment.