arnaucube
arnaucube/go-iden3-cryptoPublic Self-hosted repository
go-iden3-crypto/poseidon/poseidon.go
poseidon/poseidon.goRaw
  1package poseidon
  2
  3import (
  4	"errors"
  5	"math/big"
  6	"strconv"
  7
  8	"github.com/iden3/go-iden3-crypto/constants"
  9	"github.com/iden3/go-iden3-crypto/ff"
 10	"github.com/iden3/go-iden3-crypto/utils"
 11	"golang.org/x/crypto/blake2b"
 12)
 13
 14const SEED = "poseidon"
 15const NROUNDSF = 8
 16const NROUNDSP = 57
 17const T = 6
 18
 19var constC []*ff.Element
 20var constM [T][T]*ff.Element
 21
 22func Zero() *ff.Element {
 23	return ff.NewElement().SetZero()
 24}
 25
 26func modQ(v *big.Int) {
 27	v.Mod(v, constants.Q)
 28}
 29
 30func init() {
 31	constC = getPseudoRandom(SEED+"_constants", NROUNDSF+NROUNDSP)
 32	constM = getMDS()
 33}
 34
 35func getPseudoRandom(seed string, n int) []*ff.Element {
 36	res := make([]*ff.Element, n)
 37	hash := blake2b.Sum256([]byte(seed))
 38	for i := 0; i < n; i++ {
 39		hashBigInt := big.NewInt(int64(0))
 40		res[i] = ff.NewElement().SetBigInt(utils.SetBigIntFromLEBytes(hashBigInt, hash[:]))
 41		hash = blake2b.Sum256(hash[:])
 42	}
 43	return res
 44}
 45
 46func nonceToString(n int) string {
 47	r := strconv.Itoa(n)
 48	for len(r) < 4 {
 49		r = "0" + r
 50	}
 51	return r
 52}
 53
 54// https://eprint.iacr.org/2019/458.pdf pag.8
 55func getMDS() [T][T]*ff.Element {
 56	nonce := 0
 57	cauchyMatrix := getPseudoRandom(SEED+"_matrix_"+nonceToString(nonce), T*2)
 58	for !checkAllDifferent(cauchyMatrix) {
 59		nonce += 1
 60		cauchyMatrix = getPseudoRandom(SEED+"_matrix_"+nonceToString(nonce), T*2)
 61	}
 62	var m [T][T]*ff.Element
 63	for i := 0; i < T; i++ {
 64		for j := 0; j < T; j++ {
 65			m[i][j] = ff.NewElement().Sub(cauchyMatrix[i], cauchyMatrix[T+j])
 66			m[i][j].Inverse(m[i][j])
 67		}
 68	}
 69	return m
 70}
 71
 72func checkAllDifferent(v []*ff.Element) bool {
 73	for i := 0; i < len(v); i++ {
 74		if v[i].Equal(ff.NewElement().SetZero()) {
 75			return false
 76		}
 77		for j := i + 1; j < len(v); j++ {
 78			if v[i].Equal(v[j]) {
 79				return false
 80			}
 81		}
 82	}
 83	return true
 84}
 85
 86// ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf
 87func ark(state [T]*ff.Element, c *ff.Element) {
 88	for i := 0; i < T; i++ {
 89		state[i].Add(state[i], c)
 90	}
 91}
 92
 93// cubic performs x^5 mod p
 94// https://eprint.iacr.org/2019/458.pdf page 8
 95// var five = big.NewInt(5)
 96
 97func cubic(a *ff.Element) {
 98	a.Exp(*a, 5)
 99}
100
101// sbox https://eprint.iacr.org/2019/458.pdf page 6
102func sbox(state [T]*ff.Element, i int) {
103	if (i < NROUNDSF/2) || (i >= NROUNDSF/2+NROUNDSP) {
104		for j := 0; j < T; j++ {
105			cubic(state[j])
106		}
107	} else {
108		cubic(state[0])
109	}
110}
111
112// mix returns [[matrix]] * [vector]
113func mix(state [T]*ff.Element, newState [T]*ff.Element, m [T][T]*ff.Element) {
114	mul := Zero()
115	for i := 0; i < T; i++ {
116		newState[i].SetUint64(0)
117		for j := 0; j < T; j++ {
118			mul.Mul(m[i][j], state[j])
119			newState[i].Add(newState[i], mul)
120		}
121	}
122}
123
124// PoseidonHash computes the Poseidon hash for the given inputs
125func PoseidonHash(inpBI [T]*big.Int) (*big.Int, error) {
126	if !utils.CheckBigIntArrayInField(inpBI[:]) {
127		return nil, errors.New("inputs values not inside Finite Field")
128	}
129	inp := utils.BigIntArrayToElementArray(inpBI[:])
130	state := [T]*ff.Element{}
131	for i := 0; i < T; i++ {
132		state[i] = ff.NewElement().Set(inp[i])
133	}
134
135	// ARK --> SBox --> M, https://eprint.iacr.org/2019/458.pdf pag.5
136	var newState [T]*ff.Element
137	for i := 0; i < T; i++ {
138		newState[i] = Zero()
139	}
140	for i := 0; i < NROUNDSF+NROUNDSP; i++ {
141		ark(state, constC[i])
142		sbox(state, i)
143		mix(state, newState, constM)
144		state, newState = newState, state
145	}
146	rE := state[0]
147	r := big.NewInt(0)
148	rE.ToBigIntRegular(r)
149	return r, nil
150}
151
152// Hash performs the Poseidon hash over a ff.Element array
153// in chunks of 5 elements
154func Hash(arr []*big.Int) (*big.Int, error) {
155	r := big.NewInt(int64(1))
156	for i := 0; i < len(arr); i = i + T - 1 {
157		var toHash [T]*big.Int
158		j := 0
159		for ; j < T-1; j++ {
160			if i+j >= len(arr) {
161				break
162			}
163			toHash[j] = arr[i+j]
164		}
165		toHash[j] = r
166		j++
167		for ; j < T; j++ {
168			toHash[j] = big.NewInt(0)
169		}
170
171		ph, err := PoseidonHash(toHash)
172		if err != nil {
173			return nil, err
174		}
175		modQ(r.Add(r, ph))
176	}
177
178	return r, nil
179}
180
181// HashBytes hashes a msg byte slice by blocks of 31 bytes encoded as
182// little-endian
183func HashBytes(b []byte) (*big.Int, error) {
184	n := 31
185	bElems := make([]*big.Int, 0, len(b)/n+1)
186	for i := 0; i < len(b)/n; i++ {
187		v := big.NewInt(int64(0))
188		utils.SetBigIntFromLEBytes(v, b[n*i:n*(i+1)])
189		bElems = append(bElems, v)
190
191	}
192	if len(b)%n != 0 {
193		v := big.NewInt(int64(0))
194		utils.SetBigIntFromLEBytes(v, b[(len(b)/n)*n:])
195		bElems = append(bElems, v)
196	}
197	return Hash(bElems)
198}