mirror of
https://github.com/arnaucube/go-iden3-crypto.git
synced 2026-02-07 11:36:41 +01:00
add Poseidon
This commit is contained in:
184
poseidon/poseidon.go
Normal file
184
poseidon/poseidon.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package poseidon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/field"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
const SEED = "poseidon"
|
||||
const NROUNDSF = 8
|
||||
const NROUNDSP = 57
|
||||
const T = 6
|
||||
|
||||
var constants = generateConstantsData()
|
||||
|
||||
type constantsData struct {
|
||||
fqR field.Fq
|
||||
c []*big.Int
|
||||
m [][]*big.Int
|
||||
}
|
||||
|
||||
// checkBigIntInField checks if given big.Int fits in a Field R element
|
||||
func checkBigIntInField(a *big.Int, q *big.Int) bool {
|
||||
if a.Cmp(q) != -1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// checkBigIntArrayInField checks if given big.Int fits in a Field R element
|
||||
func checkBigIntArrayInField(arr []*big.Int, q *big.Int) bool {
|
||||
for _, a := range arr {
|
||||
if !checkBigIntInField(a, q) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func generateConstantsData() constantsData {
|
||||
var constants constantsData
|
||||
|
||||
r, ok := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
|
||||
if !ok {
|
||||
|
||||
}
|
||||
fqR := field.NewFq(r)
|
||||
constants.fqR = fqR
|
||||
constants.c = getPseudoRandom(fqR, SEED+"_constants", big.NewInt(int64(NROUNDSF+NROUNDSP)))
|
||||
constants.m = getMDS(fqR)
|
||||
|
||||
return constants
|
||||
}
|
||||
|
||||
func getPseudoRandom(fqR field.Fq, seed string, n *big.Int) []*big.Int {
|
||||
var res []*big.Int
|
||||
hash := blake2b.Sum256([]byte(seed))
|
||||
for big.NewInt(int64(len(res))).Cmp(n) == -1 { // res < n
|
||||
newN := fqR.Affine(leByteArrayToBigInt(fqR, hash[:]))
|
||||
res = append(res, newN)
|
||||
hash = blake2b.Sum256(hash[:])
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func leByteArrayToBigInt(fqR field.Fq, b []byte) *big.Int {
|
||||
res := fqR.Zero()
|
||||
for i := 0; i < len(b); i++ {
|
||||
n := big.NewInt(int64(b[i]))
|
||||
res = new(big.Int).Add(res, new(big.Int).Lsh(n, uint(i*8)))
|
||||
}
|
||||
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(fqR field.Fq) [][]*big.Int {
|
||||
nonce := 0
|
||||
cauchyMatrix := getPseudoRandom(fqR, SEED+"_matrix_"+nonceToString(nonce), big.NewInt(T*2))
|
||||
for !checkAllDifferent(cauchyMatrix) {
|
||||
nonce += 1
|
||||
cauchyMatrix = getPseudoRandom(fqR, SEED+"_matrix_"+nonceToString(nonce), big.NewInt(T*2))
|
||||
}
|
||||
var m [][]*big.Int
|
||||
for i := 0; i < T; i++ {
|
||||
var mi []*big.Int
|
||||
for j := 0; j < T; j++ {
|
||||
mi = append(mi, fqR.Inverse(fqR.Sub(cauchyMatrix[i], cauchyMatrix[T+j])))
|
||||
}
|
||||
m = append(m, mi)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func checkAllDifferent(v []*big.Int) bool {
|
||||
for i := 0; i < len(v); i++ {
|
||||
if bytes.Equal(v[i].Bytes(), big.NewInt(int64(0)).Bytes()) {
|
||||
return false
|
||||
}
|
||||
for j := i + 1; j < len(v); j++ {
|
||||
if bytes.Equal(v[i].Bytes(), v[j].Bytes()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf
|
||||
func ark(state []*big.Int, c *big.Int) []*big.Int {
|
||||
for i := 0; i < len(state); i++ {
|
||||
state[i] = constants.fqR.Add(state[i], c)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// cubic performs x^3 mod p
|
||||
func cubic(a *big.Int) *big.Int {
|
||||
return constants.fqR.Mul(a, constants.fqR.Square(constants.fqR.Square(a)))
|
||||
}
|
||||
|
||||
// sbox https://eprint.iacr.org/2019/458.pdf pag.6
|
||||
func sbox(state []*big.Int, i int) []*big.Int {
|
||||
if (i < NROUNDSF/2) || (i >= NROUNDSF/2+NROUNDSP) {
|
||||
for j := 0; j < T; j++ {
|
||||
state[j] = cubic(state[j])
|
||||
}
|
||||
} else {
|
||||
state[0] = cubic(state[0])
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// mix returns [[matrix]] * [vector]
|
||||
func mix(state []*big.Int, m [][]*big.Int) []*big.Int {
|
||||
var newState []*big.Int
|
||||
for i := 0; i < len(state); i++ {
|
||||
newState = append(newState, constants.fqR.Zero())
|
||||
for j := 0; j < len(state); j++ {
|
||||
newState[i] = constants.fqR.Add(newState[i], constants.fqR.Mul(m[i][j], state[j]))
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(state); i++ {
|
||||
state[i] = newState[i]
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// Hash computes the Poseidon hash for the given inputs
|
||||
func Hash(inp []*big.Int) (*big.Int, error) {
|
||||
var state []*big.Int
|
||||
if len(inp) < 0 || len(inp) > T {
|
||||
return nil, errors.New("wrong inputs length")
|
||||
}
|
||||
if !checkBigIntArrayInField(inp, constants.fqR.Q) {
|
||||
return nil, errors.New("inputs values not inside Finite Field")
|
||||
}
|
||||
|
||||
for i := 0; i < len(inp); i++ {
|
||||
state = append(state, inp[i])
|
||||
}
|
||||
for i := len(inp); i < T; i++ {
|
||||
state = append(state, constants.fqR.Zero())
|
||||
}
|
||||
|
||||
// ARK --> SBox --> M, https://eprint.iacr.org/2019/458.pdf pag.5
|
||||
for i := 0; i < NROUNDSF+NROUNDSP; i++ {
|
||||
state = ark(state, constants.c[i])
|
||||
state = sbox(state, i)
|
||||
state = mix(state, constants.m)
|
||||
}
|
||||
return state[0], nil
|
||||
}
|
||||
Reference in New Issue
Block a user