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