@ -1,3 +1,4 @@ |
|||||
# go-iden3-crypto [![Go Report Card](https://goreportcard.com/badge/github.com/iden3/go-iden3-crypto)](https://goreportcard.com/report/github.com/iden3/go-iden3-crypto) [![Build Status](https://travis-ci.org/iden3/go-iden3-crypto.svg?branch=master)](https://travis-ci.org/iden3/go-iden3-crypto) [![GoDoc](https://godoc.org/github.com/iden3/go-iden3-crypto?status.svg)](https://godoc.org/github.com/iden3/go-iden3-crypto) |
# go-iden3-crypto [![Go Report Card](https://goreportcard.com/badge/github.com/iden3/go-iden3-crypto)](https://goreportcard.com/report/github.com/iden3/go-iden3-crypto) [![Build Status](https://travis-ci.org/iden3/go-iden3-crypto.svg?branch=master)](https://travis-ci.org/iden3/go-iden3-crypto) [![GoDoc](https://godoc.org/github.com/iden3/go-iden3-crypto?status.svg)](https://godoc.org/github.com/iden3/go-iden3-crypto) |
||||
Go implementation of some cryptographic primitives used in iden3 |
|
||||
|
|
||||
|
Go implementation of some cryptographic primitives (that fit inside the SNARK field) used in iden3 |
||||
|
|
@ -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 |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
package poseidon |
||||
|
|
||||
|
import ( |
||||
|
"encoding/hex" |
||||
|
"math/big" |
||||
|
"testing" |
||||
|
|
||||
|
"github.com/stretchr/testify/assert" |
||||
|
"golang.org/x/crypto/blake2b" |
||||
|
) |
||||
|
|
||||
|
func TestBlake2bVersion(t *testing.T) { |
||||
|
h := blake2b.Sum256([]byte("poseidon_constants")) |
||||
|
assert.Equal(t, "e57ba154fb2c47811dc1a2369b27e25a44915b4e4ece4eb8ec74850cb78e01b1", hex.EncodeToString(h[:])) |
||||
|
} |
||||
|
|
||||
|
func TestPoseidon(t *testing.T) { |
||||
|
b1 := big.NewInt(int64(1)) |
||||
|
b2 := big.NewInt(int64(2)) |
||||
|
h, err := Hash([]*big.Int{b1, b2}) |
||||
|
assert.Nil(t, err) |
||||
|
assert.Equal(t, "12242166908188651009877250812424843524687801523336557272219921456462821518061", h.String()) |
||||
|
|
||||
|
b3 := big.NewInt(int64(3)) |
||||
|
b4 := big.NewInt(int64(4)) |
||||
|
h, err = Hash([]*big.Int{b3, b4}) |
||||
|
assert.Nil(t, err) |
||||
|
assert.Equal(t, "17185195740979599334254027721507328033796809509313949281114643312710535000993", h.String()) |
||||
|
} |