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