Browse Source

Update Poseidon to new circomlib version & https://extgit.iaik.tugraz.at/krypto/hadeshash

feature/babyjubjub-optimization
arnaucube 4 years ago
parent
commit
e04ca5764a
5 changed files with 3584 additions and 211 deletions
  1. +2
    -2
      babyjub/eddsa.go
  2. +2
    -2
      babyjub/eddsa_test.go
  3. +3511
    -0
      poseidon/constants.go
  4. +40
    -127
      poseidon/poseidon.go
  5. +29
    -80
      poseidon/poseidon_test.go

+ 2
- 2
babyjub/eddsa.go

@ -253,7 +253,7 @@ func (k *PrivateKey) SignPoseidon(msg *big.Int) *Signature {
R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B
A := k.Public().Point() A := k.Public().Point()
hmInput := [poseidon.T]*big.Int{R8.X, R8.Y, A.X, A.Y, msg, big.NewInt(int64(0))}
hmInput := []*big.Int{R8.X, R8.Y, A.X, A.Y, msg, big.NewInt(int64(0))}
hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.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 { if err != nil {
panic(err) panic(err)
@ -270,7 +270,7 @@ func (k *PrivateKey) SignPoseidon(msg *big.Int) *Signature {
// VerifyPoseidon verifies the signature of a message encoded as a big.Int in Zq // 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. // using blake-512 hash for buffer hashing and Poseidon for big.Int hashing.
func (p *PublicKey) VerifyPoseidon(msg *big.Int, sig *Signature) bool { 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))}
hmInput := []*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg, big.NewInt(int64(0))}
hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.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 { if err != nil {
panic(err) panic(err)

+ 2
- 2
babyjub/eddsa_test.go

@ -96,7 +96,7 @@ func TestSignVerifyPoseidon(t *testing.T) {
"15383486972088797283337779941324724402501462225528836549661220478783371668959", "15383486972088797283337779941324724402501462225528836549661220478783371668959",
sig.R8.Y.String()) sig.R8.Y.String())
assert.Equal(t, assert.Equal(t,
"248298168863866362217836334079793350221620631973732197668910946177382043688",
"1662463587877312619203503803508234533733252768380479199263194005796068211378",
sig.S.String()) sig.S.String())
ok := pk.VerifyPoseidon(msg, sig) ok := pk.VerifyPoseidon(msg, sig)
@ -108,7 +108,7 @@ func TestSignVerifyPoseidon(t *testing.T) {
assert.Equal(t, ""+ assert.Equal(t, ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+ "dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"28506bce274aa1b3f7e7c2fd7e4fe09bff8f9aa37a42def7994e98f322888c00",
"b23a1f04909fc088dec7e4835d85a326f7c0d0b2a3d0232d84448ca7c9ebac03",
hex.EncodeToString(sigBuf[:])) hex.EncodeToString(sigBuf[:]))
ok = pk.VerifyPoseidon(msg, sig2) ok = pk.VerifyPoseidon(msg, sig2)

+ 3511
- 0
poseidon/constants.go
File diff suppressed because it is too large
View File


+ 40
- 127
poseidon/poseidon.go

@ -2,177 +2,90 @@ package poseidon
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"strconv"
"github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/ff" "github.com/iden3/go-iden3-crypto/ff"
"github.com/iden3/go-iden3-crypto/utils" "github.com/iden3/go-iden3-crypto/utils"
"golang.org/x/crypto/blake2b"
) )
const SEED = "poseidon"
const NROUNDSF = 8 const NROUNDSF = 8
const NROUNDSP = 57
const T = 6
var constC []*ff.Element
var constM [T][T]*ff.Element
var NROUNDSP = []int{56, 57, 56, 60, 60, 63, 64, 63}
func Zero() *ff.Element {
func zero() *ff.Element {
return ff.NewElement() return ff.NewElement()
} }
func modQ(v *big.Int) {
v.Mod(v, constants.Q)
}
func init() {
constC = getPseudoRandom(SEED+"_constants", NROUNDSF+NROUNDSP)
constM = getMDS()
}
func getPseudoRandom(seed string, n int) []*ff.Element {
res := make([]*ff.Element, n)
hash := blake2b.Sum256([]byte(seed))
for i := 0; i < n; i++ {
hashBigInt := big.NewInt(int64(0))
res[i] = ff.NewElement().SetBigInt(utils.SetBigIntFromLEBytes(hashBigInt, hash[:]))
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]*ff.Element {
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]*ff.Element
for i := 0; i < T; i++ {
for j := 0; j < T; j++ {
m[i][j] = ff.NewElement().Sub(cauchyMatrix[i], cauchyMatrix[T+j])
m[i][j].Inverse(m[i][j])
}
}
return m
}
func checkAllDifferent(v []*ff.Element) bool {
for i := 0; i < len(v); i++ {
if v[i].Equal(ff.NewElement()) {
return false
}
for j := i + 1; j < len(v); j++ {
if v[i].Equal(v[j]) {
return false
}
}
}
return true
}
// ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf // ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf
func ark(state [T]*ff.Element, c *ff.Element) {
for i := 0; i < T; i++ {
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 // https://eprint.iacr.org/2019/458.pdf page 8
func cubic(a *ff.Element) {
func exp5(a *ff.Element) {
a.Exp(*a, 5) a.Exp(*a, 5)
} }
// sbox https://eprint.iacr.org/2019/458.pdf page 6 // sbox https://eprint.iacr.org/2019/458.pdf page 6
func sbox(state [T]*ff.Element, 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 { } else {
cubic(state[0])
exp5(state[0])
} }
} }
// mix returns [[matrix]] * [vector] // mix returns [[matrix]] * [vector]
func mix(state [T]*ff.Element, newState [T]*ff.Element, m [T][T]*ff.Element) {
mul := Zero()
for i := 0; i < T; i++ {
func mix(state []*ff.Element, newState []*ff.Element, m [][]*ff.Element) {
mul := zero()
for i := 0; i < len(state); i++ {
newState[i].SetUint64(0) newState[i].SetUint64(0)
for j := 0; j < T; j++ {
mul.Mul(m[i][j], state[j])
for j := 0; j < len(state); j++ {
mul.Mul(m[j][i], state[j])
newState[i].Add(newState[i], mul) newState[i].Add(newState[i], mul)
} }
} }
} }
// Hash computes the Poseidon hash for the given inputs // Hash computes the Poseidon hash for the given inputs
func Hash(inpBI [T]*big.Int) (*big.Int, error) {
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[:]) { if !utils.CheckBigIntArrayInField(inpBI[:]) {
return nil, errors.New("inputs values not inside Finite Field") return nil, errors.New("inputs values not inside Finite Field")
} }
inp := utils.BigIntArrayToElementArray(inpBI[:]) inp := utils.BigIntArrayToElementArray(inpBI[:])
state := [T]*ff.Element{}
for i := 0; i < T; i++ {
state[i] = ff.NewElement().Set(inp[i])
state := make([]*ff.Element, t)
copy(state[:], inp[:])
state[len(state)-1] = zero()
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 // ARK --> SBox --> M, https://eprint.iacr.org/2019/458.pdf pag.5
var newState [T]*ff.Element
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)
state, newState = newState, state
for i := 0; i < nRoundsF+nRoundsP; i++ {
ark(state, c.c[t-2], i*t)
sbox(nRoundsF, nRoundsP, state, i)
if i < nRoundsF+nRoundsP-1 {
mix(state, newState, c.m[t-2])
state, newState = newState, state
}
} }
rE := state[0] rE := state[0]
r := big.NewInt(0) r := big.NewInt(0)
rE.ToBigIntRegular(r) rE.ToBigIntRegular(r)
return r, nil return r, nil
} }
// HashSlice performs the Poseidon hash over a ff.Element array
// in chunks of 5 elements
func HashSlice(arr []*big.Int) (*big.Int, error) {
r := big.NewInt(int64(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] = big.NewInt(0)
}
ph, err := Hash(toHash)
if err != nil {
return nil, err
}
modQ(r.Add(r, ph))
}
return r, nil
}

