Compare commits

...

47 Commits

Author SHA1 Message Date
arnaucube
590573a0af Update Poseidon last changes of the reference impl
Update Poseidon to last changes of the reference implementation from
26ddaa91db

Compatible with update at circomlib
(cf853c1cc9)
2021-03-08 14:59:42 +01:00
Eduard S
58e589b6eb Merge pull request #35 from iden3/feature/comp-point-test
Update and add test for PackSignY & UnpackSignY
2020-12-21 17:03:44 +01:00
arnaucube
2318fd7044 Update and add test for PackSignY & UnpackSignY
- Update PackSignY & UnpackSignY interface and description
- Add test for UnpackSignY & PackPoint
2020-12-21 16:58:13 +01:00
Eduard S
a0722b9e8f Merge pull request #34 from iden3/feature/exp-comppoint-signy
Abstract & expose CompressedPointToSignAndY
2020-12-21 16:21:27 +01:00
arnaucube
71dbddb5f1 Abstract & expose CompressedPointToSignAndY 2020-12-21 16:12:49 +01:00
Eduard S
0a5c6acba3 Merge pull request #33 from iden3/feature/pkcomp-scanvalue
Add scanner/valuer interface to babyjub.PublicKeyComp
2020-12-21 10:31:37 +01:00
arnaucube
a366175021 Add scanner/valuer interface to babyjub.PublicKeyComp 2020-12-18 20:44:29 +01:00
Eduard S
a2015adb2f Merge pull request #32 from iden3/feature/upgrade-linters
Upgrade linters
2020-12-18 12:11:45 +01:00
arnaucube
6d75396b4b Upgrade linters 2020-12-16 15:07:19 +01:00
Eduard S
821a601d20 Merge pull request #31 from iden3/feature/update-bbjjeddsa
Update BabyJubJub EdDSA to last circomlib version
2020-12-03 10:52:29 +01:00
arnaucube
5dd19b46dd Update BabyJubJub EdDSA to last circomlib version
- Update BabyJubJub EdDSA signature to last circomlib version (Poseidon
usage)
- Remove panic on hash error inside verification, to avoid panic due
field overflow of BabyJubJub signature verification
2020-12-02 19:57:27 +01:00
arnau
94e92e88fb Merge pull request #30 from iden3/feature/signaturecomp-scanner
Add scanner/valuer interface to babyjub.SignatureComp
2020-10-16 16:24:44 +02:00
Arnau B
5ef832f175 Add scanner/valuer interface to babyjub.SignatureComp 2020-10-16 16:22:18 +02:00
arnau
59d8c7a4ca Merge pull request #29 from iden3/feature/babyjubjub-optimization
- Add `add-2008-bbjlp` for point addition
- Add `goff` to BabyJubJub point addition

```
Benchmarks (On a Intel(R) Core(TM) i7-8705G CPU @ 3.10GHz, with 32 GB of RAM):

- Old (commit: e04ca5764a):
BenchmarkBabyjub/AddConst-8              1000000              1072 ns/op
BenchmarkBabyjub/AddRnd-8                  93417             12943 ns/op
BenchmarkBabyjub/MulRnd-8                    252           4797810 ns/op
BenchmarkBabyjub/Compress-8              7291580               166 ns/op
BenchmarkBabyjub/InCurve-8                611137              1999 ns/op
BenchmarkBabyjub/InSubGroup-8             615792              2021 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8            126           9358542 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8          124           9484005 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                 126           9486484 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8               126           9622807 ns/op

- With new point addition algorithm (commit: aab1a681dd):
BenchmarkBabyjub/AddConst-8              1356836               881 ns/op
BenchmarkBabyjub/AddRnd-8                 274112              4220 ns/op
BenchmarkBabyjub/MulRnd-8                    492           2474412 ns/op
BenchmarkBabyjub/Compress-8              6964855               197 ns/op
BenchmarkBabyjub/InCurve-8                608169              2008 ns/op
BenchmarkBabyjub/InSubGroup-8             618772              1954 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8            238           4962397 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8          235           5234883 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                 240           5028720 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8               243           5226654 ns/op

Point Addition: ~3x
Point scalar Mul: ~1.9x
Signature (poseidon): ~1.88x
Verification (poseidon): ~1.84x

- With new point addition algorithm & goff (current commit):
BenchmarkBabyjub/AddConst-8              3000531               400 ns/op
BenchmarkBabyjub/AddRnd-8                2770335               428 ns/op
BenchmarkBabyjub/MulRnd-8                   6636            175522 ns/op
BenchmarkBabyjub/Compress-8              7358768               180 ns/op
BenchmarkBabyjub/InCurve-8                539193              1950 ns/op
BenchmarkBabyjub/InSubGroup-8             601402              1958 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8           2940            409487 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8         2908            414407 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                2395            493165 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8              2491            494849 ns/op

Point Addition: ~9.86x
Point scalar Mul: ~14x
Signature (poseidon): ~10.2x
Verification (poseidon): ~10.56x

---

Total improvement (from old to current):
Point Addition: ~30.24x
Point scalar Mul: ~27.33x
Signature (poseidon): ~19.24x
Verification (poseidon): ~19.44x
```
2020-09-13 20:05:04 +02:00
arnaucube
91767c7b61 Add goff to BabyJubJub point addition
```
Benchmarks (On a Intel(R) Core(TM) i7-8705G CPU @ 3.10GHz, with 32 GB of RAM):

- Old (commit: e04ca5764a):
BenchmarkBabyjub/AddConst-8              1000000              1072 ns/op
BenchmarkBabyjub/AddRnd-8                  93417             12943 ns/op
BenchmarkBabyjub/MulRnd-8                    252           4797810 ns/op
BenchmarkBabyjub/Compress-8              7291580               166 ns/op
BenchmarkBabyjub/InCurve-8                611137              1999 ns/op
BenchmarkBabyjub/InSubGroup-8             615792              2021 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8            126           9358542 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8          124           9484005 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                 126           9486484 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8               126           9622807 ns/op

- With new point addition algorithm (commit: aab1a681dd):
BenchmarkBabyjub/AddConst-8              1356836               881 ns/op
BenchmarkBabyjub/AddRnd-8                 274112              4220 ns/op
BenchmarkBabyjub/MulRnd-8                    492           2474412 ns/op
BenchmarkBabyjub/Compress-8              6964855               197 ns/op
BenchmarkBabyjub/InCurve-8                608169              2008 ns/op
BenchmarkBabyjub/InSubGroup-8             618772              1954 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8            238           4962397 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8          235           5234883 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                 240           5028720 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8               243           5226654 ns/op

Point Addition: ~3x
Point scalar Mul: ~1.9x
Signature (poseidon): ~1.88x
Verification (poseidon): ~1.84x

- With new point addition algorithm & goff (current commit):
BenchmarkBabyjub/AddConst-8              3000531               400 ns/op
BenchmarkBabyjub/AddRnd-8                2770335               428 ns/op
BenchmarkBabyjub/MulRnd-8                   6636            175522 ns/op
BenchmarkBabyjub/Compress-8              7358768               180 ns/op
BenchmarkBabyjub/InCurve-8                539193              1950 ns/op
BenchmarkBabyjub/InSubGroup-8             601402              1958 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8           2940            409487 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8         2908            414407 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                2395            493165 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8              2491            494849 ns/op

Point Addition: ~9.86x
Point scalar Mul: ~14x
Signature (poseidon): ~10.2x
Verification (poseidon): ~10.56x

---

Total improvement (from old to current):
Point Addition: ~30.24x
Point scalar Mul: ~27.33x
Signature (poseidon): ~19.24x
Verification (poseidon): ~19.44x
```
2020-09-05 17:34:06 +02:00
arnaucube
aab1a681dd Add add-2008-bbjlp for point addition
Add `add-2008-bbjlp` for point addition

Benchmarks (On a Intel(R) Core(TM) i7-8705G CPU @ 3.10GHz, with 32 GB of RAM):

```
- Old:
BenchmarkBabyjub/AddConst-8              1000000              1072 ns/op
BenchmarkBabyjub/AddRnd-8                  93417             12943 ns/op
BenchmarkBabyjub/MulRnd-8                    252           4797810 ns/op
BenchmarkBabyjub/Compress-8              7291580               166 ns/op
BenchmarkBabyjub/InCurve-8                611137              1999 ns/op
BenchmarkBabyjub/InSubGroup-8             615792              2021 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8            126           9358542 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8          124           9484005 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                 126           9486484 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8               126           9622807 ns/op

- With new point addition algorithm:
BenchmarkBabyjub/AddConst-8              1356836               881 ns/op
BenchmarkBabyjub/AddRnd-8                 274112              4220 ns/op
BenchmarkBabyjub/MulRnd-8                    492           2474412 ns/op
BenchmarkBabyjub/Compress-8              6964855               197 ns/op
BenchmarkBabyjub/InCurve-8                608169              2008 ns/op
BenchmarkBabyjub/InSubGroup-8             618772              1954 ns/op
BenchmarkBabyjubEddsa/SignMimc7-8            238           4962397 ns/op
BenchmarkBabyjubEddsa/VerifyMimc7-8          235           5234883 ns/op
BenchmarkBabyjubEddsa/SignPoseidon-8                 240           5028720 ns/op
BenchmarkBabyjubEddsa/VerifyPoseidon-8               243           5226654 ns/op
```

Point Addition: ~3x
Point scalar Mul: ~1.9x
Signature (poseidon): ~1.88x
Verification (poseidon): ~1.84x
2020-09-05 17:18:43 +02:00
arnaucube
e04ca5764a Update Poseidon to new circomlib version & https://extgit.iaik.tugraz.at/krypto/hadeshash 2020-08-23 19:40:58 +02:00
Eduard S
70841d78e7 Merge pull request #28 from iden3/feature/signature-sql-interface
Fix value sql interface
2020-08-14 13:03:25 +02:00
a_bennassar
674e8a6739 Fix value sql interface 2020-08-14 12:32:15 +02:00
arnau
66519124ca Merge pull request #27 from iden3/feature/signature-sql-interface
Add scanner/valuer interface to signature
2020-08-13 16:57:45 +02:00
a_bennassar
a86308cb0b Add scanner/valuer interface to PublicKey 2020-08-13 12:43:48 +02:00
a_bennassar
d91a4261f1 Add scanner/valuer interface to signature 2020-08-12 15:52:10 +02:00
Eduard S
327a8175d6 Merge pull request #26 from iden3/feature/pointfromsigny
Babyjubjub separate PointFromSignAndY from p.Decompress
2020-08-06 13:50:47 +02:00
arnaucube
833f68a614 Babyjubjub separate PointFromSignAndY from p.Decompress 2020-08-06 13:34:36 +02:00
Eduard S
29a66457f0 Merge pull request #25 from iden3/feature/poseidon-update
Update Poseidon Hash function names, rm HashBytes
2020-07-23 10:24:57 +02:00
arnaucube
f22be3cdee Update Poseidon Hash function names, rm HashBytes
Since Poseidon Hash is used because of compatibility in zkSNARK circuits, due
circuit constraints number, the hash method of [T]*big.Int is the one directly
compatible with the circuits, is the method which have the `Hash` name on it.
The method that can take arbitrary length of []*big.Int putting them in chunks
of [T]*big.Int and iterating, is called `HashSlice`. The `HashBytes` has been
removed, as is a method that will not be used in zkSNARK circuits due high
constraints number.

For zkSNARK circuits, should be used `poseidon.Hash([poseidon.T]*big.Int)`.
2020-07-23 07:59:59 +02:00
Eduard S
2c471ab545 Merge pull request #24 from iden3/fix/hashbytes-err
Poseidon & MiMC7 HashBytes remove return of err
2020-05-25 12:05:45 +02:00
arnaucube
e134988b1b Rm .travis.yml 2020-05-22 13:33:01 +02:00
arnaucube
3a9171000b Poseidon & MiMC7 HashBytes remove return of err 2020-05-22 00:42:14 +02:00
Eduard S
b1468fc076 Merge pull request #23 from iden3/feature/expose-method
Expose SkToBigInt for usage from other packages & repos
2020-04-28 18:31:15 +02:00
arnaucube
d189a6bedc Expose SkToBigInt for usage from other packages & repos 2020-04-22 14:53:31 +02:00
Eduard S
14c3144613 Merge pull request #22 from iden3/feature/utils-elembigintconv
Add utils.ElementArrayToBigIntArray
2020-04-21 15:31:34 +02:00
arnaucube
b98a9fe65a Add utils.ElementArrayToBigIntArray 2020-04-20 12:45:35 +02:00
arnau
4d1bbacd6c Merge pull request #21 from iden3/feature/githubactions
Add github actions and remove travis
2020-04-14 21:45:30 +02:00
Eduard S
0ac8b46493 Fix linters errors 2020-04-14 16:53:24 +02:00
Eduard S
14d09916cf Add github actions and remove travis 2020-04-14 16:53:15 +02:00
arnau
eb41fe0757 Merge pull request #18 from iden3/feature/fix32bits
Fix compat with 32 bit arch
2020-03-18 11:55:56 +01:00
Eduard S
e10db811aa Fix compat with 32 bit arch 2020-03-17 17:17:45 +01:00
Eduard S
ee467c6215 Merge pull request #16 from iden3/feature/mimc7-goff
Feature/mimc7 goff
2020-03-06 16:27:36 +01:00
arnaucube
4750e9c83c Remove field package which is no longer used 2020-03-06 16:24:41 +01:00
arnaucube
16a8a18a6d Optimize MiMC7 migrating from *big.Int to goff
Optimize MiMC7 migrating from *big.Int to goff generated finite field
operations.

There is still a lot of room for optimization for MiMC7 in the way that is done internally, but will be done in the future.

Benchmarks:
Tested on a Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz, with 16GB of RAM.

- Before:
```
BenchmarkMIMC7-4   	    1026	   1160298 ns/op
```

- After this commit:
```
BenchmarkMIMC7-4   	   19263	     61651 ns/op
```
2020-03-05 17:35:25 +01:00
arnau
e8be761ec7 Merge pull request #15 from iden3/feature/poseidon-opt-goff
Feature/poseidon opt goff
2020-03-04 18:34:17 +01:00
arnaucube
2a3f0d9ed5 Adapt babyjub/eddsa to new Poseidon methods 2020-03-04 12:57:20 +01:00
Eduard S
5d88f7c4cd Merge pull request #13 from iden3/feature/update-bbjj-sig
Update BabyJubJub signature with Poseidon
2020-03-03 17:57:27 +01:00
arnaucube
b45d8a582b Optimize Poseidon migrating from *big.Int to goff
Optimize Poseidon migrating from *big.Int to goff generated finite field
operations.

Benchmarks:
Tested on a Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz, with 16GB of RAM.

- Before the optimizations:
```
BenchmarkPoseidon-4                  470           2489678 ns/op
BenchmarkPoseidonLarge-4             476           2530568 ns/op
```

- With the optimizations of #12:
```
BenchmarkPoseidon-4                  766           1550013 ns/op
BenchmarkPoseidonLarge-4             782           1547572 ns/op
```

