add Poseidondecompress-modsqrt v0.0.1
@ -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,26 @@ |
|||||
|
package constants |
||||
|
|
||||
|
import ( |
||||
|
"github.com/iden3/go-iden3-crypto/utils" |
||||
|
"math/big" |
||||
|
) |
||||
|
|
||||
|
// Q is the order of the integer field (Zq) that fits inside the SNARK.
|
||||
|
var Q *big.Int |
||||
|
|
||||
|
// Zero is 0.
|
||||
|
var Zero *big.Int |
||||
|
|
||||
|
// One is 1.
|
||||
|
var One *big.Int |
||||
|
|
||||
|
// MinusOne is -1.
|
||||
|
var MinusOne *big.Int |
||||
|
|
||||
|
func init() { |
||||
|
Zero = big.NewInt(0) |
||||
|
One = big.NewInt(1) |
||||
|
MinusOne = big.NewInt(-1) |
||||
|
Q = utils.NewIntFromString( |
||||
|
"21888242871839275222246405745257275088548364400416034343698204186575808495617") |
||||
|
} |
@ -0,0 +1,166 @@ |
|||||
|
package poseidon |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"errors" |
||||
|
"math/big" |
||||
|
"strconv" |
||||
|
|
||||
|
_constants "github.com/iden3/go-iden3-crypto/constants" |
||||
|
"github.com/iden3/go-iden3-crypto/field" |
||||
|
"github.com/iden3/go-iden3-crypto/utils" |
||||
|
"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 |
||||
|
} |
||||
|
|
||||
|
func generateConstantsData() constantsData { |
||||
|
var constants constantsData |
||||
|
|
||||
|
fqR := field.NewFq(_constants.Q) |
||||
|
constants.fqR = fqR |
||||
|
constants.c = getPseudoRandom(fqR, SEED+"_constants", NROUNDSF+NROUNDSP) |
||||
|
constants.m = getMDS(fqR) |
||||
|
|
||||
|
return constants |
||||
|
} |
||||
|
|
||||
|
func leByteArrayToBigInt(b []byte) *big.Int { |
||||
|
res := big.NewInt(0) |
||||
|
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 getPseudoRandom(fqR field.Fq, seed string, n int) []*big.Int { |
||||
|
var res []*big.Int |
||||
|
hash := blake2b.Sum256([]byte(seed)) |
||||
|
for len(res) < n { |
||||
|
hashBigInt := new(big.Int) |
||||
|
newN := fqR.Affine(utils.SetBigIntFromLEBytes(hashBigInt, hash[:])) |
||||
|
// newN := fqR.Affine(leByteArrayToBigInt(hash[:]))
|
||||
|
res = append(res, newN) |
||||
|
hash = blake2b.Sum256(hash[:]) |
||||
|
} |
||||
|
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), T*2) |
||||
|
for !checkAllDifferent(cauchyMatrix) { |
||||
|
nonce += 1 |
||||
|
cauchyMatrix = getPseudoRandom(fqR, SEED+"_matrix_"+nonceToString(nonce), 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 !utils.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()) |
||||
|
} |
@ -0,0 +1,106 @@ |
|||||
|
package utils |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"encoding/hex" |
||||
|
"fmt" |
||||
|
"math/big" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// NewIntFromString creates a new big.Int from a decimal integer encoded as a
|
||||
|
// string. It will panic if the string is not a decimal integer.
|
||||
|
func NewIntFromString(s string) *big.Int { |
||||
|
v, ok := new(big.Int).SetString(s, 10) |
||||
|
if !ok { |
||||
|
panic(fmt.Sprintf("Bad base 10 string %s", s)) |
||||
|
} |
||||
|
return v |
||||
|
} |
||||
|
|
||||
|
// SwapEndianness swaps the endianness of the value encoded in xs. If xs is
|
||||
|
// Big-Endian, the result will be Little-Endian and viceversa.
|
||||
|
func SwapEndianness(xs []byte) []byte { |
||||
|
ys := make([]byte, len(xs)) |
||||
|
for i, b := range xs { |
||||
|
ys[len(xs)-1-i] = b |
||||
|
} |
||||
|
return ys |
||||
|
} |
||||
|
|
||||
|
// BigIntLEBytes encodes a big.Int into an array in Little-Endian.
|
||||
|
func BigIntLEBytes(v *big.Int) [32]byte { |
||||
|
le := SwapEndianness(v.Bytes()) |
||||
|
res := [32]byte{} |
||||
|
copy(res[:], le) |
||||
|
return res |
||||
|
} |
||||
|
|
||||
|
// SetBigIntFromLEBytes sets the value of a big.Int from a Little-Endian
|
||||
|
// encoded value.
|
||||
|
func SetBigIntFromLEBytes(v *big.Int, leBuf []byte) *big.Int { |
||||
|
beBuf := SwapEndianness(leBuf) |
||||
|
return v.SetBytes(beBuf) |
||||
|
} |
||||
|
|
||||
|
// Hex is a byte slice type that can be marshalled and unmarshaled in hex
|
||||
|
type Hex []byte |
||||
|
|
||||
|
// MarshalText encodes buf as hex
|
||||
|
func (buf Hex) MarshalText() ([]byte, error) { |
||||
|
return []byte(hex.EncodeToString(buf)), nil |
||||
|
} |
||||
|
|
||||
|
// String encodes buf as hex
|
||||
|
func (buf Hex) String() string { |
||||
|
return hex.EncodeToString(buf) |
||||
|
} |
||||
|
|
||||
|
// HexEncode encodes an array of bytes into a string in hex.
|
||||
|
func HexEncode(bs []byte) string { |
||||
|
return fmt.Sprintf("0x%s", hex.EncodeToString(bs)) |
||||
|
} |
||||
|
|
||||
|
// HexDecode decodes a hex string into an array of bytes.
|
||||
|
func HexDecode(h string) ([]byte, error) { |
||||
|
if strings.HasPrefix(h, "0x") { |
||||
|
h = h[2:] |
||||
|
} |
||||
|
return hex.DecodeString(h) |
||||
|
} |
||||
|
|
||||
|
// HexDecodeInto decodes a hex string into an array of bytes (dst), verifying
|
||||
|
// that the decoded array has the same length as dst.
|
||||
|
func HexDecodeInto(dst []byte, h []byte) error { |
||||
|
if bytes.HasPrefix(h, []byte("0x")) { |
||||
|
h = h[2:] |
||||
|
} |
||||
|
if len(h)/2 != len(dst) { |
||||
|
return fmt.Errorf("expected %v bytes in hex string, got %v", len(dst), len(h)/2) |
||||
|
} |
||||
|
n, err := hex.Decode(dst, h) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} else if n != len(dst) { |
||||
|
return fmt.Errorf("expected %v bytes when decoding hex string, got %v", len(dst), n) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// CheckBigIntInField checks if given big.Int fits in a Field Q 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 Q element
|
||||
|
func CheckBigIntArrayInField(arr []*big.Int, q *big.Int) bool { |
||||
|
for _, a := range arr { |
||||
|
if !CheckBigIntInField(a, q) { |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
return true |
||||
|
} |