+ 29
- 80
poseidon/poseidon_test.go

@ -19,104 +19,53 @@ func TestPoseidonHash(t *testing.T) {
b0 := big.NewInt(0) b0 := big.NewInt(0)
b1 := big.NewInt(1) b1 := big.NewInt(1)
b2 := big.NewInt(2) b2 := big.NewInt(2)
h, err := Hash([T]*big.Int{b1, b2, b0, b0, b0, b0})
h, err := Hash([]*big.Int{b1})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "12242166908188651009877250812424843524687801523336557272219921456462821518061", h.String())
assert.Equal(t, "11043376183861534927536506085090418075369306574649619885724436265926427398571", h.String())
b3 := big.NewInt(3)
b4 := big.NewInt(4)
h, err = Hash([T]*big.Int{b3, b4, b0, b0, b0, b0})
h, err = Hash([]*big.Int{b1, b2})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "17185195740979599334254027721507328033796809509313949281114643312710535000993", h.String())
}
assert.Equal(t, "17117985411748610629288516079940078114952304104811071254131751175361957805920", h.String())
func TestPoseidonHashArbitraryLen(t *testing.T) {
b1 := big.NewInt(1)
b2 := big.NewInt(2)
h, err := HashSlice([]*big.Int{b1, b2})
h, err = Hash([]*big.Int{b1, b2, b0, b0, b0})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "4932297968297298434239270129193057052722409868268166443802652458940273154855", h.String())
assert.Equal(t, "3975478831357328722254985704342968745327876719981393787143845259590563829094", h.String())
h, err = Hash([]*big.Int{b1, b2, b0, b0, b0, b0})
assert.Nil(t, err)
assert.Equal(t, "19772360636270345724087386688434825760738403416279047262510528378903625000110", h.String())
b3 := big.NewInt(3) b3 := big.NewInt(3)
b4 := big.NewInt(4) b4 := big.NewInt(4)
h, err = HashSlice([]*big.Int{b3, b4})
h, err = Hash([]*big.Int{b3, b4, b0, b0, b0})
assert.Nil(t, err)
assert.Equal(t, "3181200837746671699652342497997860344148947482942465819251904554707352676086", h.String())
h, err = Hash([]*big.Int{b3, b4, b0, b0, b0, b0})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "4635491972858758537477743930622086396911540895966845494943021655521913507504", h.String())
assert.Equal(t, "8386348873272147968934270337233829407378789978142456170950021426339096575008", h.String())
b5 := big.NewInt(5) b5 := big.NewInt(5)
b6 := big.NewInt(6) b6 := big.NewInt(6)
b7 := big.NewInt(7)
b8 := big.NewInt(8)
b9 := big.NewInt(9)
b10 := big.NewInt(10)
b11 := big.NewInt(11)
b12 := big.NewInt(12)
h, err = HashSlice([]*big.Int{b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12})
assert.Nil(t, err)
assert.Equal(t, "15278801138972282646981503374384603641625274360649669926363020545395022098027", 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 := HashSlice(msgElems)
assert.Nil(t, err)
assert.Equal(t, "16019700159595764790637132363672701294192939959594423814006267756172551741065", hmsg.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 := HashSlice(msg2Elems)
h, err = Hash([]*big.Int{b1, b2, b3, b4, b5, b6})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "2978613163687734485261639854325792381691890647104372645321246092227111432722", hmsg2.String())
assert.Equal(t, "5202465217520500374834597824465244016759843635092906214933648999760272616044", h.String())
} }
func TestPoseidonHashArbitraryLenBrokenChunks(t *testing.T) {
h1, err := HashSlice([]*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)})
assert.Nil(t, err)
h2, err := HashSlice([]*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)
}
func TestErrorInputs(t *testing.T) {
b0 := big.NewInt(0)
b1 := big.NewInt(1)
b2 := big.NewInt(2)
func TestPoseidonHashArbitraryLenBrokenPadding(t *testing.T) {
h1, err := HashSlice([]*big.Int{big.NewInt(int64(1))})
assert.Nil(t, err)
h2, err := HashSlice([]*big.Int{big.NewInt(int64(1)), big.NewInt(int64(0))})
_, err := Hash([]*big.Int{b1, b2, b0, b0, b0, b0})
assert.Nil(t, err) assert.Nil(t, err)
assert.NotEqual(t, h1, h2)
}
func BenchmarkPoseidonHashSmallValues(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}
_, 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())
for i := 0; i < b.N; i++ {
HashSlice(bigArray4) //nolint:errcheck
}
_, 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 BenchmarkPoseidonHash(b *testing.B) { func BenchmarkPoseidonHash(b *testing.B) {
@ -124,7 +73,7 @@ func BenchmarkPoseidonHash(b *testing.B) {
b1 := utils.NewIntFromString("12242166908188651009877250812424843524687801523336557272219921456462821518061") b1 := utils.NewIntFromString("12242166908188651009877250812424843524687801523336557272219921456462821518061")
b2 := utils.NewIntFromString("12242166908188651009877250812424843524687801523336557272219921456462821518061") b2 := utils.NewIntFromString("12242166908188651009877250812424843524687801523336557272219921456462821518061")
bigArray4 := [T]*big.Int{b1, b2, b0, b0, b0, b0}
bigArray4 := []*big.Int{b1, b2, b0, b0, b0, b0}
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Hash(bigArray4) //nolint:errcheck Hash(bigArray4) //nolint:errcheck

Loading…
Cancel
Save