- With the changes of this PR, where uses goff generated code instead of *big.Int:
```
BenchmarkPoseidon-4                 9638            121651 ns/op
BenchmarkPoseidonLarge-4            9781            119921 ns/op
```
2020-03-03 16:31:40 +01:00
arnaucube
83f87bfa46 Resolve #4 2020-03-03 16:31:09 +01:00
arnaucube
17bad75853 Add goff generated finite field arithmetic code for used field 2020-03-03 16:30:00 +01:00
26 changed files with 5583 additions and 663 deletions

16
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Lint
on: [ push, pull_request ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v1
with:
go-version: 1.14.x
- name: Checkout code
uses: actions/checkout@v2
- name: Lint
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0
$(go env GOPATH)/bin/golangci-lint run --timeout=5m -c .golangci.yml

28
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
on: [ push, pull_request ]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [ 1.13.x, 1.14.x ]
goarch: [ "amd64", "386" ]
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
env:
GOARCH: ${{ matrix.goarch }}
- name: Checkout code
uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Test
env:
GOARCH: ${{ matrix.goarch }}
run: go test ./...

17
.golangci.yml Normal file
View File

@@ -0,0 +1,17 @@
issues:
max-same-issues: 0
exclude-use-default: false
linters:
enable:
- whitespace
- gosec
- gci
- misspell
- gomnd
- gofmt
- goimports
- lll
- golint
linters-settings:
lll:
line-length: 100

View File

@@ -1,8 +0,0 @@
dist: xenial
language: go
go:
- "1.12"
env:
- GO111MODULE=on

View File

@@ -1,4 +1,4 @@
# go-iden3-crypto [![Go Report Card](https://goreportcard.com/badge/github.com/iden3/go-iden3-crypto)](https://goreportcard.com/report/github.com/iden3/go-iden3-crypto) [![Build Status](https://travis-ci.org/iden3/go-iden3-crypto.svg?branch=master)](https://travis-ci.org/iden3/go-iden3-crypto) [![GoDoc](https://godoc.org/github.com/iden3/go-iden3-crypto?status.svg)](https://godoc.org/github.com/iden3/go-iden3-crypto)
# go-iden3-crypto [![Go Report Card](https://goreportcard.com/badge/github.com/iden3/go-iden3-crypto)](https://goreportcard.com/report/github.com/iden3/go-iden3-crypto) [![Test Status](https://github.com/iden3/go-iden3-crypto/workflows/Test/badge.svg)](https://github.com/iden3/go-iden3-crypto/actions?query=workflow%3ATest) [![Lint Status](https://github.com/iden3/go-iden3-crypto/workflows/Lint/badge.svg)](https://github.com/iden3/go-iden3-crypto/actions?query=workflow%3ALint) [![GoDoc](https://godoc.org/github.com/iden3/go-iden3-crypto?status.svg)](https://godoc.org/github.com/iden3/go-iden3-crypto)
Go implementation of some cryptographic primitives (that fit inside the SNARK field) used in iden3

View File

@@ -5,15 +5,22 @@ import (
"math/big"
"github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/ff"
"github.com/iden3/go-iden3-crypto/utils"
)
// A is one of the babyjub constants.
var A *big.Int
// Aff is A value in *ff.Element representation
var Aff *ff.Element
// D is one of the babyjub constants.
var D *big.Int
// Dff is D value in *ff.Element representation
var Dff *ff.Element
// Order of the babyjub curve.
var Order *big.Int
@@ -29,6 +36,8 @@ var B8 *Point
func init() {
A = utils.NewIntFromString("168700")
D = utils.NewIntFromString("168696")
Aff = ff.NewElement().SetBigInt(A)
Dff = ff.NewElement().SetBigInt(D)
Order = utils.NewIntFromString(
"21888242871839275222246405745257275088614511777268538073601725287587578984328")
@@ -41,6 +50,73 @@ func init() {
"16950150798460657717958625567821834550301663161624707787222815936182638968203")
}
// PointProjective is the Point representation in projective coordinates
type PointProjective struct {
X *ff.Element
Y *ff.Element
Z *ff.Element
}
// NewPointProjective creates a new Point in projective coordinates.
func NewPointProjective() *PointProjective {
return &PointProjective{X: ff.NewElement().SetZero(),
Y: ff.NewElement().SetOne(), Z: ff.NewElement().SetOne()}
}
// Affine returns the Point from the projective representation
func (p *PointProjective) Affine() *Point {
if p.Z.Equal(ff.NewElement().SetZero()) {
return &Point{
X: big.NewInt(0),
Y: big.NewInt(0),
}
}
zinv := ff.NewElement().Inverse(p.Z)
x := ff.NewElement().Mul(p.X, zinv)
y := ff.NewElement().Mul(p.Y, zinv)
xBig := big.NewInt(0)
x.ToBigIntRegular(xBig)
yBig := big.NewInt(0)
y.ToBigIntRegular(yBig)
return &Point{
X: xBig,
Y: yBig,
}
}
// Add computes the addition of two points in projective coordinates
// representation
func (p *PointProjective) Add(q *PointProjective, o *PointProjective) *PointProjective {
// add-2008-bbjlp
// https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp
a := ff.NewElement().Mul(q.Z, o.Z)
b := ff.NewElement().Square(a)
c := ff.NewElement().Mul(q.X, o.X)
d := ff.NewElement().Mul(q.Y, o.Y)
e := ff.NewElement().Mul(Dff, c)
e.MulAssign(d)
f := ff.NewElement().Sub(b, e)
g := ff.NewElement().Add(b, e)
x1y1 := ff.NewElement().Add(q.X, q.Y)
x2y2 := ff.NewElement().Add(o.X, o.Y)
x3 := ff.NewElement().Mul(x1y1, x2y2)
x3.SubAssign(c)
x3.SubAssign(d)
x3.MulAssign(a)
x3.MulAssign(f)
ac := ff.NewElement().Mul(Aff, c)
y3 := ff.NewElement().Sub(d, ac)
y3.MulAssign(a)
y3.MulAssign(g)
z3 := ff.NewElement().Mul(f, g)
p.X = x3
p.Y = y3
p.Z = z3
return p
}
// Point represents a point of the babyjub curve.
type Point struct {
X *big.Int
@@ -59,63 +135,33 @@ func (p *Point) Set(c *Point) *Point {
return p
}
// Add adds Point a and b into res
func (res *Point) Add(a *Point, b *Point) *Point {
// x = (a.x * b.y + b.x * a.y) * (1 + D * a.x * b.x * a.y * b.y)^-1 mod q
x1a := new(big.Int).Mul(a.X, b.Y)
x1b := new(big.Int).Mul(b.X, a.Y)
x1a.Add(x1a, x1b) // x1a = a.x * b.y + b.x * a.y
x2 := new(big.Int).Set(D)
x2.Mul(x2, a.X)
x2.Mul(x2, b.X)
x2.Mul(x2, a.Y)
x2.Mul(x2, b.Y)
x2.Add(constants.One, x2)
x2.Mod(x2, constants.Q)
x2.ModInverse(x2, constants.Q) // x2 = (1 + D * a.x * b.x * a.y * b.y)^-1
// y = (a.y * b.y - A * a.x * b.x) * (1 - D * a.x * b.x * a.y * b.y)^-1 mod q
y1a := new(big.Int).Mul(a.Y, b.Y)
y1b := new(big.Int).Set(A)
y1b.Mul(y1b, a.X)
y1b.Mul(y1b, b.X)
y1a.Sub(y1a, y1b) // y1a = a.y * b.y - A * a.x * b.x
y2 := new(big.Int).Set(D)
y2.Mul(y2, a.X)
y2.Mul(y2, b.X)
y2.Mul(y2, a.Y)
y2.Mul(y2, b.Y)
y2.Sub(constants.One, y2)
y2.Mod(y2, constants.Q)
y2.ModInverse(y2, constants.Q) // y2 = (1 - D * a.x * b.x * a.y * b.y)^-1
res.X = x1a.Mul(x1a, x2)
res.X = res.X.Mod(res.X, constants.Q)
res.Y = y1a.Mul(y1a, y2)
res.Y = res.Y.Mod(res.Y, constants.Q)
return res
// Projective returns a PointProjective from the Point
func (p *Point) Projective() *PointProjective {
return &PointProjective{
X: ff.NewElement().SetBigInt(p.X),
Y: ff.NewElement().SetBigInt(p.Y),
Z: ff.NewElement().SetOne(),
}
}
// Mul multiplies the Point p by the scalar s and stores the result in res,
// Mul multiplies the Point q by the scalar s and stores the result in p,
// which is also returned.
func (res *Point) Mul(s *big.Int, p *Point) *Point {
res.X = big.NewInt(0)
res.Y = big.NewInt(1)
exp := NewPoint().Set(p)
func (p *Point) Mul(s *big.Int, q *Point) *Point {
resProj := &PointProjective{
X: ff.NewElement().SetZero(),
Y: ff.NewElement().SetOne(),
Z: ff.NewElement().SetOne(),
}
exp := q.Projective()
for i := 0; i < s.BitLen(); i++ {
if s.Bit(i) == 1 {
res.Add(res, exp)
resProj.Add(resProj, exp)
}
exp.Add(exp, exp)
exp = exp.Add(exp, exp)
}
return res
p = resProj.Affine()
return p
}
// InCurve returns true when the Point p is in the babyjub curve.
@@ -154,36 +200,57 @@ func (p *Point) InSubGroup() bool {
// PointCoordSign returns the sign of the curve point coordinate. It returns
// false if the sign is positive and false if the sign is negative.
func PointCoordSign(c *big.Int) bool {
if c.Cmp(new(big.Int).Rsh(constants.Q, 1)) == 1 {
return true
}
return false
return c.Cmp(new(big.Int).Rsh(constants.Q, 1)) == 1
}
func PackPoint(ay *big.Int, sign bool) [32]byte {
leBuf := utils.BigIntLEBytes(ay)
// PackSignY packs the given sign and the coordinate Y of a point into a 32
// byte array. This method does not check that the values belong to a valid
// Point in the curve.
func PackSignY(sign bool, y *big.Int) [32]byte {
leBuf := utils.BigIntLEBytes(y)
if sign {
leBuf[31] = leBuf[31] | 0x80
leBuf[31] = leBuf[31] | 0x80 //nolint:gomnd
}
return leBuf
}
// UnpackSignY returns the sign and coordinate Y from a given compressed point.
// This method does not check that the Point belongs to the BabyJubJub curve,
// thus does not return error in such case. This method is intended to obtain
// the sign and the Y coordinate without checking if the point belongs to the
// curve, if the objective is to uncompress a point, Decompress method should
// be used instead.
func UnpackSignY(leBuf [32]byte) (bool, *big.Int) {
sign := false
y := big.NewInt(0)
if (leBuf[31] & 0x80) != 0x00 { //nolint:gomnd
sign = true
leBuf[31] = leBuf[31] & 0x7F //nolint:gomnd
}
utils.SetBigIntFromLEBytes(y, leBuf[:])
return sign, y
}
// Compress the point into a 32 byte array that contains the y coordinate in
// little endian and the sign of the x coordinate.
func (p *Point) Compress() [32]byte {
sign := PointCoordSign(p.X)
return PackPoint(p.Y, sign)
return PackSignY(sign, p.Y)
}
// Decompress a compressed Point into p, and also returns the decompressed
// Point. Returns error if the compressed Point is invalid.
func (p *Point) Decompress(leBuf [32]byte) (*Point, error) {
sign := false
if (leBuf[31] & 0x80) != 0x00 {
sign = true
leBuf[31] = leBuf[31] & 0x7F
}
utils.SetBigIntFromLEBytes(p.Y, leBuf[:])
var sign bool
sign, p.Y = UnpackSignY(leBuf)
return PointFromSignAndY(sign, p.Y)
}
// PointFromSignAndY returns a Point from a Sign and the Y coordinate
func PointFromSignAndY(sign bool, y *big.Int) (*Point, error) {
var p Point
p.X = big.NewInt(0)
p.Y = y
if p.Y.Cmp(constants.Q) >= 0 {
return nil, fmt.Errorf("p.y >= Q")
}
@@ -212,5 +279,5 @@ func (p *Point) Decompress(leBuf [32]byte) (*Point, error) {
}
p.X.Mod(p.X, constants.Q)
return p, nil
return &p, nil
}

View File

@@ -15,7 +15,7 @@ func TestAdd1(t *testing.T) {
a := &Point{X: big.NewInt(0), Y: big.NewInt(1)}
b := &Point{X: big.NewInt(0), Y: big.NewInt(1)}
c := NewPoint().Add(a, b)
c := NewPoint().Projective().Add(a.Projective(), b.Projective())
// fmt.Printf("%v = 2 * %v", *c, *a)
assert.Equal(t, "0", c.X.String())
assert.Equal(t, "1", c.Y.String())
@@ -34,7 +34,7 @@ func TestAdd2(t *testing.T) {
"2626589144620713026669568689430873010625803728049924121243784502389097019475")
b := &Point{X: bX, Y: bY}
c := NewPoint().Add(a, b)
c := NewPoint().Projective().Add(a.Projective(), b.Projective()).Affine()
// fmt.Printf("%v = 2 * %v", *c, *a)
assert.Equal(t,
"6890855772600357754907169075114257697580319025794532037257385534741338397365",
@@ -42,6 +42,18 @@ func TestAdd2(t *testing.T) {
assert.Equal(t,
"4338620300185947561074059802482547481416142213883829469920100239455078257889",
c.Y.String())
d := NewPointProjective().Add(c.Projective(), c.Projective()).Affine()
assert.Equal(t,
"2f6458832049e917c95867185a96621336df33e13c98e81d1ef4928cdbb77772",
hex.EncodeToString(d.X.Bytes()))
// Projective
aP := a.Projective()
bP := b.Projective()
cP := NewPointProjective().Add(aP, bP)
c2 := cP.Affine()
assert.Equal(t, c, c2)
}
func TestAdd3(t *testing.T) {
@@ -57,7 +69,7 @@ func TestAdd3(t *testing.T) {
"20819045374670962167435360035096875258406992893633759881276124905556507972311")
b := &Point{X: bX, Y: bY}
c := NewPoint().Add(a, b)
c := NewPoint().Projective().Add(a.Projective(), b.Projective()).Affine()
// fmt.Printf("%v = 2 * %v", *c, *a)
assert.Equal(t,
"7916061937171219682591368294088513039687205273691143098332585753343424131937",
@@ -80,7 +92,7 @@ func TestAdd4(t *testing.T) {
"20819045374670962167435360035096875258406992893633759881276124905556507972311")
b := &Point{X: bX, Y: bY}
c := NewPoint().Add(a, b)
c := NewPoint().Projective().Add(a.Projective(), b.Projective()).Affine()
// fmt.Printf("%v = 2 * %v", *c, *a)
assert.Equal(t,
"16540640123574156134436876038791482806971768689494387082833631921987005038935",
@@ -108,8 +120,8 @@ func TestMul0(t *testing.T) {
p := &Point{X: x, Y: y}
s := utils.NewIntFromString("3")
r2 := NewPoint().Add(p, p)
r2 = NewPoint().Add(r2, p)
r2 := NewPoint().Projective().Add(p.Projective(), p.Projective()).Affine()
r2 = NewPoint().Projective().Add(r2.Projective(), p.Projective()).Affine()
r := NewPoint().Mul(s, p)
assert.Equal(t, r2.X.String(), r.X.String())
assert.Equal(t, r2.Y.String(), r.Y.String())
@@ -192,6 +204,40 @@ func TestInSubGroup2(t *testing.T) {
assert.Equal(t, true, p.InSubGroup())
}
func TestPointFromSignAndy(t *testing.T) {
x := utils.NewIntFromString(
"17777552123799933955779906779655732241715742912184938656739573121738514868268")
y := utils.NewIntFromString(
"2626589144620713026669568689430873010625803728049924121243784502389097019475")
p := &Point{X: x, Y: y}
sign := PointCoordSign(p.X)
p2, err := PointFromSignAndY(sign, p.Y)
assert.Equal(t, nil, err)
assert.Equal(t, p.X.String(), p2.X.String())
assert.Equal(t, p.Y.String(), p2.Y.String())
}
func TestPackAndUnpackSignY(t *testing.T) {
x := utils.NewIntFromString(
"17777552123799933955779906779655732241715742912184938656739573121738514868268")
y := utils.NewIntFromString(
"2626589144620713026669568689430873010625803728049924121243784502389097019475")
p := &Point{X: x, Y: y}
pComp := p.Compress()
s, y := UnpackSignY(pComp)
pComp2 := PackSignY(s, y)
assert.Equal(t, pComp, pComp2)
emptyPointComp := [32]byte{}
s, y = UnpackSignY(emptyPointComp)
pComp2 = PackSignY(s, y)
assert.Equal(t, emptyPointComp, pComp2)
}
func TestCompressDecompress1(t *testing.T) {
x := utils.NewIntFromString(
"17777552123799933955779906779655732241715742912184938656739573121738514868268")
@@ -200,7 +246,9 @@ func TestCompressDecompress1(t *testing.T) {
p := &Point{X: x, Y: y}
buf := p.Compress()
assert.Equal(t, "53b81ed5bffe9545b54016234682e7b2f699bd42a5e9eae27ff4051bc698ce85", hex.EncodeToString(buf[:]))
assert.Equal(t,
"53b81ed5bffe9545b54016234682e7b2f699bd42a5e9eae27ff4051bc698ce85",
hex.EncodeToString(buf[:]))
p2, err := NewPoint().Decompress(buf)
assert.Equal(t, nil, err)
@@ -216,7 +264,9 @@ func TestCompressDecompress2(t *testing.T) {
p := &Point{X: x, Y: y}
buf := p.Compress()
assert.Equal(t, "e114eb17eddf794f063a68fecac515e3620e131976108555735c8b0773929709", hex.EncodeToString(buf[:]))
assert.Equal(t,
"e114eb17eddf794f063a68fecac515e3620e131976108555735c8b0773929709",
hex.EncodeToString(buf[:]))
p2, err := NewPoint().Decompress(buf)
assert.Equal(t, nil, err)
@@ -230,14 +280,15 @@ func TestCompressDecompressRnd(t *testing.T) {
buf := p1.Compress()
p2, err := NewPoint().Decompress(buf)
assert.Equal(t, nil, err)
assert.Equal(t, p1, p2)
assert.Equal(t, p1.X.Bytes(), p2.X.Bytes())
assert.Equal(t, p1.Y.Bytes(), p2.Y.Bytes())
}
}
func BenchmarkBabyjub(b *testing.B) {
const n = 256
rnd := rand.New(rand.NewSource(42))
rnd := rand.New(rand.NewSource(42)) //nolint:gosec
var badpoints [n]*Point
for i := 0; i < n; i++ {
@@ -247,6 +298,7 @@ func BenchmarkBabyjub(b *testing.B) {
}
var points [n]*Point
var pointsProj [n]*PointProjective
baseX := utils.NewIntFromString(
"17777552123799933955779906779655732241715742912184938656739573121738514868268")
baseY := utils.NewIntFromString(
@@ -255,6 +307,7 @@ func BenchmarkBabyjub(b *testing.B) {
for i := 0; i < n; i++ {
s := new(big.Int).Rand(rnd, constants.Q)
points[i] = NewPoint().Mul(s, base)
pointsProj[i] = NewPoint().Mul(s, base).Projective()
}
var scalars [n]*big.Int
@@ -265,17 +318,19 @@ func BenchmarkBabyjub(b *testing.B) {
b.Run("AddConst", func(b *testing.B) {
p0 := &Point{X: big.NewInt(0), Y: big.NewInt(1)}
p1 := &Point{X: big.NewInt(0), Y: big.NewInt(1)}
p0Proj := p0.Projective()
p1Proj := p1.Projective()
p2 := NewPoint()
p2 := NewPoint().Projective()
for i := 0; i < b.N; i++ {
p2.Add(p0, p1)
p2.Add(p0Proj, p1Proj)
}
})
b.Run("AddRnd", func(b *testing.B) {
res := NewPoint()
res := NewPoint().Projective()
for i := 0; i < b.N; i++ {
res.Add(points[i%(n/2)], points[i%(n/2)+1])
res.Add(pointsProj[i%(n/2)], pointsProj[i%(n/2)+1])
}
})

View File

@@ -1,13 +1,16 @@
// Package babyjub eddsa implements the EdDSA over the BabyJubJub curve
//nolint:gomnd
package babyjub
import (
"crypto/rand"
"database/sql/driver"
"fmt"
"math/big"
"github.com/iden3/go-iden3-crypto/mimc7"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/iden3/go-iden3-crypto/utils"
"math/big"
)
// pruneBuffer prunes the buffer during key generation according to RFC 8032.
@@ -36,6 +39,13 @@ func NewRandPrivKey() PrivateKey {
// Scalar converts a private key into the scalar value s following the EdDSA
// standard, and using blake-512 hash.
func (k *PrivateKey) Scalar() *PrivKeyScalar {
s := SkToBigInt(k)
return NewPrivKeyScalar(s)
}
// SkToBigInt converts a private key into the *big.Int value following the
// EdDSA standard, and using blake-512 hash
func SkToBigInt(k *PrivateKey) *big.Int {
sBuf := Blake512(k[:])
sBuf32 := [32]byte{}
copy(sBuf32[:], sBuf[:32])
@@ -43,10 +53,10 @@ func (k *PrivateKey) Scalar() *PrivKeyScalar {
s := new(big.Int)
utils.SetBigIntFromLEBytes(s, sBuf32[:])
s.Rsh(s, 3)
return NewPrivKeyScalar(s)
return s
}
// Pub returns the public key corresponding to a private key.
// Public returns the public key corresponding to a private key.
func (k *PrivateKey) Public() *PublicKey {
return k.Scalar().Public()
}
@@ -60,8 +70,8 @@ func NewPrivKeyScalar(s *big.Int) *PrivKeyScalar {
return &sk
}
// Pub returns the public key corresponding to the scalar value s of a private
// key.
// Public returns the public key corresponding to the scalar value s of a
// private key.
func (s *PrivKeyScalar) Public() *PublicKey {
p := NewPoint().Mul((*big.Int)(s), B8)
pk := PublicKey(*p)
@@ -76,16 +86,19 @@ func (s *PrivKeyScalar) BigInt() *big.Int {
// PublicKey represents an EdDSA public key, which is a curve point.
type PublicKey Point
// MarshalText implements the marshaler for PublicKey
func (pk PublicKey) MarshalText() ([]byte, error) {
pkc := pk.Compress()
return utils.Hex(pkc[:]).MarshalText()
}
// String returns the string representation of the PublicKey
func (pk PublicKey) String() string {
pkc := pk.Compress()
return utils.Hex(pkc[:]).String()
}
// UnmarshalText implements the unmarshaler for the PublicKey
func (pk *PublicKey) UnmarshalText(h []byte) error {
var pkc PublicKeyComp
if err := utils.HexDecodeInto(pkc[:], h); err != nil {
@@ -100,24 +113,35 @@ func (pk *PublicKey) UnmarshalText(h []byte) error {
}
// Point returns the Point corresponding to a PublicKey.
func (p *PublicKey) Point() *Point {
return (*Point)(p)
func (pk *PublicKey) Point() *Point {
return (*Point)(pk)
}
// PublicKeyComp represents a compressed EdDSA Public key; it's a compressed curve
// point.
type PublicKeyComp [32]byte
func (buf PublicKeyComp) MarshalText() ([]byte, error) { return utils.Hex(buf[:]).MarshalText() }
func (buf PublicKeyComp) String() string { return utils.Hex(buf[:]).String() }
func (buf *PublicKeyComp) UnmarshalText(h []byte) error { return utils.HexDecodeInto(buf[:], h) }
func (p *PublicKey) Compress() PublicKeyComp {
return PublicKeyComp((*Point)(p).Compress())
// MarshalText implements the marshaler for the PublicKeyComp
func (pkComp PublicKeyComp) MarshalText() ([]byte, error) {
return utils.Hex(pkComp[:]).MarshalText()
}
func (p *PublicKeyComp) Decompress() (*PublicKey, error) {
point, err := NewPoint().Decompress(*p)
// String returns the string representation of the PublicKeyComp
func (pkComp PublicKeyComp) String() string { return utils.Hex(pkComp[:]).String() }
// UnmarshalText implements the unmarshaler for the PublicKeyComp
func (pkComp *PublicKeyComp) UnmarshalText(h []byte) error {
return utils.HexDecodeInto(pkComp[:], h)
}
// Compress returns the PublicKeyCompr for the given PublicKey
func (pk *PublicKey) Compress() PublicKeyComp {
return PublicKeyComp((*Point)(pk).Compress())
}
// Decompress returns the PublicKey for the given PublicKeyComp
func (pkComp *PublicKeyComp) Decompress() (*PublicKey, error) {
point, err := NewPoint().Decompress(*pkComp)
if err != nil {
return nil, err
}
@@ -134,9 +158,18 @@ type Signature struct {
// SignatureComp represents a compressed EdDSA signature.
type SignatureComp [64]byte
func (buf SignatureComp) MarshalText() ([]byte, error) { return utils.Hex(buf[:]).MarshalText() }
func (buf SignatureComp) String() string { return utils.Hex(buf[:]).String() }
func (buf *SignatureComp) UnmarshalText(h []byte) error { return utils.HexDecodeInto(buf[:], h) }
// MarshalText implements the marshaler for the SignatureComp
func (sComp SignatureComp) MarshalText() ([]byte, error) {
return utils.Hex(sComp[:]).MarshalText()
}
// String returns the string representation of the SignatureComp
func (sComp SignatureComp) String() string { return utils.Hex(sComp[:]).String() }
// UnmarshalText implements the unmarshaler for the SignatureComp
func (sComp *SignatureComp) UnmarshalText(h []byte) error {
return utils.HexDecodeInto(sComp[:], h)
}
// Compress an EdDSA signature by concatenating the compression of
// the point R8 and the Little-Endian encoding of S.
@@ -164,8 +197,47 @@ func (s *Signature) Decompress(buf [64]byte) (*Signature, error) {
// Decompress a compressed signature. Returns error if the Point decompression
// fails.
func (s *SignatureComp) Decompress() (*Signature, error) {
return new(Signature).Decompress(*s)
func (sComp *SignatureComp) Decompress() (*Signature, error) {
return new(Signature).Decompress(*sComp)
}
// Scan implements Scanner for database/sql.
func (sComp *SignatureComp) Scan(src interface{}) error {
srcB, ok := src.([]byte)
if !ok {
return fmt.Errorf("can't scan %T into Signature", src)
}
if len(srcB) != 64 {
return fmt.Errorf("can't scan []byte of len %d into Signature, want %d", len(srcB), 64)
}
copy(sComp[:], srcB[:])
return nil
}
// Value implements valuer for database/sql.
func (sComp SignatureComp) Value() (driver.Value, error) {
return sComp[:], nil
}
// Scan implements Scanner for database/sql.
func (s *Signature) Scan(src interface{}) error {
srcB, ok := src.([]byte)
if !ok {
return fmt.Errorf("can't scan %T into Signature", src)
}
if len(srcB) != 64 {
return fmt.Errorf("can't scan []byte of len %d into Signature, want %d", len(srcB), 64)
}
buf := [64]byte{}
copy(buf[:], srcB[:])
_, err := s.Decompress(buf)
return err
}
// Value implements valuer for database/sql.
func (s Signature) Value() (driver.Value, error) {
comp := s.Compress()
return comp[:], nil
}
// SignMimc7 signs a message encoded as a big.Int in Zq using blake-512 hash
@@ -195,18 +267,20 @@ func (k *PrivateKey) SignMimc7(msg *big.Int) *Signature {
// VerifyMimc7 verifies the signature of a message encoded as a big.Int in Zq
// using blake-512 hash for buffer hashing and mimc7 for big.Int hashing.
func (p *PublicKey) VerifyMimc7(msg *big.Int, sig *Signature) bool {
hmInput := []*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg}
func (pk *PublicKey) VerifyMimc7(msg *big.Int, sig *Signature) bool {
hmInput := []*big.Int{sig.R8.X, sig.R8.Y, pk.X, pk.Y, msg}
hm, err := mimc7.Hash(hmInput, nil) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
if err != nil {
panic(err)
return false
}
left := NewPoint().Mul(sig.S, B8) // left = s * 8 * B
r1 := big.NewInt(8)
r1.Mul(r1, hm)
right := NewPoint().Mul(r1, p.Point())
right.Add(sig.R8, right) // right = 8 * R + 8 * hm * A
right := NewPoint().Mul(r1, pk.Point())
rightProj := right.Projective()
rightProj.Add(sig.R8.Projective(), rightProj) // right = 8 * R + 8 * hm * A
right = rightProj.Affine()
return (left.X.Cmp(right.X) == 0) && (left.Y.Cmp(right.Y) == 0)
}
@@ -222,11 +296,13 @@ func (k *PrivateKey) SignPoseidon(msg *big.Int) *Signature {
r.Mod(r, SubOrder)
R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B
A := k.Public().Point()
hmInput := [poseidon.T]*big.Int{R8.X, R8.Y, A.X, A.Y, msg, big.NewInt(int64(0))}
hm, err := poseidon.PoseidonHash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
hmInput := []*big.Int{R8.X, R8.Y, A.X, A.Y, msg}
hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
if err != nil {
panic(err)
}
S := new(big.Int).Lsh(k.Scalar().BigInt(), 3)
S = S.Mul(hm, S)
S.Add(r, S)
@@ -237,17 +313,62 @@ func (k *PrivateKey) SignPoseidon(msg *big.Int) *Signature {
// VerifyPoseidon verifies the signature of a message encoded as a big.Int in Zq
// using blake-512 hash for buffer hashing and Poseidon for big.Int hashing.
func (p *PublicKey) VerifyPoseidon(msg *big.Int, sig *Signature) bool {
hmInput := [poseidon.T]*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg, big.NewInt(int64(0))}
hm, err := poseidon.PoseidonHash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
func (pk *PublicKey) VerifyPoseidon(msg *big.Int, sig *Signature) bool {
hmInput := []*big.Int{sig.R8.X, sig.R8.Y, pk.X, pk.Y, msg}
hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
if err != nil {
panic(err)
return false
}
left := NewPoint().Mul(sig.S, B8) // left = s * 8 * B
r1 := big.NewInt(8)
r1.Mul(r1, hm)
right := NewPoint().Mul(r1, p.Point())
right.Add(sig.R8, right) // right = 8 * R + 8 * hm * A
right := NewPoint().Mul(r1, pk.Point())
rightProj := right.Projective()
rightProj.Add(sig.R8.Projective(), rightProj) // right = 8 * R + 8 * hm * A
right = rightProj.Affine()
return (left.X.Cmp(right.X) == 0) && (left.Y.Cmp(right.Y) == 0)
}
// Scan implements Scanner for database/sql.
func (pk *PublicKey) Scan(src interface{}) error {
srcB, ok := src.([]byte)
if !ok {
return fmt.Errorf("can't scan %T into PublicKey", src)
}
if len(srcB) != 32 {
return fmt.Errorf("can't scan []byte of len %d into PublicKey, want %d", len(srcB), 32)
}
var comp PublicKeyComp
copy(comp[:], srcB)
decomp, err := comp.Decompress()
if err != nil {
return err
}
*pk = *decomp
return nil
}
// Value implements valuer for database/sql.
func (pk PublicKey) Value() (driver.Value, error) {
comp := pk.Compress()
return comp[:], nil
}
// Scan implements Scanner for database/sql.
func (pkComp *PublicKeyComp) Scan(src interface{}) error {
srcB, ok := src.([]byte)
if !ok {
return fmt.Errorf("can't scan %T into PublicKeyComp", src)
}
if len(srcB) != 32 {
return fmt.Errorf("can't scan []byte of len %d into PublicKeyComp, want %d", len(srcB), 32)
}
copy(pkComp[:], srcB)
return nil
}
// Value implements valuer for database/sql.
func (pkComp PublicKeyComp) Value() (driver.Value, error) {
return pkComp[:], nil
}

View File

@@ -1,7 +1,8 @@
package babyjub
import (
"crypto/rand"
"database/sql"
"database/sql/driver"
"encoding/hex"
"fmt"
"math/big"
@@ -10,25 +11,13 @@ import (
"github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func genInputs() (*PrivateKey, *big.Int) {
k := NewRandPrivKey()
fmt.Println("k", hex.EncodeToString(k[:]))
msgBuf := [32]byte{}
rand.Read(msgBuf[:])
msg := utils.SetBigIntFromLEBytes(new(big.Int), msgBuf[:])
msg.Mod(msg, constants.Q)
fmt.Println("msg", msg)
return &k, msg
}
func TestPublicKey(t *testing.T) {
var k PrivateKey
for i := 0; i < 256; i++ {
hex.Decode(k[:], []byte{byte(i)})
for i := 0; i < 32; i++ {
k[i] = byte(i)
}
pk := k.Public()
assert.True(t, pk.X.Cmp(constants.Q) == -1)
@@ -37,7 +26,9 @@ func TestPublicKey(t *testing.T) {
func TestSignVerifyMimc7(t *testing.T) {
var k PrivateKey
hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
_, err := hex.Decode(k[:],
[]byte("0001020304050607080900010203040506070809000102030405060708090001"))
require.Nil(t, err)
msgBuf, err := hex.DecodeString("00010203040506070809")
if err != nil {
panic(err)
@@ -81,7 +72,9 @@ func TestSignVerifyMimc7(t *testing.T) {
func TestSignVerifyPoseidon(t *testing.T) {
var k PrivateKey
hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
_, err := hex.Decode(k[:],
[]byte("0001020304050607080900010203040506070809000102030405060708090001"))
require.Nil(t, err)
msgBuf, err := hex.DecodeString("00010203040506070809")
if err != nil {
panic(err)
@@ -104,7 +97,7 @@ func TestSignVerifyPoseidon(t *testing.T) {
"15383486972088797283337779941324724402501462225528836549661220478783371668959",
sig.R8.Y.String())
assert.Equal(t,
"248298168863866362217836334079793350221620631973732197668910946177382043688",
"1672775540645840396591609181675628451599263765380031905495115170613215233181",
sig.S.String())
ok := pk.VerifyPoseidon(msg, sig)
@@ -116,7 +109,7 @@ func TestSignVerifyPoseidon(t *testing.T) {
assert.Equal(t, ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"28506bce274aa1b3f7e7c2fd7e4fe09bff8f9aa37a42def7994e98f322888c00",
"9d043ece562a8f82bfc0adb640c0107a7d3a27c1c7c1a6179a0da73de5c1b203",
hex.EncodeToString(sigBuf[:]))
ok = pk.VerifyPoseidon(msg, sig2)
@@ -125,7 +118,9 @@ func TestSignVerifyPoseidon(t *testing.T) {
func TestCompressDecompress(t *testing.T) {
var k PrivateKey
hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
_, err := hex.Decode(k[:],
[]byte("0001020304050607080900010203040506070809000102030405060708090001"))
require.Nil(t, err)
pk := k.Public()
for i := 0; i < 64; i++ {
msgBuf, err := hex.DecodeString(fmt.Sprintf("000102030405060708%02d", i))
@@ -142,9 +137,64 @@ func TestCompressDecompress(t *testing.T) {
}
}
func TestSignatureCompScannerValuer(t *testing.T) {
privK := NewRandPrivKey()
var value driver.Valuer //nolint:gosimple this is done to ensure interface compatibility
value = privK.SignPoseidon(big.NewInt(674238462)).Compress()
scan := privK.SignPoseidon(big.NewInt(1)).Compress()
fromDB, err := value.Value()
assert.Nil(t, err)
assert.Nil(t, scan.Scan(fromDB))
assert.Equal(t, value, scan)
}
func TestSignatureScannerValuer(t *testing.T) {
privK := NewRandPrivKey()
var value driver.Valuer
var scan sql.Scanner
value = privK.SignPoseidon(big.NewInt(674238462))
scan = privK.SignPoseidon(big.NewInt(1))
fromDB, err := value.Value()
assert.Nil(t, err)
assert.Nil(t, scan.Scan(fromDB))
assert.Equal(t, value, scan)
}
func TestPublicKeyScannerValuer(t *testing.T) {
privKValue := NewRandPrivKey()
pubKValue := privKValue.Public()
privKScan := NewRandPrivKey()
pubKScan := privKScan.Public()
var value driver.Valuer
var scan sql.Scanner
value = pubKValue
scan = pubKScan
fromDB, err := value.Value()
assert.Nil(t, err)
assert.Nil(t, scan.Scan(fromDB))
assert.Equal(t, value, scan)
}
func TestPublicKeyCompScannerValuer(t *testing.T) {
privKValue := NewRandPrivKey()
pubKCompValue := privKValue.Public().Compress()
privKScan := NewRandPrivKey()
pubKCompScan := privKScan.Public().Compress()
var value driver.Valuer
var scan sql.Scanner
value = &pubKCompValue
scan = &pubKCompScan
fromDB, err := value.Value()
assert.Nil(t, err)
assert.Nil(t, scan.Scan(fromDB))
assert.Equal(t, value, scan)
}
func BenchmarkBabyjubEddsa(b *testing.B) {
var k PrivateKey
hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
_, err := hex.Decode(k[:],
[]byte("0001020304050607080900010203040506070809000102030405060708090001"))
require.Nil(b, err)
pk := k.Public()
const n = 256

View File

@@ -1,13 +1,20 @@
package babyjub
import (
"github.com/dchest/blake512" // I have personally reviewed that this module doesn't do anything suspicious
"github.com/dchest/blake512"
)
// Note on dchest/blake512: This specific blake512 module is compatible with
// the version of Blake512 used at circomlib, and this module has been reviewed
// to don't be doing do anything suspicious.
// Blake512 performs the blake-512 hash over the buffer m. Note that this is
// the original blake from the SHA3 competition and not the new blake2 version.
func Blake512(m []byte) []byte {
h := blake512.New()
h.Write(m[:])
_, err := h.Write(m[:])
if err != nil {
panic(err)
}
return h.Sum(nil)
}

View File

@@ -1,7 +1,7 @@
package constants
import (
"github.com/iden3/go-iden3-crypto/utils"
"fmt"
"math/big"
)
@@ -21,6 +21,11 @@ func init() {
Zero = big.NewInt(0)
One = big.NewInt(1)
MinusOne = big.NewInt(-1)
Q = utils.NewIntFromString(
"21888242871839275222246405745257275088548364400416034343698204186575808495617")
qString := "21888242871839275222246405745257275088548364400416034343698204186575808495617"
var ok bool
Q, ok = new(big.Int).SetString(qString, 10)
if !ok {
panic(fmt.Sprintf("Bad base 10 string %s", qString))
}
}

122
ff/arith.go Normal file
View File

@@ -0,0 +1,122 @@
// Copyright 2020 ConsenSys AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by goff DO NOT EDIT
package ff
import (
"math/bits"
)
func madd(a, b, t, u, v uint64) (uint64, uint64, uint64) {
var carry uint64
hi, lo := bits.Mul64(a, b)
v, carry = bits.Add64(lo, v, 0)
u, carry = bits.Add64(hi, u, carry)
t, _ = bits.Add64(t, 0, carry)
return t, u, v
}
// madd0 hi = a*b + c (discards lo bits)
func madd0(a, b, c uint64) (hi uint64) {
var carry, lo uint64
hi, lo = bits.Mul64(a, b)
_, carry = bits.Add64(lo, c, 0)
hi, _ = bits.Add64(hi, 0, carry)
return
}
// madd1 hi, lo = a*b + c
func madd1(a, b, c uint64) (hi uint64, lo uint64) {
var carry uint64
hi, lo = bits.Mul64(a, b)
lo, carry = bits.Add64(lo, c, 0)
hi, _ = bits.Add64(hi, 0, carry)
return
}
// madd2 hi, lo = a*b + c + d
func madd2(a, b, c, d uint64) (hi uint64, lo uint64) {
var carry uint64
hi, lo = bits.Mul64(a, b)
c, carry = bits.Add64(c, d, 0)
hi, _ = bits.Add64(hi, 0, carry)
lo, carry = bits.Add64(lo, c, 0)
hi, _ = bits.Add64(hi, 0, carry)
return
}
// madd2s superhi, hi, lo = 2*a*b + c + d + e
func madd2s(a, b, c, d, e uint64) (superhi, hi, lo uint64) {
var carry, sum uint64
hi, lo = bits.Mul64(a, b)
lo, carry = bits.Add64(lo, lo, 0)
hi, superhi = bits.Add64(hi, hi, carry)
sum, carry = bits.Add64(c, e, 0)
hi, _ = bits.Add64(hi, 0, carry)
lo, carry = bits.Add64(lo, sum, 0)
hi, _ = bits.Add64(hi, 0, carry)
hi, _ = bits.Add64(hi, 0, d)
return
}
func madd1s(a, b, d, e uint64) (superhi, hi, lo uint64) {
var carry uint64
hi, lo = bits.Mul64(a, b)
lo, carry = bits.Add64(lo, lo, 0)
hi, superhi = bits.Add64(hi, hi, carry)
lo, carry = bits.Add64(lo, e, 0)
hi, _ = bits.Add64(hi, 0, carry)
hi, _ = bits.Add64(hi, 0, d)
return
}
func madd2sb(a, b, c, e uint64) (superhi, hi, lo uint64) {
var carry, sum uint64
hi, lo = bits.Mul64(a, b)
lo, carry = bits.Add64(lo, lo, 0)
hi, superhi = bits.Add64(hi, hi, carry)
sum, carry = bits.Add64(c, e, 0)
hi, _ = bits.Add64(hi, 0, carry)
lo, carry = bits.Add64(lo, sum, 0)
hi, _ = bits.Add64(hi, 0, carry)
return
}
func madd1sb(a, b, e uint64) (superhi, hi, lo uint64) {
var carry uint64
hi, lo = bits.Mul64(a, b)
lo, carry = bits.Add64(lo, lo, 0)
hi, superhi = bits.Add64(hi, hi, carry)
lo, carry = bits.Add64(lo, e, 0)
hi, _ = bits.Add64(hi, 0, carry)
return
}
func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) {
var carry uint64
hi, lo = bits.Mul64(a, b)
c, carry = bits.Add64(c, d, 0)
hi, _ = bits.Add64(hi, 0, carry)
lo, carry = bits.Add64(lo, c, 0)
hi, _ = bits.Add64(hi, e, carry)
return
}

792
ff/element.go Normal file
View File

@@ -0,0 +1,792 @@
// Copyright 2020 ConsenSys AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// field modulus q =
//
// 21888242871839275222246405745257275088548364400416034343698204186575808495617
// Code generated by goff DO NOT EDIT
// goff version: - build:
// Element are assumed to be in Montgomery form in all methods
// Package ff (generated by goff) contains field arithmetics operations
package ff
import (
"crypto/rand"
"encoding/binary"
"io"
"math/big"
"math/bits"
"sync"
"unsafe"
)
// Element represents a field element stored on 4 words (uint64)
// Element are assumed to be in Montgomery form in all methods
type Element [4]uint64
// ElementLimbs number of 64 bits words needed to represent Element
const ElementLimbs = 4
// ElementBits number bits needed to represent Element
const ElementBits = 254
// SetUint64 z = v, sets z LSB to v (non-Montgomery form) and convert z to Montgomery form
func (z *Element) SetUint64(v uint64) *Element {
z[0] = v
z[1] = 0
z[2] = 0
z[3] = 0
return z.ToMont()
}
// Set z = x
func (z *Element) Set(x *Element) *Element {
z[0] = x[0]
z[1] = x[1]
z[2] = x[2]
z[3] = x[3]
return z
}
// SetZero z = 0
func (z *Element) SetZero() *Element {
z[0] = 0
z[1] = 0
z[2] = 0
z[3] = 0
return z
}
// SetOne z = 1 (in Montgomery form)
func (z *Element) SetOne() *Element {
z[0] = 12436184717236109307
z[1] = 3962172157175319849
z[2] = 7381016538464732718
z[3] = 1011752739694698287
return z
}
// Neg z = q - x
func (z *Element) Neg(x *Element) *Element {
if x.IsZero() {
return z.SetZero()
}
var borrow uint64
z[0], borrow = bits.Sub64(4891460686036598785, x[0], 0)
z[1], borrow = bits.Sub64(2896914383306846353, x[1], borrow)
z[2], borrow = bits.Sub64(13281191951274694749, x[2], borrow)
z[3], _ = bits.Sub64(3486998266802970665, x[3], borrow)
return z
}
// Div z = x*y^-1 mod q
func (z *Element) Div(x, y *Element) *Element {
var yInv Element
yInv.Inverse(y)
z.Mul(x, &yInv)
return z
}
// Equal returns z == x
func (z *Element) Equal(x *Element) bool {
return (z[3] == x[3]) && (z[2] == x[2]) && (z[1] == x[1]) && (z[0] == x[0])
}
// IsZero returns z == 0
func (z *Element) IsZero() bool {
return (z[3] | z[2] | z[1] | z[0]) == 0
}
// field modulus stored as big.Int
var _elementModulusBigInt big.Int
var onceelementModulus sync.Once
func elementModulusBigInt() *big.Int {
onceelementModulus.Do(func() {
_elementModulusBigInt.SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
})
return &_elementModulusBigInt
}
// Inverse z = x^-1 mod q
// Algorithm 16 in "Efficient Software-Implementation of Finite Fields with Applications to Cryptography"
// if x == 0, sets and returns z = x
func (z *Element) Inverse(x *Element) *Element {
if x.IsZero() {
return z.Set(x)
}
// initialize u = q
var u = Element{
4891460686036598785,
2896914383306846353,
13281191951274694749,
3486998266802970665,
}
// initialize s = r^2
var s = Element{
1997599621687373223,
6052339484930628067,
10108755138030829701,
150537098327114917,
}
// r = 0
r := Element{}
v := *x
var carry, borrow, t, t2 uint64
var bigger, uIsOne, vIsOne bool
for !uIsOne && !vIsOne {
for v[0]&1 == 0 {
// v = v >> 1
t2 = v[3] << 63
v[3] >>= 1
t = t2
t2 = v[2] << 63
v[2] = (v[2] >> 1) | t
t = t2
t2 = v[1] << 63
v[1] = (v[1] >> 1) | t
t = t2
v[0] = (v[0] >> 1) | t
if s[0]&1 == 1 {
// s = s + q
s[0], carry = bits.Add64(s[0], 4891460686036598785, 0)
s[1], carry = bits.Add64(s[1], 2896914383306846353, carry)
s[2], carry = bits.Add64(s[2], 13281191951274694749, carry)
s[3], _ = bits.Add64(s[3], 3486998266802970665, carry)
}
// s = s >> 1
t2 = s[3] << 63
s[3] >>= 1
t = t2
t2 = s[2] << 63
s[2] = (s[2] >> 1) | t
t = t2
t2 = s[1] << 63
s[1] = (s[1] >> 1) | t
t = t2
s[0] = (s[0] >> 1) | t
}
for u[0]&1 == 0 {
// u = u >> 1
t2 = u[3] << 63
u[3] >>= 1
t = t2
t2 = u[2] << 63
u[2] = (u[2] >> 1) | t
t = t2
t2 = u[1] << 63
u[1] = (u[1] >> 1) | t
t = t2
u[0] = (u[0] >> 1) | t
if r[0]&1 == 1 {
// r = r + q
r[0], carry = bits.Add64(r[0], 4891460686036598785, 0)
r[1], carry = bits.Add64(r[1], 2896914383306846353, carry)
r[2], carry = bits.Add64(r[2], 13281191951274694749, carry)
r[3], _ = bits.Add64(r[3], 3486998266802970665, carry)
}
// r = r >> 1
t2 = r[3] << 63
r[3] >>= 1
t = t2
t2 = r[2] << 63
r[2] = (r[2] >> 1) | t
t = t2
t2 = r[1] << 63
r[1] = (r[1] >> 1) | t
t = t2
r[0] = (r[0] >> 1) | t
}
// v >= u
bigger = !(v[3] < u[3] || (v[3] == u[3] && (v[2] < u[2] || (v[2] == u[2] && (v[1] < u[1] || (v[1] == u[1] && (v[0] < u[0])))))))
if bigger {
// v = v - u
v[0], borrow = bits.Sub64(v[0], u[0], 0)
v[1], borrow = bits.Sub64(v[1], u[1], borrow)
v[2], borrow = bits.Sub64(v[2], u[2], borrow)
v[3], _ = bits.Sub64(v[3], u[3], borrow)
// r >= s
bigger = !(r[3] < s[3] || (r[3] == s[3] && (r[2] < s[2] || (r[2] == s[2] && (r[1] < s[1] || (r[1] == s[1] && (r[0] < s[0])))))))
if bigger {
// s = s + q
s[0], carry = bits.Add64(s[0], 4891460686036598785, 0)
s[1], carry = bits.Add64(s[1], 2896914383306846353, carry)
s[2], carry = bits.Add64(s[2], 13281191951274694749, carry)
s[3], _ = bits.Add64(s[3], 3486998266802970665, carry)
}
// s = s - r
s[0], borrow = bits.Sub64(s[0], r[0], 0)
s[1], borrow = bits.Sub64(s[1], r[1], borrow)
s[2], borrow = bits.Sub64(s[2], r[2], borrow)
s[3], _ = bits.Sub64(s[3], r[3], borrow)
} else {
// u = u - v
u[0], borrow = bits.Sub64(u[0], v[0], 0)
u[1], borrow = bits.Sub64(u[1], v[1], borrow)
u[2], borrow = bits.Sub64(u[2], v[2], borrow)
u[3], _ = bits.Sub64(u[3], v[3], borrow)
// s >= r
bigger = !(s[3] < r[3] || (s[3] == r[3] && (s[2] < r[2] || (s[2] == r[2] && (s[1] < r[1] || (s[1] == r[1] && (s[0] < r[0])))))))
if bigger {
// r = r + q
r[0], carry = bits.Add64(r[0], 4891460686036598785, 0)
r[1], carry = bits.Add64(r[1], 2896914383306846353, carry)
r[2], carry = bits.Add64(r[2], 13281191951274694749, carry)
r[3], _ = bits.Add64(r[3], 3486998266802970665, carry)
}
// r = r - s
r[0], borrow = bits.Sub64(r[0], s[0], 0)
r[1], borrow = bits.Sub64(r[1], s[1], borrow)
r[2], borrow = bits.Sub64(r[2], s[2], borrow)
r[3], _ = bits.Sub64(r[3], s[3], borrow)
}
uIsOne = (u[0] == 1) && (u[3]|u[2]|u[1]) == 0
vIsOne = (v[0] == 1) && (v[3]|v[2]|v[1]) == 0
}
if uIsOne {
z.Set(&r)
} else {
z.Set(&s)
}
return z
}
// SetRandom sets z to a random element < q
func (z *Element) SetRandom() *Element {
bytes := make([]byte, 32)
io.ReadFull(rand.Reader, bytes)
z[0] = binary.BigEndian.Uint64(bytes[0:8])
z[1] = binary.BigEndian.Uint64(bytes[8:16])
z[2] = binary.BigEndian.Uint64(bytes[16:24])
z[3] = binary.BigEndian.Uint64(bytes[24:32])
z[3] %= 3486998266802970665
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}
// Add z = x + y mod q
func (z *Element) Add(x, y *Element) *Element {
var carry uint64
z[0], carry = bits.Add64(x[0], y[0], 0)
z[1], carry = bits.Add64(x[1], y[1], carry)
z[2], carry = bits.Add64(x[2], y[2], carry)
z[3], _ = bits.Add64(x[3], y[3], carry)
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}
// AddAssign z = z + x mod q
func (z *Element) AddAssign(x *Element) *Element {
var carry uint64
z[0], carry = bits.Add64(z[0], x[0], 0)
z[1], carry = bits.Add64(z[1], x[1], carry)
z[2], carry = bits.Add64(z[2], x[2], carry)
z[3], _ = bits.Add64(z[3], x[3], carry)
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}
// Double z = x + x mod q, aka Lsh 1
func (z *Element) Double(x *Element) *Element {
var carry uint64
z[0], carry = bits.Add64(x[0], x[0], 0)
z[1], carry = bits.Add64(x[1], x[1], carry)
z[2], carry = bits.Add64(x[2], x[2], carry)
z[3], _ = bits.Add64(x[3], x[3], carry)
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}
// Sub z = x - y mod q
func (z *Element) Sub(x, y *Element) *Element {
var b uint64
z[0], b = bits.Sub64(x[0], y[0], 0)
z[1], b = bits.Sub64(x[1], y[1], b)
z[2], b = bits.Sub64(x[2], y[2], b)
z[3], b = bits.Sub64(x[3], y[3], b)
if b != 0 {
var c uint64
z[0], c = bits.Add64(z[0], 4891460686036598785, 0)
z[1], c = bits.Add64(z[1], 2896914383306846353, c)
z[2], c = bits.Add64(z[2], 13281191951274694749, c)
z[3], _ = bits.Add64(z[3], 3486998266802970665, c)
}
return z
}
// SubAssign z = z - x mod q
func (z *Element) SubAssign(x *Element) *Element {
var b uint64
z[0], b = bits.Sub64(z[0], x[0], 0)
z[1], b = bits.Sub64(z[1], x[1], b)
z[2], b = bits.Sub64(z[2], x[2], b)
z[3], b = bits.Sub64(z[3], x[3], b)
if b != 0 {
var c uint64
z[0], c = bits.Add64(z[0], 4891460686036598785, 0)
z[1], c = bits.Add64(z[1], 2896914383306846353, c)
z[2], c = bits.Add64(z[2], 13281191951274694749, c)
z[3], _ = bits.Add64(z[3], 3486998266802970665, c)
}
return z
}
// Exp z = x^e mod q
func (z *Element) Exp(x Element, e uint64) *Element {
if e == 0 {
return z.SetOne()
}
z.Set(&x)
l := bits.Len64(e) - 2
for i := l; i >= 0; i-- {
z.Square(z)
if e&(1<<uint(i)) != 0 {
z.MulAssign(&x)
}
}
return z
}
// FromMont converts z in place (i.e. mutates) from Montgomery to regular representation
// sets and returns z = z * 1
func (z *Element) FromMont() *Element {
// the following lines implement z = z * 1
// with a modified CIOS montgomery multiplication
{
// m = z[0]n'[0] mod W
m := z[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, z[0])
C, z[0] = madd2(m, 2896914383306846353, z[1], C)
C, z[1] = madd2(m, 13281191951274694749, z[2], C)
C, z[2] = madd2(m, 3486998266802970665, z[3], C)
z[3] = C
}
{
// m = z[0]n'[0] mod W
m := z[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, z[0])
C, z[0] = madd2(m, 2896914383306846353, z[1], C)
C, z[1] = madd2(m, 13281191951274694749, z[2], C)
C, z[2] = madd2(m, 3486998266802970665, z[3], C)
z[3] = C
}
{
// m = z[0]n'[0] mod W
m := z[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, z[0])
C, z[0] = madd2(m, 2896914383306846353, z[1], C)
C, z[1] = madd2(m, 13281191951274694749, z[2], C)
C, z[2] = madd2(m, 3486998266802970665, z[3], C)
z[3] = C
}
{
// m = z[0]n'[0] mod W
m := z[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, z[0])
C, z[0] = madd2(m, 2896914383306846353, z[1], C)
C, z[1] = madd2(m, 13281191951274694749, z[2], C)
C, z[2] = madd2(m, 3486998266802970665, z[3], C)
z[3] = C
}
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}
// ToMont converts z to Montgomery form
// sets and returns z = z * r^2
func (z *Element) ToMont() *Element {
var rSquare = Element{
1997599621687373223,
6052339484930628067,
10108755138030829701,
150537098327114917,
}
return z.MulAssign(&rSquare)
}
// ToRegular returns z in regular form (doesn't mutate z)
func (z Element) ToRegular() Element {
return *z.FromMont()
}
// String returns the string form of an Element in Montgomery form
func (z *Element) String() string {
var _z big.Int
return z.ToBigIntRegular(&_z).String()
}
// ToBigInt returns z as a big.Int in Montgomery form
func (z *Element) ToBigInt(res *big.Int) *big.Int {
if bits.UintSize == 64 {
bits := (*[4]big.Word)(unsafe.Pointer(z))
return res.SetBits(bits[:])
} else {
var bits [8]big.Word
for i := 0; i < len(z); i++ {
bits[i*2] = big.Word(z[i])
bits[i*2+1] = big.Word(z[i] >> 32)
}
return res.SetBits(bits[:])
}
}
// ToBigIntRegular returns z as a big.Int in regular form
func (z Element) ToBigIntRegular(res *big.Int) *big.Int {
z.FromMont()
if bits.UintSize == 64 {
bits := (*[4]big.Word)(unsafe.Pointer(&z))
return res.SetBits(bits[:])
} else {
var bits [8]big.Word
for i := 0; i < len(z); i++ {
bits[i*2] = big.Word(z[i])
bits[i*2+1] = big.Word(z[i] >> 32)
}
return res.SetBits(bits[:])
}
}
// SetBigInt sets z to v (regular form) and returns z in Montgomery form
func (z *Element) SetBigInt(v *big.Int) *Element {
z.SetZero()
zero := big.NewInt(0)
q := elementModulusBigInt()
// copy input
vv := new(big.Int).Set(v)
// while v < 0, v+=q
for vv.Cmp(zero) == -1 {
vv.Add(vv, q)
}
// while v > q, v-=q
for vv.Cmp(q) == 1 {
vv.Sub(vv, q)
}
// if v == q, return 0
if vv.Cmp(q) == 0 {
return z
}
// v should
vBits := vv.Bits()
if bits.UintSize == 64 {
for i := 0; i < len(vBits); i++ {
z[i] = uint64(vBits[i])
}
} else {
for i := 0; i < len(vBits); i++ {
if i%2 == 0 {
z[i/2] = uint64(vBits[i])
} else {
z[i/2] |= uint64(vBits[i]) << 32
}
}
}
return z.ToMont()
}
// SetString creates a big.Int with s (in base 10) and calls SetBigInt on z
func (z *Element) SetString(s string) *Element {
x, ok := new(big.Int).SetString(s, 10)
if !ok {
panic("Element.SetString failed -> can't parse number in base10 into a big.Int")
}
return z.SetBigInt(x)
}
// Mul z = x * y mod q
func (z *Element) Mul(x, y *Element) *Element {
var t [4]uint64
var c [3]uint64
{
// round 0
v := x[0]
c[1], c[0] = bits.Mul64(v, y[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd1(v, y[1], c[1])
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd1(v, y[2], c[1])
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd1(v, y[3], c[1])
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
{
// round 1
v := x[1]
c[1], c[0] = madd1(v, y[0], t[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd2(v, y[1], c[1], t[1])
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd2(v, y[2], c[1], t[2])
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd2(v, y[3], c[1], t[3])
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
{
// round 2
v := x[2]
c[1], c[0] = madd1(v, y[0], t[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd2(v, y[1], c[1], t[1])
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd2(v, y[2], c[1], t[2])
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd2(v, y[3], c[1], t[3])
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
{
// round 3
v := x[3]
c[1], c[0] = madd1(v, y[0], t[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd2(v, y[1], c[1], t[1])
c[2], z[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd2(v, y[2], c[1], t[2])
c[2], z[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd2(v, y[3], c[1], t[3])
z[3], z[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}
// MulAssign z = z * x mod q
func (z *Element) MulAssign(x *Element) *Element {
var t [4]uint64
var c [3]uint64
{
// round 0
v := z[0]
c[1], c[0] = bits.Mul64(v, x[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd1(v, x[1], c[1])
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd1(v, x[2], c[1])
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd1(v, x[3], c[1])
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
{
// round 1
v := z[1]
c[1], c[0] = madd1(v, x[0], t[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd2(v, x[1], c[1], t[1])
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd2(v, x[2], c[1], t[2])
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd2(v, x[3], c[1], t[3])
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
{
// round 2
v := z[2]
c[1], c[0] = madd1(v, x[0], t[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd2(v, x[1], c[1], t[1])
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd2(v, x[2], c[1], t[2])
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd2(v, x[3], c[1], t[3])
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
{
// round 3
v := z[3]
c[1], c[0] = madd1(v, x[0], t[0])
m := c[0] * 14042775128853446655
c[2] = madd0(m, 4891460686036598785, c[0])
c[1], c[0] = madd2(v, x[1], c[1], t[1])
c[2], z[0] = madd2(m, 2896914383306846353, c[2], c[0])
c[1], c[0] = madd2(v, x[2], c[1], t[2])
c[2], z[1] = madd2(m, 13281191951274694749, c[2], c[0])
c[1], c[0] = madd2(v, x[3], c[1], t[3])
z[3], z[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
}
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}
// Square z = x * x mod q
func (z *Element) Square(x *Element) *Element {
var p [4]uint64
var u, v uint64
{
// round 0
u, p[0] = bits.Mul64(x[0], x[0])
m := p[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, p[0])
var t uint64
t, u, v = madd1sb(x[0], x[1], u)
C, p[0] = madd2(m, 2896914383306846353, v, C)
t, u, v = madd1s(x[0], x[2], t, u)
C, p[1] = madd2(m, 13281191951274694749, v, C)
_, u, v = madd1s(x[0], x[3], t, u)
p[3], p[2] = madd3(m, 3486998266802970665, v, C, u)
}
{
// round 1
m := p[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, p[0])
u, v = madd1(x[1], x[1], p[1])
C, p[0] = madd2(m, 2896914383306846353, v, C)
var t uint64
t, u, v = madd2sb(x[1], x[2], p[2], u)
C, p[1] = madd2(m, 13281191951274694749, v, C)
_, u, v = madd2s(x[1], x[3], p[3], t, u)
p[3], p[2] = madd3(m, 3486998266802970665, v, C, u)
}
{
// round 2
m := p[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, p[0])
C, p[0] = madd2(m, 2896914383306846353, p[1], C)
u, v = madd1(x[2], x[2], p[2])
C, p[1] = madd2(m, 13281191951274694749, v, C)
_, u, v = madd2sb(x[2], x[3], p[3], u)
p[3], p[2] = madd3(m, 3486998266802970665, v, C, u)
}
{
// round 3
m := p[0] * 14042775128853446655
C := madd0(m, 4891460686036598785, p[0])
C, z[0] = madd2(m, 2896914383306846353, p[1], C)
C, z[1] = madd2(m, 13281191951274694749, p[2], C)
u, v = madd1(x[3], x[3], p[3])
z[3], z[2] = madd3(m, 3486998266802970665, v, C, u)
}
// if z > q --> z -= q
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
var b uint64
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
}
return z
}

234
ff/element_test.go Normal file
View File

@@ -0,0 +1,234 @@
// Code generated by goff DO NOT EDIT
package ff
import (
"crypto/rand"
"math/big"
mrand "math/rand"
"testing"
)
func TestELEMENTCorrectnessAgainstBigInt(t *testing.T) {
modulus, _ := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
cmpEandB := func(e *Element, b *big.Int, name string) {
var _e big.Int
if e.FromMont().ToBigInt(&_e).Cmp(b) != 0 {
t.Fatal(name, "failed")
}
}
var modulusMinusOne, one big.Int
one.SetUint64(1)
modulusMinusOne.Sub(modulus, &one)
for i := 0; i < 1000; i++ {
// sample 2 random big int
b1, _ := rand.Int(rand.Reader, modulus)
b2, _ := rand.Int(rand.Reader, modulus)
rExp := mrand.Uint64()
// adding edge cases
// TODO need more edge cases
switch i {
case 0:
rExp = 0
b1.SetUint64(0)
case 1:
b2.SetUint64(0)
case 2:
b1.SetUint64(0)
b2.SetUint64(0)
case 3:
rExp = 0
case 4:
rExp = 1
case 5:
rExp = ^uint64(0) // max uint
case 6:
rExp = 2
b1.Set(&modulusMinusOne)
case 7:
b2.Set(&modulusMinusOne)
case 8:
b1.Set(&modulusMinusOne)
b2.Set(&modulusMinusOne)
}
rbExp := new(big.Int).SetUint64(rExp)
var bMul, bAdd, bSub, bDiv, bNeg, bLsh, bInv, bExp, bSquare big.Int
// e1 = mont(b1), e2 = mont(b2)
var e1, e2, eMul, eAdd, eSub, eDiv, eNeg, eLsh, eInv, eExp, eSquare, eMulAssign, eSubAssign, eAddAssign Element
e1.SetBigInt(b1)
e2.SetBigInt(b2)
// (e1*e2).FromMont() === b1*b2 mod q ... etc
eSquare.Square(&e1)
eMul.Mul(&e1, &e2)
eMulAssign.Set(&e1)
eMulAssign.MulAssign(&e2)
eAdd.Add(&e1, &e2)
eAddAssign.Set(&e1)
eAddAssign.AddAssign(&e2)
eSub.Sub(&e1, &e2)
eSubAssign.Set(&e1)
eSubAssign.SubAssign(&e2)
eDiv.Div(&e1, &e2)
eNeg.Neg(&e1)
eInv.Inverse(&e1)
eExp.Exp(e1, rExp)
eLsh.Double(&e1)
// same operations with big int
bAdd.Add(b1, b2).Mod(&bAdd, modulus)
bMul.Mul(b1, b2).Mod(&bMul, modulus)
bSquare.Mul(b1, b1).Mod(&bSquare, modulus)
bSub.Sub(b1, b2).Mod(&bSub, modulus)
bDiv.ModInverse(b2, modulus)
bDiv.Mul(&bDiv, b1).
Mod(&bDiv, modulus)
bNeg.Neg(b1).Mod(&bNeg, modulus)
bInv.ModInverse(b1, modulus)
bExp.Exp(b1, rbExp, modulus)
bLsh.Lsh(b1, 1).Mod(&bLsh, modulus)
cmpEandB(&eSquare, &bSquare, "Square")
cmpEandB(&eMul, &bMul, "Mul")
cmpEandB(&eMulAssign, &bMul, "MulAssign")
cmpEandB(&eAdd, &bAdd, "Add")
cmpEandB(&eAddAssign, &bAdd, "AddAssign")
cmpEandB(&eSub, &bSub, "Sub")
cmpEandB(&eSubAssign, &bSub, "SubAssign")
cmpEandB(&eDiv, &bDiv, "Div")
cmpEandB(&eNeg, &bNeg, "Neg")
cmpEandB(&eInv, &bInv, "Inv")
cmpEandB(&eExp, &bExp, "Exp")
cmpEandB(&eLsh, &bLsh, "Lsh")
}
}
func TestELEMENTIsRandom(t *testing.T) {
for i := 0; i < 1000; i++ {
var x, y Element
x.SetRandom()
y.SetRandom()
if x.Equal(&y) {
t.Fatal("2 random numbers are unlikely to be equal")
}
}
}
// -------------------------------------------------------------------------------------------------
// benchmarks
// most benchmarks are rudimentary and should sample a large number of random inputs
// or be run multiple times to ensure it didn't measure the fastest path of the function
// TODO: clean up and push benchmarking branch
var benchResElement Element
func BenchmarkInverseELEMENT(b *testing.B) {
var x Element
x.SetRandom()
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Inverse(&x)
}
}
func BenchmarkExpELEMENT(b *testing.B) {
var x Element
x.SetRandom()
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Exp(x, mrand.Uint64())
}
}
func BenchmarkDoubleELEMENT(b *testing.B) {
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Double(&benchResElement)
}
}
func BenchmarkAddELEMENT(b *testing.B) {
var x Element
x.SetRandom()
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Add(&x, &benchResElement)
}
}
func BenchmarkSubELEMENT(b *testing.B) {
var x Element
x.SetRandom()
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Sub(&x, &benchResElement)
}
}
func BenchmarkNegELEMENT(b *testing.B) {
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Neg(&benchResElement)
}
}
func BenchmarkDivELEMENT(b *testing.B) {
var x Element
x.SetRandom()
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Div(&x, &benchResElement)
}
}
func BenchmarkFromMontELEMENT(b *testing.B) {
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.FromMont()
}
}
func BenchmarkToMontELEMENT(b *testing.B) {
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.ToMont()
}
}
func BenchmarkSquareELEMENT(b *testing.B) {
benchResElement.SetRandom()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.Square(&benchResElement)
}
}
func BenchmarkMulAssignELEMENT(b *testing.B) {
x := Element{
1997599621687373223,
6052339484930628067,
10108755138030829701,
150537098327114917,
}
benchResElement.SetOne()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchResElement.MulAssign(&x)
}
}

6
ff/util.go Normal file
View File

@@ -0,0 +1,6 @@
package ff
// NewElement returns a new empty *Element
func NewElement() *Element {
return &Element{}
}

View File

@@ -1,152 +0,0 @@
// code originally taken from https://github.com/arnaucube/go-snark (https://github.com/arnaucube/go-snark/blob/master/fields/fq.go), pasted here to ensure compatibility among future changes
package field
import (
"bytes"
"crypto/rand"
"math/big"
)
// Fq is the Z field over modulus Q
type Fq struct {
Q *big.Int // Q
}
// NewFq generates a new Fq
func NewFq(q *big.Int) Fq {
return Fq{
q,
}
}
// Zero returns a Zero value on the Fq
func (fq Fq) Zero() *big.Int {
return big.NewInt(int64(0))
}
// One returns a One value on the Fq
func (fq Fq) One() *big.Int {
return big.NewInt(int64(1))
}
// Add performs an addition on the Fq
func (fq Fq) Add(a, b *big.Int) *big.Int {
r := new(big.Int).Add(a, b)
return new(big.Int).Mod(r, fq.Q)
}
// Double performs a doubling on the Fq
func (fq Fq) Double(a *big.Int) *big.Int {
r := new(big.Int).Add(a, a)
return new(big.Int).Mod(r, fq.Q)
}
// Sub performs a subtraction on the Fq
func (fq Fq) Sub(a, b *big.Int) *big.Int {
r := new(big.Int).Sub(a, b)
return new(big.Int).Mod(r, fq.Q)
}
// Neg performs a negation on the Fq
func (fq Fq) Neg(a *big.Int) *big.Int {
m := new(big.Int).Neg(a)
return new(big.Int).Mod(m, fq.Q)
}
// Mul performs a multiplication on the Fq
func (fq Fq) Mul(a, b *big.Int) *big.Int {
m := new(big.Int).Mul(a, b)
return new(big.Int).Mod(m, fq.Q)
}
func (fq Fq) MulScalar(base, e *big.Int) *big.Int {
return fq.Mul(base, e)
}
// Inverse returns the inverse on the Fq
func (fq Fq) Inverse(a *big.Int) *big.Int {
return new(big.Int).ModInverse(a, fq.Q)
}
// Div performs the division over the finite field
func (fq Fq) Div(a, b *big.Int) *big.Int {
d := fq.Mul(a, fq.Inverse(b))
return new(big.Int).Mod(d, fq.Q)
}
// Square performs a square operation on the Fq
func (fq Fq) Square(a *big.Int) *big.Int {
m := new(big.Int).Mul(a, a)
return new(big.Int).Mod(m, fq.Q)
}
// Exp performs the exponential over Fq
func (fq Fq) Exp(base *big.Int, e *big.Int) *big.Int {
res := fq.One()
rem := fq.Copy(e)
exp := base
for !bytes.Equal(rem.Bytes(), big.NewInt(int64(0)).Bytes()) {
if BigIsOdd(rem) {
res = fq.Mul(res, exp)
}
exp = fq.Square(exp)
rem = new(big.Int).Rsh(rem, 1)
}
return res
}
func (fq Fq) Rand() (*big.Int, error) {
maxbits := fq.Q.BitLen()
b := make([]byte, (maxbits/8)-1)
_, err := rand.Read(b)
if err != nil {
return nil, err
}
r := new(big.Int).SetBytes(b)
rq := new(big.Int).Mod(r, fq.Q)
// r over q, nil
return rq, nil
}
func (fq Fq) IsZero(a *big.Int) bool {
return bytes.Equal(a.Bytes(), fq.Zero().Bytes())
}
func (fq Fq) Copy(a *big.Int) *big.Int {
return new(big.Int).SetBytes(a.Bytes())
}
func (fq Fq) Affine(a *big.Int) *big.Int {
nq := fq.Neg(fq.Q)
aux := a
if aux.Cmp(big.NewInt(int64(0))) == -1 { // negative value
if aux.Cmp(nq) != 1 { // aux less or equal nq
aux = new(big.Int).Mod(aux, fq.Q)
}
if aux.Cmp(big.NewInt(int64(0))) == -1 { // negative value
aux = new(big.Int).Add(aux, fq.Q)
}
} else {
if aux.Cmp(fq.Q) != -1 { // aux greater or equal nq
aux = new(big.Int).Mod(aux, fq.Q)
}
}
return aux
}
func (fq Fq) Equal(a, b *big.Int) bool {
aAff := fq.Affine(a)
bAff := fq.Affine(b)
return bytes.Equal(aAff.Bytes(), bAff.Bytes())
}
func BigIsOdd(n *big.Int) bool {
one := big.NewInt(int64(1))
and := new(big.Int).And(n, one)
return bytes.Equal(and.Bytes(), big.NewInt(int64(1)).Bytes())
}

View File

@@ -1,39 +0,0 @@
// code originally taken from https://github.com/arnaucube/go-snark (https://github.com/arnaucube/go-snark/blob/master/fields/fq.go), pasted here to ensure compatibility among future changes
package field
import (
"math/big"
"testing"
"github.com/stretchr/testify/assert"
)
func iToBig(a int) *big.Int {
return big.NewInt(int64(a))
}
func TestFq1(t *testing.T) {
fq1 := NewFq(iToBig(7))
res := fq1.Add(iToBig(4), iToBig(4))
assert.Equal(t, iToBig(1), fq1.Affine(res))
res = fq1.Double(iToBig(5))
assert.Equal(t, iToBig(3), fq1.Affine(res))
res = fq1.Sub(iToBig(5), iToBig(7))
assert.Equal(t, iToBig(5), fq1.Affine(res))
res = fq1.Neg(iToBig(5))
assert.Equal(t, iToBig(2), fq1.Affine(res))
res = fq1.Mul(iToBig(5), iToBig(11))
assert.Equal(t, iToBig(6), fq1.Affine(res))
res = fq1.Inverse(iToBig(4))
assert.Equal(t, iToBig(2), res)
res = fq1.Square(iToBig(5))
assert.Equal(t, iToBig(4), res)
}

7
go.mod
View File

@@ -4,7 +4,8 @@ go 1.12
require (
github.com/dchest/blake512 v1.0.0
github.com/ethereum/go-ethereum v1.8.27
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4
github.com/ethereum/go-ethereum v1.9.12
github.com/stretchr/testify v1.4.0
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
)

163
go.sum
View File

@@ -1,20 +1,173 @@
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.5.3 h1:2odJnXLbFZcoV9KYtQ+7TH1UOq3dn3AssMgieaezkR4=
github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/blake512 v1.0.0 h1:oDFEQFIqFSeuA34xLtXZ/rWxCXdSjirjzPhey5EUvmA=
github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI=
github.com/ethereum/go-ethereum v1.8.27 h1:d+gkiLaBDk5fn3Pe/xNVaMrB/ozI+AUB2IlVBp29IrY=
github.com/ethereum/go-ethereum v1.8.27/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY=
github.com/iden3/go-iden3 v0.0.5 h1:NV6HXnLmp+1YmKd2FmymzU6OAP77q1WWDcB/B+BUL9g=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa h1:XKAhUk/dtp+CV0VO6mhG2V7jA9vbcGcnYF/Ay9NjZrY=
github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs=
github.com/ethereum/go-ethereum v1.9.12 h1:EPtimwsp/KGDSiXcNunzsI4kefdsMHZGJntKx3fvbaI=
github.com/ethereum/go-ethereum v1.9.12/go.mod h1:PvsVkQmhZFx92Y+h2ylythYlheEDt/uBgFbl61Js/jo=
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=

View File

@@ -6,82 +6,84 @@ import (
"github.com/ethereum/go-ethereum/crypto"
_constants "github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/field"
"github.com/iden3/go-iden3-crypto/ff"
"github.com/iden3/go-iden3-crypto/utils"
)
// SEED defines the seed used to constants
const SEED = "mimc"
var constants = generateConstantsData()
type constantsData struct {
maxFieldVal *big.Int
seedHash *big.Int
iv *big.Int
fqR field.Fq
nRounds int
cts []*big.Int
cts []*ff.Element
}
func generateConstantsData() constantsData {
var constants constantsData
fqR := field.NewFq(_constants.Q)
constants.fqR = fqR
// maxFieldVal is the R value of the Finite Field
constants.maxFieldVal = constants.fqR.Q
constants.seedHash = new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED)))
c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED + "_iv")))
constants.iv = new(big.Int).Mod(c, constants.maxFieldVal)
constants.iv = new(big.Int).Mod(c, _constants.Q)
constants.nRounds = 91
cts := getConstants(constants.fqR, SEED, constants.nRounds)
cts := getConstants(SEED, constants.nRounds)
constants.cts = cts
return constants
}
func getConstants(fqR field.Fq, seed string, nRounds int) []*big.Int {
cts := make([]*big.Int, nRounds)
cts[0] = big.NewInt(int64(0))
func getConstants(seed string, nRounds int) []*ff.Element {
cts := make([]*ff.Element, nRounds)
cts[0] = ff.NewElement()
c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED)))
for i := 1; i < nRounds; i++ {
c = new(big.Int).SetBytes(crypto.Keccak256(c.Bytes()))
n := fqR.Affine(c)
cts[i] = n
n := new(big.Int).Mod(c, _constants.Q)
cts[i] = ff.NewElement().SetBigInt(n)
}
return cts
}
// MIMC7HashGeneric performs the MIMC7 hash over a *big.Int, in a generic way, where it can be specified the Finite Field over R, and the number of rounds
func MIMC7HashGeneric(fqR field.Fq, xIn, k *big.Int, nRounds int) *big.Int {
cts := getConstants(fqR, SEED, nRounds)
var r *big.Int
// MIMC7HashGeneric performs the MIMC7 hash over a *big.Int, in a generic way,
// where it can be specified the Finite Field over R, and the number of rounds
func MIMC7HashGeneric(xInBI, kBI *big.Int, nRounds int) *big.Int { //nolint:golint
xIn := ff.NewElement().SetBigInt(xInBI)
k := ff.NewElement().SetBigInt(kBI)
cts := getConstants(SEED, nRounds)
var r *ff.Element
for i := 0; i < nRounds; i++ {
var t *big.Int
var t *ff.Element
if i == 0 {
t = fqR.Add(xIn, k)
t = ff.NewElement().Add(xIn, k)
} else {
t = fqR.Add(fqR.Add(r, k), cts[i])
t = ff.NewElement().Add(ff.NewElement().Add(r, k), cts[i])
}
t2 := fqR.Square(t)
t4 := fqR.Square(t2)
r = fqR.Mul(fqR.Mul(t4, t2), t)
t2 := ff.NewElement().Square(t)
t4 := ff.NewElement().Square(t2)
r = ff.NewElement().Mul(ff.NewElement().Mul(t4, t2), t)
}
return fqR.Affine(fqR.Add(r, k))
rE := ff.NewElement().Add(r, k)
res := big.NewInt(0)
rE.ToBigIntRegular(res)
return res
}
// HashGeneric performs the MIMC7 hash over a *big.Int array, in a generic way, where it can be specified the Finite Field over R, and the number of rounds
func HashGeneric(iv *big.Int, arr []*big.Int, fqR field.Fq, nRounds int) (*big.Int, error) {
if !utils.CheckBigIntArrayInField(arr, constants.fqR.Q) {
// HashGeneric performs the MIMC7 hash over a *big.Int array, in a generic way,
// where it can be specified the Finite Field over R, and the number of rounds
func HashGeneric(iv *big.Int, arr []*big.Int, nRounds int) (*big.Int, error) {
if !utils.CheckBigIntArrayInField(arr) {
return nil, errors.New("inputs values not inside Finite Field")
}
r := iv
var err error
for i := 0; i < len(arr); i++ {
r = MIMC7HashGeneric(fqR, r, arr[i], nRounds)
r = MIMC7HashGeneric(r, arr[i], nRounds)
if err != nil {
return r, err
}
@@ -89,48 +91,57 @@ func HashGeneric(iv *big.Int, arr []*big.Int, fqR field.Fq, nRounds int) (*big.I
return r, nil
}
// MIMC7Hash performs the MIMC7 hash over a *big.Int, using the Finite Field over R and the number of rounds setted in the `constants` variable
func MIMC7Hash(xIn, k *big.Int) *big.Int {
var r *big.Int
// MIMC7Hash performs the MIMC7 hash over a *big.Int, using the Finite Field
// over R and the number of rounds setted in the `constants` variable
func MIMC7Hash(xInBI, kBI *big.Int) *big.Int { //nolint:golint
xIn := ff.NewElement().SetBigInt(xInBI)
k := ff.NewElement().SetBigInt(kBI)
var r *ff.Element
for i := 0; i < constants.nRounds; i++ {
var t *big.Int
var t *ff.Element
if i == 0 {
t = constants.fqR.Add(xIn, k)
t = ff.NewElement().Add(xIn, k)
} else {
t = constants.fqR.Add(constants.fqR.Add(r, k), constants.cts[i])
t = ff.NewElement().Add(ff.NewElement().Add(r, k), constants.cts[i])
}
t2 := constants.fqR.Square(t)
t4 := constants.fqR.Square(t2)
r = constants.fqR.Mul(constants.fqR.Mul(t4, t2), t)
t2 := ff.NewElement().Square(t)
t4 := ff.NewElement().Square(t2)
r = ff.NewElement().Mul(ff.NewElement().Mul(t4, t2), t)
}
return constants.fqR.Affine(constants.fqR.Add(r, k))
rE := ff.NewElement().Add(r, k)
res := big.NewInt(0)
rE.ToBigIntRegular(res)
return res
}
// Hash performs the MIMC7 hash over a *big.Int array
func Hash(arr []*big.Int, key *big.Int) (*big.Int, error) {
if !utils.CheckBigIntArrayInField(arr, constants.fqR.Q) {
if !utils.CheckBigIntArrayInField(arr) {
return nil, errors.New("inputs values not inside Finite Field")
}
var r *big.Int
if key == nil {
r = constants.fqR.Zero()
r = big.NewInt(0)
} else {
r = key
}
for i := 0; i < len(arr); i++ {
r = constants.fqR.Add(
constants.fqR.Add(
r = new(big.Int).Add(
new(big.Int).Add(
r,
arr[i],
),
MIMC7Hash(arr[i], r))
r = new(big.Int).Mod(r, _constants.Q)
}
return r, nil
}
// HashBytes hashes a msg byte slice by blocks of 31 bytes encoded as
// little-endian
func HashBytes(b []byte) (*big.Int, error) {
func HashBytes(b []byte) *big.Int {
n := 31
bElems := make([]*big.Int, 0, len(b)/n+1)
for i := 0; i < len(b)/n; i++ {
@@ -143,5 +154,9 @@ func HashBytes(b []byte) (*big.Int, error) {
utils.SetBigIntFromLEBytes(v, b[(len(b)/n)*n:])
bElems = append(bElems, v)
}
return Hash(bElems, nil)
h, err := Hash(bElems, nil)
if err != nil {
panic(err)
}
return h
}

View File

@@ -6,15 +6,18 @@ import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/iden3/go-iden3-crypto/field"
"github.com/stretchr/testify/assert"
)
func TestKeccak256(t *testing.T) {
res := crypto.Keccak256([]byte(SEED))
assert.Equal(t, "b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e", hex.EncodeToString(res))
assert.Equal(t,
"b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e",
hex.EncodeToString(res))
c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED)))
assert.Equal(t, "82724731331859054037315113496710413141112897654334566532528783843265082629790", c.String())
assert.Equal(t,
"82724731331859054037315113496710413141112897654334566532528783843265082629790",
c.String())
}
func TestMIMC7Generic(t *testing.T) {
@@ -22,18 +25,18 @@ func TestMIMC7Generic(t *testing.T) {
b2 := big.NewInt(int64(2))
b3 := big.NewInt(int64(3))
r, ok := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
assert.True(t, ok)
fqR := field.NewFq(r)
bigArray := []*big.Int{b1, b2, b3}
// Generic Hash
mhg := MIMC7HashGeneric(fqR, b1, b2, 91)
assert.Equal(t, "10594780656576967754230020536574539122676596303354946869887184401991294982664", mhg.String())
hg, err := HashGeneric(fqR.Zero(), bigArray, fqR, 91)
mhg := MIMC7HashGeneric(b1, b2, 91)
assert.Equal(t,
"10594780656576967754230020536574539122676596303354946869887184401991294982664",
mhg.String())
hg, err := HashGeneric(big.NewInt(0), bigArray, 91)
assert.Nil(t, err)
assert.Equal(t, "6464402164086696096195815557694604139393321133243036833927490113253119343397", (*big.Int)(hg).String())
assert.Equal(t,
"6464402164086696096195815557694604139393321133243036833927490113253119343397",
(*big.Int)(hg).String())
}
func TestMIMC7(t *testing.T) {
@@ -48,7 +51,8 @@ func TestMIMC7(t *testing.T) {
h1, err := Hash(bigArray1, nil)
assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h1).Bytes()), "0x237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd")
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h1).Bytes()),
"0x237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd")
// h2a, hash of 2 elements
bigArray2a := []*big.Int{b78, b41}
@@ -56,19 +60,22 @@ func TestMIMC7(t *testing.T) {
h2a, err := Hash(bigArray2a, nil)
assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2a).Bytes()), "0x067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70")
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2a).Bytes()),
"0x067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70")
// h2b, hash of 2 elements
bigArray2b := []*big.Int{b12, b45}
mh2b := MIMC7Hash(b12, b45)
assert.Nil(t, err)
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(mh2b).Bytes()), "0x2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4")
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(mh2b).Bytes()),
"0x2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4")
h2b, err := Hash(bigArray2b, nil)
assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2b).Bytes()), "0x15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879")
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2b).Bytes()),
"0x15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879")
// h4, hash of 4 elements
bigArray4 := []*big.Int{b12, b45, b78, b41}
@@ -76,12 +83,14 @@ func TestMIMC7(t *testing.T) {
h4, err := Hash(bigArray4, nil)
assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h4).Bytes()), "0x284bc1f34f335933a23a433b6ff3ee179d682cd5e5e2fcdd2d964afa85104beb")
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h4).Bytes()),
"0x284bc1f34f335933a23a433b6ff3ee179d682cd5e5e2fcdd2d964afa85104beb")
msg := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
hmsg, err := HashBytes(msg)
assert.Nil(t, err)
assert.Equal(t, "16855787120419064316734350414336285711017110414939748784029922801367685456065", hmsg.String())
msg := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") //nolint:lll
hmsg := HashBytes(msg)
assert.Equal(t,
"16855787120419064316734350414336285711017110414939748784029922801367685456065",
hmsg.String())
}
func BenchmarkMIMC7(b *testing.B) {
@@ -92,6 +101,6 @@ func BenchmarkMIMC7(b *testing.B) {
bigArray4 := []*big.Int{b12, b45, b78, b41}
for i := 0; i < b.N; i++ {
Hash(bigArray4, nil)
Hash(bigArray4, nil) //nolint:errcheck,gosec
}
}

3511
poseidon/constants.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,209 +1,89 @@
package poseidon
import (
"bytes"
"errors"
"fmt"
"math/big"
"strconv"
"github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/ff"
"github.com/iden3/go-iden3-crypto/utils"
"golang.org/x/crypto/blake2b"
)
const SEED = "poseidon"
const NROUNDSF = 8
const NROUNDSP = 57
const T = 6
const NROUNDSF = 8 //nolint:golint
var constC []*big.Int
var constM [T][T]*big.Int
var NROUNDSP = []int{56, 57, 56, 60, 60, 63, 64, 63} //nolint:golint
func Zero() *big.Int {
return new(big.Int)
}
func modQ(v *big.Int) {
v.Mod(v, constants.Q)
}
func init() {
constC = getPseudoRandom(SEED+"_constants", NROUNDSF+NROUNDSP)
constM = getMDS()
}
func leByteArrayToBigInt(b []byte) *big.Int {
res := big.NewInt(0)
for i := 0; i < len(b); i++ {
n := big.NewInt(int64(b[i]))
res = new(big.Int).Add(res, new(big.Int).Lsh(n, uint(i*8)))
}
return res
}
func getPseudoRandom(seed string, n int) []*big.Int {
res := make([]*big.Int, n)
hash := blake2b.Sum256([]byte(seed))
for i := 0; i < n; i++ {
hashBigInt := Zero()
res[i] = utils.SetBigIntFromLEBytes(hashBigInt, hash[:])
modQ(res[i])
hash = blake2b.Sum256(hash[:])
}
return res
}
func nonceToString(n int) string {
r := strconv.Itoa(n)
for len(r) < 4 {
r = "0" + r
}
return r
}
// https://eprint.iacr.org/2019/458.pdf pag.8
func getMDS() [T][T]*big.Int {
nonce := 0
cauchyMatrix := getPseudoRandom(SEED+"_matrix_"+nonceToString(nonce), T*2)
for !checkAllDifferent(cauchyMatrix) {
nonce += 1
cauchyMatrix = getPseudoRandom(SEED+"_matrix_"+nonceToString(nonce), T*2)
}
var m [T][T]*big.Int
for i := 0; i < T; i++ {
// var mi []*big.Int
for j := 0; j < T; j++ {
m[i][j] = new(big.Int).Sub(cauchyMatrix[i], cauchyMatrix[T+j])
m[i][j].ModInverse(m[i][j], constants.Q)
}
}
return m
}
func checkAllDifferent(v []*big.Int) bool {
for i := 0; i < len(v); i++ {
if bytes.Equal(v[i].Bytes(), big.NewInt(int64(0)).Bytes()) {
return false
}
for j := i + 1; j < len(v); j++ {
if bytes.Equal(v[i].Bytes(), v[j].Bytes()) {
return false
}
}
}
return true
func zero() *ff.Element {
return ff.NewElement()
}
// ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf
func ark(state [T]*big.Int, c *big.Int) {
for i := 0; i < T; i++ {
modQ(state[i].Add(state[i], c))
func ark(state []*ff.Element, c []*ff.Element, it int) {
for i := 0; i < len(state); i++ {
state[i].Add(state[i], c[it+i])
}
}
// cubic performs x^5 mod p
// exp5 performs x^5 mod p
// https://eprint.iacr.org/2019/458.pdf page 8
var five = big.NewInt(5)
func cubic(a *big.Int) {
a.Exp(a, five, constants.Q)
func exp5(a *ff.Element) {
a.Exp(*a, 5)
}
// sbox https://eprint.iacr.org/2019/458.pdf page 6
func sbox(state [T]*big.Int, i int) {
if (i < NROUNDSF/2) || (i >= NROUNDSF/2+NROUNDSP) {
for j := 0; j < T; j++ {
cubic(state[j])
func sbox(nRoundsF, nRoundsP int, state []*ff.Element, i int) {
if (i < nRoundsF/2) || (i >= nRoundsF/2+nRoundsP) {
for j := 0; j < len(state); j++ {
exp5(state[j])
}
} else {
cubic(state[0])
exp5(state[0])
}
}
// mix returns [[matrix]] * [vector]
func mix(state [T]*big.Int, newState [T]*big.Int, m [T][T]*big.Int) {
mul := Zero()
for i := 0; i < T; i++ {
newState[i].SetInt64(0)
for j := 0; j < T; j++ {
modQ(mul.Mul(m[i][j], state[j]))
func mix(state []*ff.Element, newState []*ff.Element, m [][]*ff.Element) {
mul := zero()
for i := 0; i < len(state); i++ {
newState[i].SetUint64(0)
for j := 0; j < len(state); j++ {
mul.Mul(m[i][j], state[j])
newState[i].Add(newState[i], mul)
}
modQ(newState[i])
}
}
// PoseidonHash computes the Poseidon hash for the given inputs
func PoseidonHash(inp [T]*big.Int) (*big.Int, error) {
if !utils.CheckBigIntArrayInField(inp[:], constants.Q) {
// Hash computes the Poseidon hash for the given inputs
func Hash(inpBI []*big.Int) (*big.Int, error) {
t := len(inpBI) + 1
if len(inpBI) == 0 || len(inpBI) >= len(NROUNDSP)-1 {
return nil, fmt.Errorf("invalid inputs length %d, max %d", len(inpBI), len(NROUNDSP)-1)
}
if !utils.CheckBigIntArrayInField(inpBI[:]) {
return nil, errors.New("inputs values not inside Finite Field")
}
state := [T]*big.Int{}
for i := 0; i < T; i++ {
state[i] = new(big.Int).Set(inp[i])
inp := utils.BigIntArrayToElementArray(inpBI[:])
state := make([]*ff.Element, t)
state[0] = zero()
copy(state[1:], inp[:])
nRoundsF := NROUNDSF
nRoundsP := NROUNDSP[t-2]
newState := make([]*ff.Element, t)
for i := 0; i < t; i++ {
newState[i] = zero()
}
// ARK --> SBox --> M, https://eprint.iacr.org/2019/458.pdf pag.5
var newState [T]*big.Int
for i := 0; i < T; i++ {
newState[i] = Zero()
}
for i := 0; i < NROUNDSF+NROUNDSP; i++ {
ark(state, constC[i])
sbox(state, i)
mix(state, newState, constM)
for i := 0; i < nRoundsF+nRoundsP; i++ {
ark(state, c.c[t-2], i*t)
sbox(nRoundsF, nRoundsP, state, i)
mix(state, newState, c.m[t-2])
state, newState = newState, state
}
return state[0], nil
}
// Hash performs the Poseidon hash over a *big.Int array
// in chunks of 5 elements
func Hash(arr []*big.Int) (*big.Int, error) {
if !utils.CheckBigIntArrayInField(arr, constants.Q) {
return nil, errors.New("inputs values not inside Finite Field")
}
r := big.NewInt(1)
for i := 0; i < len(arr); i = i + T - 1 {
var toHash [T]*big.Int
j := 0
for ; j < T-1; j++ {
if i+j >= len(arr) {
break
}
toHash[j] = arr[i+j]
}
toHash[j] = r
j++
for ; j < T; j++ {
toHash[j] = constants.Zero
}
ph, err := PoseidonHash(toHash)
if err != nil {
return nil, err
}
modQ(r.Add(r, ph))
}
rE := state[0]
r := big.NewInt(0)
rE.ToBigIntRegular(r)
return r, nil
}
// HashBytes hashes a msg byte slice by blocks of 31 bytes encoded as
// little-endian
func HashBytes(b []byte) (*big.Int, error) {
n := 31
bElems := make([]*big.Int, 0, len(b)/n+1)
for i := 0; i < len(b)/n; i++ {
v := Zero()
utils.SetBigIntFromLEBytes(v, b[n*i:n*(i+1)])
bElems = append(bElems, v)
}
if len(b)%n != 0 {
v := Zero()
utils.SetBigIntFromLEBytes(v, b[(len(b)/n)*n:])
bElems = append(bElems, v)
}
return Hash(bElems)
}

View File

@@ -12,86 +12,86 @@ import (
func TestBlake2bVersion(t *testing.T) {
h := blake2b.Sum256([]byte("poseidon_constants"))
assert.Equal(t, "e57ba154fb2c47811dc1a2369b27e25a44915b4e4ece4eb8ec74850cb78e01b1", hex.EncodeToString(h[:]))
assert.Equal(t,
"e57ba154fb2c47811dc1a2369b27e25a44915b4e4ece4eb8ec74850cb78e01b1",
hex.EncodeToString(h[:]))
}
func TestPoseidon(t *testing.T) {
b1 := big.NewInt(int64(1))
b2 := big.NewInt(int64(2))
h, err := Hash([]*big.Int{b1, b2})
assert.Nil(t, err)
assert.Equal(t, "4932297968297298434239270129193057052722409868268166443802652458940273154855", h.String())
func TestPoseidonHash(t *testing.T) {
b0 := big.NewInt(0)
b1 := big.NewInt(1)
b2 := big.NewInt(2)
b3 := big.NewInt(int64(3))
b4 := big.NewInt(int64(4))
h, err = Hash([]*big.Int{b3, b4})
h, err := Hash([]*big.Int{b1})
assert.Nil(t, err)
assert.Equal(t, "4635491972858758537477743930622086396911540895966845494943021655521913507504", h.String())
assert.Equal(t,
"18586133768512220936620570745912940619677854269274689475585506675881198879027",
h.String())
msg := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
n := 31
msgElems := make([]*big.Int, 0, len(msg)/n+1)
for i := 0; i < len(msg)/n; i++ {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg[n*i:n*(i+1)])
msgElems = append(msgElems, v)
}
if len(msg)%n != 0 {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg[(len(msg)/n)*n:])
msgElems = append(msgElems, v)
}
hmsg, err := Hash(msgElems)
h, err = Hash([]*big.Int{b1, b2})
assert.Nil(t, err)
assert.Equal(t, "16019700159595764790637132363672701294192939959594423814006267756172551741065", hmsg.String())
assert.Equal(t,
"7853200120776062878684798364095072458815029376092732009249414926327459813530",
h.String())
msg2 := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet.")
msg2Elems := make([]*big.Int, 0, len(msg2)/n+1)
for i := 0; i < len(msg2)/n; i++ {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg2[n*i:n*(i+1)])
msg2Elems = append(msg2Elems, v)
}
if len(msg2)%n != 0 {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg2[(len(msg2)/n)*n:])
msg2Elems = append(msg2Elems, v)
}
hmsg2, err := Hash(msg2Elems)
h, err = Hash([]*big.Int{b1, b2, b0, b0, b0})
assert.Nil(t, err)
assert.Equal(t, "2978613163687734485261639854325792381691890647104372645321246092227111432722", hmsg2.String())
assert.Equal(t,
"1018317224307729531995786483840663576608797660851238720571059489595066344487",
h.String())
h, err = Hash([]*big.Int{b1, b2, b0, b0, b0, b0})
assert.Nil(t, err)
assert.Equal(t,
"15336558801450556532856248569924170992202208561737609669134139141992924267169",
h.String())
hmsg2, err = HashBytes(msg2)
b3 := big.NewInt(3)
b4 := big.NewInt(4)
h, err = Hash([]*big.Int{b3, b4, b0, b0, b0})
assert.Nil(t, err)
assert.Equal(t, "2978613163687734485261639854325792381691890647104372645321246092227111432722", hmsg2.String())
assert.Equal(t,
"5811595552068139067952687508729883632420015185677766880877743348592482390548",
h.String())
h, err = Hash([]*big.Int{b3, b4, b0, b0, b0, b0})
assert.Nil(t, err)
assert.Equal(t,
"12263118664590987767234828103155242843640892839966517009184493198782366909018",
h.String())
b5 := big.NewInt(5)
b6 := big.NewInt(6)
h, err = Hash([]*big.Int{b1, b2, b3, b4, b5, b6})
assert.Nil(t, err)
assert.Equal(t,
"20400040500897583745843009878988256314335038853985262692600694741116813247201",
h.String())
}
func TestPoseidonBrokenChunks(t *testing.T) {
h1, err := Hash([]*big.Int{big.NewInt(0), big.NewInt(1), big.NewInt(2), big.NewInt(3), big.NewInt(4),
big.NewInt(5), big.NewInt(6), big.NewInt(7), big.NewInt(8), big.NewInt(9)})
func TestErrorInputs(t *testing.T) {
b0 := big.NewInt(0)
b1 := big.NewInt(1)
b2 := big.NewInt(2)
_, err := Hash([]*big.Int{b1, b2, b0, b0, b0, b0})
assert.Nil(t, err)
h2, err := Hash([]*big.Int{big.NewInt(5), big.NewInt(6), big.NewInt(7), big.NewInt(8), big.NewInt(9),
big.NewInt(0), big.NewInt(1), big.NewInt(2), big.NewInt(3), big.NewInt(4)})
assert.Nil(t, err)
assert.NotEqual(t, h1, h2)
_, err = Hash([]*big.Int{b1, b2, b0, b0, b0, b0, b0})
assert.NotNil(t, err)
assert.Equal(t, "invalid inputs length 7, max 7", err.Error())
_, err = Hash([]*big.Int{b1, b2, b0, b0, b0, b0, b0, b0})
assert.NotNil(t, err)
assert.Equal(t, "invalid inputs length 8, max 7", err.Error())
}
func TestPoseidonBrokenPadding(t *testing.T) {
h1, err := Hash([]*big.Int{big.NewInt(1)})
assert.Nil(t, err)
h2, err := Hash([]*big.Int{big.NewInt(1), big.NewInt(0)})
assert.Nil(t, err)
assert.NotEqual(t, h1, h2)
}
func BenchmarkPoseidonHash(b *testing.B) {
b0 := big.NewInt(0)
b1 := utils.NewIntFromString("12242166908188651009877250812424843524687801523336557272219921456462821518061") //nolint:lll
b2 := utils.NewIntFromString("12242166908188651009877250812424843524687801523336557272219921456462821518061") //nolint:lll
func BenchmarkPoseidon(b *testing.B) {
b12 := big.NewInt(int64(12))
b45 := big.NewInt(int64(45))
b78 := big.NewInt(int64(78))
b41 := big.NewInt(int64(41))
bigArray4 := []*big.Int{b12, b45, b78, b41}
bigArray4 := []*big.Int{b1, b2, b0, b0, b0, b0}
for i := 0; i < b.N; i++ {
Hash(bigArray4)
Hash(bigArray4) //nolint:errcheck,gosec
}
}

View File

@@ -0,0 +1,11 @@
package utils
import (
"fmt"
"math/bits"
"testing"
)
func TestShowArchBits(t *testing.T) {
fmt.Printf("Architecture is %v bits\n", bits.UintSize)
}

View File

@@ -6,6 +6,9 @@ import (
"fmt"
"math/big"
"strings"
"github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/ff"
)
// NewIntFromString creates a new big.Int from a decimal integer encoded as a
@@ -63,9 +66,7 @@ func HexEncode(bs []byte) string {
// HexDecode decodes a hex string into an array of bytes.
func HexDecode(h string) ([]byte, error) {
if strings.HasPrefix(h, "0x") {
h = h[2:]
}
h = strings.TrimPrefix(h, "0x")
return hex.DecodeString(h)
}
@@ -76,7 +77,7 @@ func HexDecodeInto(dst []byte, h []byte) error {
h = h[2:]
}
if len(h)/2 != len(dst) {
return fmt.Errorf("expected %v bytes in hex string, got %v", len(dst), len(h)/2)
return fmt.Errorf("expected %v bytes in hex string, got %v", len(dst), len(h)/2) //nolint:gomnd
}
n, err := hex.Decode(dst, h)
if err != nil {
@@ -87,20 +88,38 @@ func HexDecodeInto(dst []byte, h []byte) error {
return nil
}
// CheckBigIntInField checks if given big.Int fits in a Field Q element
func CheckBigIntInField(a *big.Int, q *big.Int) bool {
if a.Cmp(q) != -1 {
// CheckBigIntInField checks if given *big.Int fits in a Field Q element
func CheckBigIntInField(a *big.Int) bool {
return a.Cmp(constants.Q) == -1
}
// CheckBigIntArrayInField checks if given *big.Int fits in a Field Q element
func CheckBigIntArrayInField(arr []*big.Int) bool {
for _, a := range arr {
if !CheckBigIntInField(a) {
return false
}
}
return true
}
// CheckBigIntArrayInField checks if given big.Int fits in a Field Q element
func CheckBigIntArrayInField(arr []*big.Int, q *big.Int) bool {
for _, a := range arr {
if !CheckBigIntInField(a, q) {
return false
// BigIntArrayToElementArray converts an array of *big.Int into an array of *ff.Element
func BigIntArrayToElementArray(bi []*big.Int) []*ff.Element {
var o []*ff.Element
for i := range bi {
o = append(o, ff.NewElement().SetBigInt(bi[i]))
}
}
return true
return o
}
// ElementArrayToBigIntArray converts an array of *ff.Element into an array of *big.Int
func ElementArrayToBigIntArray(e []*ff.Element) []*big.Int {
var o []*big.Int
for i := range e {
ei := e[i]
bi := big.NewInt(0)
ei.ToBigIntRegular(bi)
o = append(o, bi)
}
return o
}