Browse Source

Poseidon Sponge Hash with different frame sizes (#52)

* Poseidon Sponge Hash with different frame sizes
* Update deps. Bump go version
* Update & fix linter.
* Refactor a bit.
* Reduce gc pressure
fix/bbjj-err
Oleksandr Brezhniev 1 year ago
committed by GitHub
parent
commit
e5cf066b8b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 353 additions and 163 deletions
  1. +12
    -10
      .github/workflows/lint.yml
  2. +1
    -1
      .github/workflows/test.yml
  3. +75
    -15
      .golangci.yml
  4. +3
    -3
      babyjub/babyjub.go
  5. +6
    -5
      babyjub/eddsa.go
  6. +1
    -1
      babyjub/helpers.go
  7. +6
    -18
      constants/constants.go
  8. +10
    -7
      go.mod
  9. +14
    -16
      go.sum
  10. +8
    -4
      goldenposeidon/constants.go
  11. +3
    -1
      goldenposeidon/poseidon.go
  12. +2
    -25
      goldenposeidon/poseidon_test.go
  13. +21
    -0
      keccak256/keccac256_test.go
  14. +8
    -8
      mimc7/mimc7.go
  15. +7
    -19
      mimc7/mimc7_test.go
  16. +1
    -1
      poseidon/constants.go
  17. +81
    -15
      poseidon/poseidon.go
  18. +88
    -8
      poseidon/poseidon_test.go
  19. +6
    -6
      utils/utils.go

+ 12
- 10
.github/workflows/lint.yml

@ -1,16 +1,18 @@
name: Lint
on: [ push, pull_request ]
on:
push:
branches:
- main
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v1
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: 1.16.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
go-version: 1.20.x
- uses: golangci/golangci-lint-action@v3
with:
version: v1.51.1

+ 1
- 1
.github/workflows/test.yml

@ -4,7 +4,7 @@ jobs:
test:
strategy:
matrix:
go-version: [ 1.16.x, 1.17.x ]
go-version: [ 1.18.x, 1.19.x, 1.20.x ]
goarch: [ "amd64", "386" ]
runs-on: ubuntu-latest
steps:

+ 75
- 15
.golangci.yml

@ -1,17 +1,77 @@
issues:
max-same-issues: 0
exclude-use-default: false
service:
golangci-lint-version: 1.51.x
run:
timeout: 2m
skip-dirs:
- vendor
linters-settings:
govet:
check-shadowing: true
revive:
min-confidence: 0.1
rules:
- name: package-comments
disabled: true
maligned:
suggest-new: true
goconst:
min-len: 2
min-occurrences: 2
misspell:
locale: US
lll:
line-length: 140
gocritic:
enabled-tags:
- performance
- style
- experimental
disabled-checks:
- hugeParam
linters:
enable:
- whitespace
enable:
- bodyclose
- megacheck
- revive
- govet
- unconvert
- megacheck
- gas
- gocyclo
- dupl
- misspell
- unparam
- typecheck
- ineffassign
- stylecheck
- exportloopref
- nakedret
- gosimple
- prealloc
- unused
## format - fill free to fix
# - errcheck
# - gofmt
# - goimports
fast: false
disable-all: true
issues:
exclude-rules:
# - Fix and remove
- text: "at least one file in a package should have a package comment"
linters:
- stylecheck
# - Fix and remove
- text: "should have a package comment, unless it's in another file for this package"
linters:
- revive
- path: _test\.go
linters:
- gosec
- gci
- misspell
- gomnd
- gofmt
- goimports
- lll
- golint
linters-settings:
lll:
line-length: 100
- dupl
exclude-use-default: false

+ 3
- 3
babyjub/babyjub.go

@ -87,7 +87,7 @@ func (p *PointProjective) Affine() *Point {
// Add computes the addition of two points in projective coordinates
// representation
func (p *PointProjective) Add(q *PointProjective, o *PointProjective) *PointProjective {
func (p *PointProjective) Add(q, 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)
@ -209,7 +209,7 @@ func PointCoordSign(c *big.Int) bool {
func PackSignY(sign bool, y *big.Int) [32]byte {
leBuf := utils.BigIntLEBytes(y)
if sign {
leBuf[31] = leBuf[31] | 0x80 //nolint:gomnd
leBuf[31] |= 0x80 //nolint:gomnd
}
return leBuf
}
@ -225,7 +225,7 @@ func UnpackSignY(leBuf [32]byte) (bool, *big.Int) {
y := big.NewInt(0)
if (leBuf[31] & 0x80) != 0x00 { //nolint:gomnd
sign = true
leBuf[31] = leBuf[31] & 0x7F //nolint:gomnd
leBuf[31] &= 0x7F //nolint:gomnd
}
utils.SetBigIntFromLEBytes(y, leBuf[:])
return sign, y

+ 6
- 5
babyjub/eddsa.go

@ -1,4 +1,5 @@
// Package babyjub eddsa implements the EdDSA over the BabyJubJub curve
//
//nolint:gomnd
package babyjub
@ -16,9 +17,9 @@ import (
// pruneBuffer prunes the buffer during key generation according to RFC 8032.
// https://tools.ietf.org/html/rfc8032#page-13
func pruneBuffer(buf *[32]byte) *[32]byte {
buf[0] = buf[0] & 0xF8
buf[31] = buf[31] & 0x7F
buf[31] = buf[31] | 0x40
buf[0] &= 0xF8
buf[31] &= 0x7F
buf[31] |= 0x40
return buf
}
@ -210,7 +211,7 @@ func (sComp *SignatureComp) Scan(src interface{}) error {
if len(srcB) != 64 {
return fmt.Errorf("can't scan []byte of len %d into Signature, want %d", len(srcB), 64)
}
copy(sComp[:], srcB[:])
copy(sComp[:], srcB)
return nil
}
@ -229,7 +230,7 @@ func (s *Signature) Scan(src interface{}) error {
return fmt.Errorf("can't scan []byte of len %d into Signature, want %d", len(srcB), 64)
}
buf := [64]byte{}
copy(buf[:], srcB[:])
copy(buf[:], srcB)
_, err := s.Decompress(buf)
return err
}

+ 1
- 1
babyjub/helpers.go

@ -12,7 +12,7 @@ import (
// the original blake from the SHA3 competition and not the new blake2 version.
func Blake512(m []byte) []byte {
h := blake512.New()
_, err := h.Write(m[:])
_, err := h.Write(m)
if err != nil {
panic(err)
}

+ 6
- 18
constants/constants.go

@ -1,31 +1,19 @@
package constants
import (
"fmt"
"math/big"
)
const qString = "21888242871839275222246405745257275088548364400416034343698204186575808495617"
// Q is the order of the integer field (Zq) that fits inside the SNARK.
var Q *big.Int
var Q, _ = new(big.Int).SetString(qString, 10)
// Zero is 0.
var Zero *big.Int
var Zero = big.NewInt(0)
// One is 1.
var One *big.Int
var One = big.NewInt(1)
// MinusOne is -1.
var MinusOne *big.Int
func init() {
Zero = big.NewInt(0)
One = big.NewInt(1)
MinusOne = big.NewInt(-1)
qString := "21888242871839275222246405745257275088548364400416034343698204186575808495617"
var ok bool
Q, ok = new(big.Int).SetString(qString, 10) //nolint:gomnd
if !ok {
panic(fmt.Sprintf("Bad base 10 string %s", qString))
}
}
var MinusOne = big.NewInt(-1)

+ 10
- 7
go.mod

@ -1,14 +1,17 @@
module github.com/iden3/go-iden3-crypto
go 1.16
go 1.18
require (
github.com/dchest/blake512 v1.0.0
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/leanovate/gopter v0.2.9 // indirect
github.com/leanovate/gopter v0.2.9
github.com/stretchr/testify v1.8.2
golang.org/x/crypto v0.7.0
golang.org/x/sys v0.6.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

+ 14
- 16
go.sum

@ -1,5 +1,6 @@
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/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
@ -7,21 +8,18 @@ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 8
- 4
goldenposeidon/constants.go

@ -3,10 +3,14 @@ package poseidon
import "github.com/iden3/go-iden3-crypto/ffg"
const (
NROUNDSF = 8 //nolint:golint
NROUNDSP = 22 //nolint:golint
CAPLEN = 4 //nolint:golint
mLen = 12
// NROUNDSF const from original paper
NROUNDSF = 8
// NROUNDSP const from original paper
NROUNDSP = 22
// CAPLEN const
CAPLEN = 4
// mLen const
mLen = 12
)
var (

+ 3
- 1
goldenposeidon/poseidon.go

@ -10,9 +10,11 @@ func zero() *ffg.Element {
return ffg.NewElement()
}
var big7 = big.NewInt(7)
// exp7 performs x^7 mod p
func exp7(a *ffg.Element) {
a.Exp(*a, big.NewInt(7)) //nolint:gomnd
a.Exp(*a, big7)
}
// exp7state perform exp7 for whole state

+ 2
- 25
goldenposeidon/poseidon_test.go

@ -1,10 +1,8 @@
package poseidon
import (
"math/big"
"testing"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/stretchr/testify/assert"
)
@ -99,32 +97,11 @@ func TestPoseidonHashCompare(t *testing.T) {
)
}
func BenchmarkPoseidonHash12Inputs(b *testing.B) {
bigArray12 := []*big.Int{
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),
big.NewInt(10),
big.NewInt(11),
big.NewInt(12),
}
for i := 0; i < b.N; i++ {
poseidon.Hash(bigArray12) //nolint:errcheck,gosec
}
}
func BenchmarkNeptuneHash(b *testing.B) {
inp := [NROUNDSF]uint64{1, 2, 3, 4, 5, 6, 7, 8}
cap := [CAPLEN]uint64{10, 11, 12, 13}
_cap := [CAPLEN]uint64{10, 11, 12, 13}
for i := 0; i < b.N; i++ {
Hash(inp, cap) //nolint:errcheck,gosec
_, _ = Hash(inp, _cap)
}
}

+ 21
- 0
keccak256/keccac256_test.go

@ -0,0 +1,21 @@
package keccak256
import (
"encoding/hex"
"math/big"
"testing"
"github.com/stretchr/testify/assert"
)
func TestKeccak256(t *testing.T) {
const SEED = "mimc"
res := Hash([]byte(SEED))
assert.Equal(t,
"b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e",
hex.EncodeToString(res))
c := new(big.Int).SetBytes(Hash([]byte(SEED)))
assert.Equal(t,
"82724731331859054037315113496710413141112897654334566532528783843265082629790",
c.String())
}

+ 8
- 8
mimc7/mimc7.go

@ -23,22 +23,22 @@ type constantsData struct {
}
func generateConstantsData() constantsData {
var constants constantsData
var consts constantsData
constants.seedHash = new(big.Int).SetBytes(keccak256.Hash([]byte(SEED)))
consts.seedHash = new(big.Int).SetBytes(keccak256.Hash([]byte(SEED)))
c := new(big.Int).SetBytes(keccak256.Hash([]byte(SEED + "_iv")))
constants.iv = new(big.Int).Mod(c, _constants.Q)
consts.iv = new(big.Int).Mod(c, _constants.Q)
constants.nRounds = 91
cts := getConstants(SEED, constants.nRounds)
constants.cts = cts
return constants
consts.nRounds = 91
cts := getConstants(SEED, consts.nRounds)
consts.cts = cts
return consts
}
func getConstants(seed string, nRounds int) []*ff.Element {
cts := make([]*ff.Element, nRounds)
cts[0] = ff.NewElement()
c := new(big.Int).SetBytes(keccak256.Hash([]byte(SEED)))
c := new(big.Int).SetBytes(keccak256.Hash([]byte(seed)))
for i := 1; i < nRounds; i++ {
c = new(big.Int).SetBytes(keccak256.Hash(c.Bytes()))

+ 7
- 19
mimc7/mimc7_test.go

@ -5,21 +5,9 @@ import (
"math/big"
"testing"
"github.com/iden3/go-iden3-crypto/keccak256"
"github.com/stretchr/testify/assert"
)
func TestKeccak256(t *testing.T) {
res := keccak256.Hash([]byte(SEED))
assert.Equal(t,
"b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e",
hex.EncodeToString(res))
c := new(big.Int).SetBytes(keccak256.Hash([]byte(SEED)))
assert.Equal(t,
"82724731331859054037315113496710413141112897654334566532528783843265082629790",
c.String())
}
func TestMIMC7Generic(t *testing.T) {
b1 := big.NewInt(int64(1))
b2 := big.NewInt(int64(2))
@ -36,7 +24,7 @@ func TestMIMC7Generic(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t,
"6464402164086696096195815557694604139393321133243036833927490113253119343397",
(*big.Int)(hg).String())
hg.String())
}
func TestMIMC7(t *testing.T) {
@ -51,7 +39,7 @@ 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()),
assert.Equal(t, "0x"+hex.EncodeToString(h1.Bytes()),
"0x237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd")
// h2a, hash of 2 elements
@ -60,7 +48,7 @@ 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()),
assert.Equal(t, "0x"+hex.EncodeToString(h2a.Bytes()),
"0x067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70")
// h2b, hash of 2 elements
@ -68,13 +56,13 @@ func TestMIMC7(t *testing.T) {
mh2b := MIMC7Hash(b12, b45)
assert.Nil(t, err)
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(mh2b).Bytes()),
assert.Equal(t, "0x"+hex.EncodeToString(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()),
assert.Equal(t, "0x"+hex.EncodeToString(h2b.Bytes()),
"0x15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879")
// h4, hash of 4 elements
@ -83,7 +71,7 @@ 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()),
assert.Equal(t, "0x"+hex.EncodeToString(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.") //nolint:lll
@ -101,6 +89,6 @@ func BenchmarkMIMC7(b *testing.B) {
bigArray4 := []*big.Int{b12, b45, b78, b41}
for i := 0; i < b.N; i++ {
Hash(bigArray4, nil) //nolint:errcheck,gosec
_, _ = Hash(bigArray4, nil)
}
}

+ 1
- 1
poseidon/constants.go

@ -96,7 +96,7 @@ func init() {
}
}
//nolint:lll
//nolint:lll,dupl // long lines, duplicated parts
var cs = constantsStr{
C: [][]string{{
"9c46e9ec68e9bd4fe1faaba294cba38a71aa177534cdd1b6c7dc0dbd0abd7a7",

+ 81
- 15
poseidon/poseidon.go

@ -9,9 +9,11 @@ import (
"github.com/iden3/go-iden3-crypto/utils"
)
const NROUNDSF = 8 //nolint:golint
// NROUNDSF constant from Poseidon paper
const NROUNDSF = 8
var NROUNDSP = []int{56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68} //nolint:golint
// NROUNDSP constant from Poseidon paper
var NROUNDSP = []int{56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68}
const spongeChunkSize = 31
const spongeInputs = 16
@ -20,10 +22,12 @@ func zero() *ff.Element {
return ff.NewElement()
}
var big5 = big.NewInt(5)
// exp5 performs x^5 mod p
// https://eprint.iacr.org/2019/458.pdf page 8
func exp5(a *ff.Element) {
a.Exp(*a, big.NewInt(5)) //nolint:gomnd
a.Exp(*a, big5)
}
// exp5state perform exp5 for whole state
@ -34,7 +38,7 @@ func exp5state(state []*ff.Element) {
}
// ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf
func ark(state []*ff.Element, c []*ff.Element, it int) {
func ark(state, c []*ff.Element, it int) {
for i := 0; i < len(state); i++ {
state[i].Add(state[i], c[it+i])
}
@ -61,12 +65,12 @@ func mix(state []*ff.Element, t int, m [][]*ff.Element) []*ff.Element {
func Hash(inpBI []*big.Int) (*big.Int, error) {
t := len(inpBI) + 1
if len(inpBI) == 0 || len(inpBI) > len(NROUNDSP) {
return nil, fmt.Errorf("invalid inputs length %d, max %d", len(inpBI), len(NROUNDSP)) //nolint:gomnd,lll
return nil, fmt.Errorf("invalid inputs length %d, max %d", len(inpBI), len(NROUNDSP))
}
if !utils.CheckBigIntArrayInField(inpBI[:]) {
if !utils.CheckBigIntArrayInField(inpBI) {
return nil, errors.New("inputs values not inside Finite Field")
}
inp := utils.BigIntArrayToElementArray(inpBI[:])
inp := utils.BigIntArrayToElementArray(inpBI)
nRoundsF := NROUNDSF
nRoundsP := NROUNDSP[t-2]
@ -77,7 +81,7 @@ func Hash(inpBI []*big.Int) (*big.Int, error) {
state := make([]*ff.Element, t)
state[0] = zero()
copy(state[1:], inp[:])
copy(state[1:], inp)
ark(state, C, 0)
@ -90,11 +94,12 @@ func Hash(inpBI []*big.Int) (*big.Int, error) {
ark(state, C, (nRoundsF/2)*t)
state = mix(state, t, P)
mul := zero()
for i := 0; i < nRoundsP; i++ {
exp5(state[0])
state[0].Add(state[0], C[(nRoundsF/2+1)*t+i])
mul := zero()
mul.SetZero()
newState0 := zero()
for j := 0; j < len(state); j++ {
mul.Mul(S[(t*2-1)*i+j], state[j])
@ -102,7 +107,7 @@ func Hash(inpBI []*big.Int) (*big.Int, error) {
}
for k := 1; k < t; k++ {
mul = zero()
mul.SetZero()
state[k] = state[k].Add(state[k], mul.Mul(state[0], S[(t*2-1)*i+t+k-1]))
}
state[0] = newState0
@ -124,9 +129,18 @@ func Hash(inpBI []*big.Int) (*big.Int, error) {
// HashBytes returns a sponge hash of a msg byte slice split into blocks of 31 bytes
func HashBytes(msg []byte) (*big.Int, error) {
return HashBytesX(msg, spongeInputs)
}
// HashBytesX returns a sponge hash of a msg byte slice split into blocks of 31 bytes
func HashBytesX(msg []byte, frameSize int) (*big.Int, error) {
if frameSize < 2 || frameSize > 16 {
return nil, errors.New("incorrect frame size")
}
// not used inputs default to zero
inputs := make([]*big.Int, spongeInputs)
for j := 0; j < spongeInputs; j++ {
inputs := make([]*big.Int, frameSize)
for j := 0; j < frameSize; j++ {
inputs[j] = new(big.Int)
}
dirty := false
@ -137,15 +151,15 @@ func HashBytes(msg []byte) (*big.Int, error) {
for i := 0; i < len(msg)/spongeChunkSize; i++ {
dirty = true
inputs[k].SetBytes(msg[spongeChunkSize*i : spongeChunkSize*(i+1)])
if k == spongeInputs-1 {
if k == frameSize-1 {
hash, err = Hash(inputs)
dirty = false
if err != nil {
return nil, err
}
inputs = make([]*big.Int, spongeInputs)
inputs = make([]*big.Int, frameSize)
inputs[0] = hash
for j := 1; j < spongeInputs; j++ {
for j := 1; j < frameSize; j++ {
inputs[j] = new(big.Int)
}
k = 1
@ -174,3 +188,55 @@ func HashBytes(msg []byte) (*big.Int, error) {
return hash, nil
}
// SpongeHash returns a sponge hash of inputs (using Poseidon with frame size of 16 inputs)
func SpongeHash(inputs []*big.Int) (*big.Int, error) {
return SpongeHashX(inputs, spongeInputs)
}
// SpongeHashX returns a sponge hash of inputs using Poseidon with configurable frame size
func SpongeHashX(inputs []*big.Int, frameSize int) (*big.Int, error) {
if frameSize < 2 || frameSize > 16 {
return nil, errors.New("incorrect frame size")
}
// not used frame default to zero
frame := make([]*big.Int, frameSize)
for j := 0; j < frameSize; j++ {
frame[j] = new(big.Int)
}
dirty := false
var hash *big.Int
var err error
k := 0
for i := 0; i < len(inputs); i++ {
dirty = true
frame[k] = inputs[i]
if k == frameSize-1 {
hash, err = Hash(frame)
dirty = false
if err != nil {
return nil, err
}
frame = make([]*big.Int, frameSize)
frame[0] = hash
for j := 1; j < frameSize; j++ {
frame[j] = new(big.Int)
}
k = 1
} else {
k++
}
}
if dirty {
// we haven't hashed something in the main sponge loop and need to do hash here
hash, err = Hash(frame)
if err != nil {
return nil, err
}
}
return hash, nil
}

+ 88
- 8
poseidon/poseidon_test.go
File diff suppressed because it is too large
View File


+ 6
- 6
utils/utils.go

@ -46,7 +46,7 @@ func SetBigIntFromLEBytes(v *big.Int, leBuf []byte) *big.Int {
return v.SetBytes(beBuf)
}
// Hex is a byte slice type that can be marshalled and unmarshaled in hex
// Hex is a byte slice type that can be marshaled and unmarshaled in hex
type Hex []byte
// MarshalText encodes buf as hex
@ -72,7 +72,7 @@ func HexDecode(h string) ([]byte, error) {
// HexDecodeInto decodes a hex string into an array of bytes (dst), verifying
// that the decoded array has the same length as dst.
func HexDecodeInto(dst []byte, h []byte) error {
func HexDecodeInto(dst, h []byte) error {
if bytes.HasPrefix(h, []byte("0x")) {
h = h[2:]
}
@ -105,21 +105,21 @@ func CheckBigIntArrayInField(arr []*big.Int) bool {
// BigIntArrayToElementArray converts an array of *big.Int into an array of *ff.Element
func BigIntArrayToElementArray(bi []*big.Int) []*ff.Element {
var o []*ff.Element
o := make([]*ff.Element, len(bi))
for i := range bi {
o = append(o, ff.NewElement().SetBigInt(bi[i]))
o[i] = ff.NewElement().SetBigInt(bi[i])
}
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
o := make([]*big.Int, len(e))
for i := range e {
ei := e[i]
bi := big.NewInt(0)
ei.ToBigIntRegular(bi)
o = append(o, bi)
o[i] = bi
}
return o
}

Loading…
Cancel
Save