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