From b0836b1c4320e1b17ae58595200048f66f48a970 Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Sun, 2 Feb 2025 20:59:06 -0600 Subject: [PATCH 01/21] feat: poseidon2 hash e2e --- ecc/bls12-377/fr/poseidon2/hash.go | 20 ++++ ecc/bls12-377/fr/poseidon2/poseidon2.go | 92 ++++++++++++------- hash/all/allhashes.go | 1 + hash/hashes.go | 19 ++-- hash/merkle-damgard.go | 69 ++++++++++++++ .../hash/poseidon2/template/poseidon2.go.tmpl | 22 ++++- 6 files changed, 181 insertions(+), 42 deletions(-) create mode 100644 ecc/bls12-377/fr/poseidon2/hash.go create mode 100644 hash/merkle-damgard.go diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go new file mode 100644 index 0000000000..83976df46c --- /dev/null +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -0,0 +1,20 @@ +package poseidon2 + +import ( + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/gnark-crypto/hash" +) + +func NewPoseidon2() hash.StateStorer { + return hash.NewMerkleDamgardHasher(&Hash{params: GetParameters()}, make([]byte, fr.Bytes)) +} + +// TODO @Tabaie @ThomasPiellard Generify once Poseidon2 parameters are known for all curves +func GetParameters() Parameters { + return Parameters{ + T: 2, + Rf: 6, + Rp: 26, + RoundKeys: InitRC("Poseidon2 hash for BLS12-377 with t=2, rF=6, rP=26, d=17", 6, 26, 2), + } +} diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index a180904e0e..3de0d3e8b6 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // origina paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -135,39 +135,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -176,15 +176,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -193,11 +193,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -205,40 +205,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -246,3 +250,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} diff --git a/hash/all/allhashes.go b/hash/all/allhashes.go index cb7b5947e3..a06636e056 100644 --- a/hash/all/allhashes.go +++ b/hash/all/allhashes.go @@ -2,6 +2,7 @@ package all import ( _ "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" + _ "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" _ "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" _ "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/mimc" _ "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/mimc" diff --git a/hash/hashes.go b/hash/hashes.go index 897a8f11fe..4f433c86d9 100644 --- a/hash/hashes.go +++ b/hash/hashes.go @@ -38,19 +38,22 @@ const ( MIMC_BLS24_317 // MIMC_BW6_633 is the MiMC hash function for the BW6-633 curve. MIMC_BW6_633 + // POSEIDON2_BLS12_377 is the Poseidon2 hash function for the BLS12-377 curve. + POSEIDON2_BLS12_377 maxHash ) // size of digests in bytes var digestSize = []uint8{ - MIMC_BN254: 32, - MIMC_BLS12_381: 48, - MIMC_BLS12_377: 48, - MIMC_BW6_761: 96, - MIMC_BLS24_315: 48, - MIMC_BLS24_317: 48, - MIMC_BW6_633: 80, + MIMC_BN254: 32, + MIMC_BLS12_381: 48, + MIMC_BLS12_377: 48, + MIMC_BW6_761: 96, + MIMC_BLS24_315: 48, + MIMC_BLS24_317: 48, + MIMC_BW6_633: 80, + POSEIDON2_BLS12_377: 48, } // New initializes the hash function. This is a convenience function which does @@ -87,6 +90,8 @@ func (m Hash) String() string { return "MIMC_BLS24_317" case MIMC_BW6_633: return "MIMC_BW6_633" + case POSEIDON2_BLS12_377: + return "POSEIDON2_BLS12_377" default: return "unknown hash function" } diff --git a/hash/merkle-damgard.go b/hash/merkle-damgard.go new file mode 100644 index 0000000000..6d832048e9 --- /dev/null +++ b/hash/merkle-damgard.go @@ -0,0 +1,69 @@ +package hash + +// CompressionFunction is a 2 to 1 function +type CompressionFunction interface { + Apply([]byte, []byte) ([]byte, error) // TODO @Tabaie @ThomasPiellard better name + BlockSize() int +} +type merkleDamgardHasher struct { + state []byte + iv []byte + f CompressionFunction +} + +// Write implements hash.Write +func (h *merkleDamgardHasher) Write(p []byte) (n int, err error) { + blockSize := h.f.BlockSize() + for len(p) != 0 { + if len(p) < blockSize { + p = append(make([]byte, blockSize-len(p), blockSize), p...) + } + if h.state, err = h.f.Apply(h.state, p[:blockSize]); err != nil { + return + } + n += blockSize + p = p[blockSize:] + } + return +} + +func (h *merkleDamgardHasher) Sum(b []byte) []byte { + if _, err := h.Write(b); err != nil { + panic(err) + } + return h.state +} + +func (h *merkleDamgardHasher) Reset() { + h.state = h.iv +} + +func (h *merkleDamgardHasher) Size() int { + return h.f.BlockSize() +} + +func (h *merkleDamgardHasher) BlockSize() int { + return h.f.BlockSize() +} + +func (h *merkleDamgardHasher) State() []byte { + return h.state +} + +func (h *merkleDamgardHasher) SetState(state []byte) error { + h.state = state + return nil +} + +// NewMerkleDamgardHasher transforms a 2-1 one-way function into a hash +// initialState is a value whose preimage is not known +// WARNING: The padding performed by the resulting hasher is trivial. +// It simply left zero-pads the last block of input +// THIS IS NOT COLLISION RESISTANT FOR GENERIC DATA +func NewMerkleDamgardHasher(f CompressionFunction, initialState []byte) StateStorer { + return &merkleDamgardHasher{ + state: initialState, + iv: initialState, + f: f, + } +} diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index 346db87726..f9477d7eae 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -10,7 +10,7 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf // parameters describing the poseidon2 implementation type parameters struct { @@ -250,3 +250,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a, b []byte) ([]byte, error) { + if h.params.t != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} \ No newline at end of file From 6e98abf16d1e9168ba88eccc0d53e2abef1c4304 Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Sun, 2 Feb 2025 21:02:58 -0600 Subject: [PATCH 02/21] build: generify --- ecc/bls12-377/fr/poseidon2/poseidon2.go | 2 +- ecc/bls12-381/fr/poseidon2/poseidon2.go | 94 ++++++++++++------- ecc/bls24-315/fr/poseidon2/poseidon2.go | 94 ++++++++++++------- ecc/bls24-317/fr/poseidon2/poseidon2.go | 94 ++++++++++++------- ecc/bn254/fr/poseidon2/poseidon2.go | 94 ++++++++++++------- ecc/bw6-633/fr/poseidon2/poseidon2.go | 94 ++++++++++++------- ecc/bw6-761/fr/poseidon2/poseidon2.go | 94 ++++++++++++------- .../hash/poseidon2/template/poseidon2.go.tmpl | 76 ++++++++------- 8 files changed, 395 insertions(+), 247 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 3de0d3e8b6..a7cb613420 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -17,7 +17,7 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf // Parameters describing the poseidon2 implementation type Parameters struct { diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index 481c81ba72..ead192c09e 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -17,38 +17,38 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -133,39 +133,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -174,15 +174,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,11 +191,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -203,40 +203,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -244,3 +248,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index 824dcead74..324ff074d7 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -17,38 +17,38 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -133,39 +133,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -174,15 +174,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,11 +191,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -203,40 +203,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -244,3 +248,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 74bec677a8..715b175a6f 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -17,38 +17,38 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -134,39 +134,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -175,15 +175,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -192,11 +192,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -204,40 +204,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -245,3 +249,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index bf5384f4ce..5d3df1cff0 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -17,38 +17,38 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -133,39 +133,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -174,15 +174,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,11 +191,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -203,40 +203,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -244,3 +248,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index 0a3c894a71..22b09603d5 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -17,38 +17,38 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -133,39 +133,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -174,15 +174,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,11 +191,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -203,40 +203,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -244,3 +248,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index ee8d45dd41..d879701d0f 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -17,38 +17,38 @@ var ( // reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// origina paper: https://eprint.iacr.org/2023/323.pdf +// original paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -133,39 +133,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -174,15 +174,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,11 +191,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -203,40 +203,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -244,3 +248,23 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } + +// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { + return nil, errors.New("need a 2-1 function") + } + var x [2]fr.Element + + if err := x[0].SetBytesCanonical(a); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(b); err != nil { + return nil, err + } + if err := h.Permutation(x[:]); err != nil { + return nil, err + } + res := x[1].Bytes() + return res[:], nil +} diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index f9477d7eae..f1127c2b10 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -12,36 +12,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + T int // number of full rounds (even number) - rF int + Rf int // number of partial rounds - rP int + Rp int // diagonal elements of the internal matrices, minus one - diagInternalMatrices []fr.Element + DiagInternalMatrices []fr.Element // round keys - roundKeys [][]fr.Element + RoundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // parameters describing the - params parameters + // Parameters describing the + params Parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) + params := Parameters{T: t, Rf: rf, Rp: rp} + params.RoundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -139,39 +139,39 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { } } -// when t=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) -// see https://eprint.iacr.org/2023/323.pdf page 15, case t=2,3 +// when T=2,3 the buffer is multiplied by circ(2,1) and circ(2,1,1) +// see https://eprint.iacr.org/2023/323.pdf page 15, case T=2,3 // -// when t=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.T == 4 { h.matMulM4InPlace(input) } else { - // at this stage t is supposed to be a multiple of 4 + // at this stage T is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.T/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -180,15 +180,15 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { } } -// when t=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] +// when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.T == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + } else if h.params.T == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -197,11 +197,11 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { + for i := 1; i < h.params.T; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + for i := 0; i < h.params.T; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } } @@ -209,40 +209,44 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } +func (h *Hash) BlockSize() int { + return fr.Bytes +} + // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { + if len(input) != h.params.T { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.Rf / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.Rp; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.T; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -252,8 +256,8 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a, b []byte) ([]byte, error) { - if h.params.t != 2 { +func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { + if h.params.T != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element From ef9d20ded1af24a8af49e2e72da740940e96347b Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Sun, 2 Feb 2025 21:14:19 -0600 Subject: [PATCH 03/21] revert: make parameters private --- ecc/bls12-377/fr/poseidon2/hash.go | 20 +++---- ecc/bls12-377/fr/poseidon2/poseidon2.go | 58 +++++++++---------- ecc/bls12-381/fr/poseidon2/poseidon2.go | 58 +++++++++---------- ecc/bls24-315/fr/poseidon2/poseidon2.go | 58 +++++++++---------- ecc/bls24-317/fr/poseidon2/poseidon2.go | 58 +++++++++---------- ecc/bn254/fr/poseidon2/poseidon2.go | 58 +++++++++---------- ecc/bw6-633/fr/poseidon2/poseidon2.go | 58 +++++++++---------- ecc/bw6-761/fr/poseidon2/poseidon2.go | 58 +++++++++---------- .../hash/poseidon2/template/poseidon2.go.tmpl | 58 +++++++++---------- 9 files changed, 241 insertions(+), 243 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 83976df46c..5fdfa29964 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -5,16 +5,14 @@ import ( "github.com/consensys/gnark-crypto/hash" ) -func NewPoseidon2() hash.StateStorer { - return hash.NewMerkleDamgardHasher(&Hash{params: GetParameters()}, make([]byte, fr.Bytes)) -} - +// NewPoseidon2 returns a Poseidon2 hasher // TODO @Tabaie @ThomasPiellard Generify once Poseidon2 parameters are known for all curves -func GetParameters() Parameters { - return Parameters{ - T: 2, - Rf: 6, - Rp: 26, - RoundKeys: InitRC("Poseidon2 hash for BLS12-377 with t=2, rF=6, rP=26, d=17", 6, 26, 2), - } +func NewPoseidon2() hash.StateStorer { + return hash.NewMerkleDamgardHasher( + &Hash{params: parameters{ + t: 2, + rF: 6, + rP: 26, + roundKeys: InitRC("Poseidon2 hash for BLS12-377 with t=2, rF=6, rP=26, d=17", 6, 26, 2), + }}, make([]byte, fr.Bytes)) } diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index a7cb613420..a89f6ffbb8 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -142,32 +142,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -179,12 +179,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -193,10 +193,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -205,8 +205,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -216,33 +216,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -253,7 +253,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index ead192c09e..d4b8144fa3 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -140,32 +140,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -177,12 +177,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,10 +191,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -203,8 +203,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -214,33 +214,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -251,7 +251,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index 324ff074d7..674a24a92d 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -140,32 +140,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -177,12 +177,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,10 +191,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -203,8 +203,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -214,33 +214,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -251,7 +251,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 715b175a6f..d54a5adf12 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -141,32 +141,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -178,12 +178,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -192,10 +192,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -204,8 +204,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -215,33 +215,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -252,7 +252,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index 5d3df1cff0..d02aaf5555 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -140,32 +140,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -177,12 +177,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,10 +191,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -203,8 +203,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -214,33 +214,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -251,7 +251,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index 22b09603d5..dc093b76fe 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -140,32 +140,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -177,12 +177,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,10 +191,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -203,8 +203,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -214,33 +214,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -251,7 +251,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index d879701d0f..81e557fb7e 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -19,36 +19,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -140,32 +140,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -177,12 +177,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -191,10 +191,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -203,8 +203,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -214,33 +214,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -251,7 +251,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index f1127c2b10..88bb3d3fc3 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -12,36 +12,36 @@ var ( // specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf // original paper: https://eprint.iacr.org/2023/323.pdf -// Parameters describing the poseidon2 implementation -type Parameters struct { +// parameters describing the poseidon2 implementation +type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - T int + t int // number of full rounds (even number) - Rf int + rF int // number of partial rounds - Rp int + rP int // diagonal elements of the internal matrices, minus one DiagInternalMatrices []fr.Element // round keys - RoundKeys [][]fr.Element + roundKeys [][]fr.Element } // Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation // methods on the buffer type Hash struct { - // Parameters describing the - params Parameters + // parameters describing the + params parameters } // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { - params := Parameters{T: t, Rf: rf, Rp: rp} - params.RoundKeys = InitRC(seed, rf, rp, t) + params := parameters{t: t, rF: rf, rP: rp} + params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} return res } @@ -146,32 +146,32 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Hash) matMulExternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.T == 4 { + } else if h.params.t == 4 { h.matMulM4InPlace(input) } else { - // at this stage T is supposed to be a multiple of 4 + // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.T/4; i++ { + for i := 0; i < h.params.t/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -183,12 +183,12 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.T == 2 { + if h.params.t == 2 { var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.T == 3 { + } else if h.params.t == 3 { var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) @@ -197,10 +197,10 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } else { var sum fr.Element sum.Set(&input[0]) - for i := 1; i < h.params.T; i++ { + for i := 1; i < h.params.t; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.T; i++ { + for i := 0; i < h.params.t; i++ { input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). Add(&input[i], &sum) } @@ -209,8 +209,8 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.RoundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.RoundKeys[round][i]) + for i := 0; i < len(h.params.roundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.roundKeys[round][i]) } } @@ -220,33 +220,33 @@ func (h *Hash) BlockSize() int { // Permutation applies the permutation on input, and stores the result in input. func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.T { + if len(input) != h.params.t { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.Rf / 2 + rf := h.params.rF / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.Rp; i++ { + for i := rf; i < rf+h.params.rP; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.Rp; i < h.params.Rf+h.params.Rp; i++ { + for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.T; j++ { + for j := 0; j < h.params.t; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -257,7 +257,7 @@ func (h *Hash) Permutation(input []fr.Element) error { // Apply implements hash.CompressionFunction, as a generic wrapper for Permutation func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { - if h.params.T != 2 { + if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element From 19afe00d3be1f695b605de33520c47eaa6600ba6 Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Sun, 2 Feb 2025 21:31:18 -0600 Subject: [PATCH 04/21] fix: match seed with gnark --- ecc/bls12-377/fr/poseidon2/hash.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 5fdfa29964..06df5e310b 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -13,6 +13,6 @@ func NewPoseidon2() hash.StateStorer { t: 2, rF: 6, rP: 26, - roundKeys: InitRC("Poseidon2 hash for BLS12-377 with t=2, rF=6, rP=26, d=17", 6, 26, 2), + roundKeys: InitRC("Poseidon2 hash for BLS12_377 with t=2, rF=6, rP=26, d=17", 6, 26, 2), }}, make([]byte, fr.Bytes)) } From d67ae5dbfb6562e08bc32393723f539ede5b5453 Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:31:11 -0600 Subject: [PATCH 05/21] feat: register poseidon2 for bls12377 --- ecc/bls12-377/fr/poseidon2/hash.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 06df5e310b..613dd3617e 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -2,13 +2,14 @@ package poseidon2 import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/consensys/gnark-crypto/hash" + gnarkHash "github.com/consensys/gnark-crypto/hash" + "hash" ) // NewPoseidon2 returns a Poseidon2 hasher // TODO @Tabaie @ThomasPiellard Generify once Poseidon2 parameters are known for all curves -func NewPoseidon2() hash.StateStorer { - return hash.NewMerkleDamgardHasher( +func NewPoseidon2() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( &Hash{params: parameters{ t: 2, rF: 6, @@ -16,3 +17,9 @@ func NewPoseidon2() hash.StateStorer { roundKeys: InitRC("Poseidon2 hash for BLS12_377 with t=2, rF=6, rP=26, d=17", 6, 26, 2), }}, make([]byte, fr.Bytes)) } + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS12_377, func() hash.Hash { + return NewPoseidon2() + }) +} From 5f6f607f9ad6a04a056f4323fd6baed837cf3854 Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:55:05 -0600 Subject: [PATCH 06/21] feat: add gkr gates --- ecc/bls12-377/fr/poseidon2/hash.go | 206 ++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 6 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 613dd3617e..6d576757d5 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -1,7 +1,9 @@ package poseidon2 import ( + "fmt" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/gkr" gnarkHash "github.com/consensys/gnark-crypto/hash" "hash" ) @@ -10,12 +12,21 @@ import ( // TODO @Tabaie @ThomasPiellard Generify once Poseidon2 parameters are known for all curves func NewPoseidon2() gnarkHash.StateStorer { return gnarkHash.NewMerkleDamgardHasher( - &Hash{params: parameters{ - t: 2, - rF: 6, - rP: 26, - roundKeys: InitRC("Poseidon2 hash for BLS12_377 with t=2, rF=6, rP=26, d=17", 6, 26, 2), - }}, make([]byte, fr.Bytes)) + &Hash{params: params()}, make([]byte, fr.Bytes)) +} + +const ( + seed = "Poseidon2 hash for BLS12_377 with t=2, rF=6, rP=26, d=17" + d = 17 +) + +func params() parameters { + return parameters{ + t: 2, + rF: 6, + rP: 26, + roundKeys: InitRC(seed, 6, 26, 2), + } } func init() { @@ -23,3 +34,186 @@ func init() { return NewPoseidon2() }) } + +// The GKR gates needed for proving Poseidon2 permutations +// TODO @Tabaie @ThomasPiellard generify once Poseidon2 parameters are known for all curves + +// extKeySBoxGate applies the external matrix mul, then adds the round key, then applies the sBox +// because of its symmetry, we don't need to define distinct x1 and x2 versions of it +type extKeySBoxGate struct { + roundKey fr.Element +} + +func (g *extKeySBoxGate) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + + x[0]. + Double(&x[0]). + Add(&x[0], &x[1]). + Add(&x[0], &g.roundKey) + return powerFr(x[0], d) +} + +func (g *extKeySBoxGate) Degree() int { + return d +} + +// for x1, the partial round gates are identical to full round gates +// for x2, the partial round gates are just a linear combination +// TODO @Tabaie eliminate the x2 partial round gates and have the x1 gates depend on i - rf/2 or so previous x1's + +// extGate2 applies the external matrix mul, outputting the second element of the result +type extGate2 struct{} + +func (extGate2) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[1]. + Double(&x[1]). + Add(&x[1], &x[0]) + return x[1] +} + +func (g extGate2) Degree() int { + return 1 +} + +// intGate2 applies the internal matrix mul, returning the second element +type intGate2 struct { +} + +func (g intGate2) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[0].Add(&x[0], &x[1]) + x[1]. + Double(&x[1]). + Add(&x[1], &x[0]) + return x[1] +} + +func (g intGate2) Degree() int { + return 1 +} + +// intKeySBoxGateFr applies the second row of internal matrix mul, then adds the round key, then applies the sBox +type intKeySBoxGate2 struct { + roundKey fr.Element +} + +func (g *intKeySBoxGate2) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[0].Add(&x[0], &x[1]) + x[1]. + Double(&x[1]). + Add(&x[1], &x[0]). + Add(&x[1], &g.roundKey) + + return powerFr(x[1], 17) +} + +func (g *intKeySBoxGate2) Degree() int { + return d +} + +type extGate struct{} + +func (g extGate) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[0]. + Double(&x[0]). + Add(&x[0], &x[1]) + return x[0] +} + +func (g extGate) Degree() int { + return 1 +} + +func powerFr(x fr.Element, n int) fr.Element { + tmp := x + switch n { + case 3: + x.Square(&x).Mul(&tmp, &x) + case 5: + x.Square(&x).Square(&x).Mul(&x, &tmp) + case 7: + x.Square(&x).Mul(&x, &tmp).Square(&x).Mul(&x, &tmp) + case 17: + x.Square(&x).Square(&x).Square(&x).Square(&x).Mul(&x, &tmp) + case -1: + x.Inverse(&x) + default: + panic("unknown sBox degree") + } + return x +} + +func DefineGkrGates() { + p := params() + halfRf := p.rF / 2 + + gateNameX := func(i int) string { + return fmt.Sprintf("x-round=%d%s", i, seed) + } + gateNameY := func(i int) string { + return fmt.Sprintf("y-round=%d%s", i, seed) + } + + fullRound := func(i int) { + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ + roundKey: p.roundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = &extKeySBoxGate{ + roundKey: p.roundKeys[i][1], + } + } + + for i := range halfRf { + fullRound(i) + } + + { // i = halfRf: first partial round + i := halfRf + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ + roundKey: p.roundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = extGate2{} + } + + for i := halfRf + 1; i < halfRf+p.rP; i++ { + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ // for x1, intKeySBox is identical to extKeySBox + roundKey: p.roundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = intGate2{} + + } + + { + i := halfRf + p.rP + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ + roundKey: p.roundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = &intKeySBoxGate2{ + roundKey: p.roundKeys[i][1], + } + } + + for i := halfRf + p.rP + 1; i < p.rP+p.rF; i++ { + fullRound(i) + } + + gkr.Gates[gateNameY(p.rP+p.rF)] = extGate{} +} From a7ee00be8890143210bbbc13e129c070a1ba4700 Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Mon, 3 Feb 2025 14:01:29 -0600 Subject: [PATCH 07/21] refactor: powerFr -> sBox --- ecc/bls12-377/fr/poseidon2/hash.go | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 6d576757d5..d241114daf 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -53,7 +53,7 @@ func (g *extKeySBoxGate) Evaluate(x ...fr.Element) fr.Element { Double(&x[0]). Add(&x[0], &x[1]). Add(&x[0], &g.roundKey) - return powerFr(x[0], d) + return sBox2(x[0]) } func (g *extKeySBoxGate) Degree() int { @@ -115,7 +115,7 @@ func (g *intKeySBoxGate2) Evaluate(x ...fr.Element) fr.Element { Add(&x[1], &x[0]). Add(&x[1], &g.roundKey) - return powerFr(x[1], 17) + return sBox2(x[1]) } func (g *intKeySBoxGate2) Degree() int { @@ -138,22 +138,10 @@ func (g extGate) Degree() int { return 1 } -func powerFr(x fr.Element, n int) fr.Element { - tmp := x - switch n { - case 3: - x.Square(&x).Mul(&tmp, &x) - case 5: - x.Square(&x).Square(&x).Mul(&x, &tmp) - case 7: - x.Square(&x).Mul(&x, &tmp).Square(&x).Mul(&x, &tmp) - case 17: - x.Square(&x).Square(&x).Square(&x).Square(&x).Mul(&x, &tmp) - case -1: - x.Inverse(&x) - default: - panic("unknown sBox degree") - } +// sBox2 is Hash.sBox for t=2 +func sBox2(x fr.Element) fr.Element { + var y fr.Element + y.Square(&x).Square(&y).Square(&y).Square(&y).Mul(&x, &y) return x } From 6342bcafbaf570d67660c41474207e336cf257ce Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:38:56 -0600 Subject: [PATCH 08/21] fix: sBox2 route correct output --- ecc/bls12-377/fr/poseidon2/hash.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index d241114daf..186354df5e 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -142,7 +142,7 @@ func (g extGate) Degree() int { func sBox2(x fr.Element) fr.Element { var y fr.Element y.Square(&x).Square(&y).Square(&y).Square(&y).Mul(&x, &y) - return x + return y } func DefineGkrGates() { From 6f9e706c7ead324432cf2d8169126b971821bb7a Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 15:18:51 +0000 Subject: [PATCH 09/21] chore: gofmt --- ecc/bls12-377/fr/poseidon2/hash.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 186354df5e..80562c5d77 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -2,10 +2,11 @@ package poseidon2 import ( "fmt" + "hash" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/gkr" gnarkHash "github.com/consensys/gnark-crypto/hash" - "hash" ) // NewPoseidon2 returns a Poseidon2 hasher From 3a59d348667092d3fb29a53866aac9e6f5a82278 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 15:23:03 +0000 Subject: [PATCH 10/21] refactor: rename the interface to Compresser --- ecc/bls12-377/fr/poseidon2/poseidon2.go | 9 +++++---- ecc/bls12-381/fr/poseidon2/poseidon2.go | 9 +++++---- ecc/bls24-315/fr/poseidon2/poseidon2.go | 9 +++++---- ecc/bls24-317/fr/poseidon2/poseidon2.go | 9 +++++---- ecc/bn254/fr/poseidon2/poseidon2.go | 9 +++++---- ecc/bw6-633/fr/poseidon2/poseidon2.go | 9 +++++---- ecc/bw6-761/fr/poseidon2/poseidon2.go | 9 +++++---- hash/interface.go | 18 ++++++++++++++++++ hash/merkle-damgard.go | 11 +++-------- .../hash/poseidon2/template/poseidon2.go.tmpl | 9 +++++---- 10 files changed, 61 insertions(+), 40 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 1ab7311eac..54439bf42c 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -251,17 +251,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index a9d2122807..45e7185d62 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -249,17 +249,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index 2ad404f874..4fcb216ce0 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -249,17 +249,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 2e56716bf2..b3bf2df2ec 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -250,17 +250,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index 7c431d4977..505764d8b3 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -249,17 +249,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index 64a3f8a6f2..7da6f2bb4f 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -249,17 +249,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index ce327cc95e..797067e314 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -249,17 +249,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { diff --git a/hash/interface.go b/hash/interface.go index 5b2c3063b4..c6670f3dc9 100644 --- a/hash/interface.go +++ b/hash/interface.go @@ -14,3 +14,21 @@ type StateStorer interface { // state retrieved using [StateStorer.State] method. SetState(state []byte) error } + +// Compresser is a 2-1 one-way function. It takes two inputs and compresses +// them into one output. The inputs and outputs are all of the same size, which +// is the block size. See [BlockSize]. +// +// NB! This is lossy compression, meaning that the output is not guaranteed to +// be unique for different inputs. The output is guaranteed to be the same for +// the same inputs. +// +// The Compresser is used in the Merkle-Damgard construction to build a hash +// function. +type Compresser interface { + // Compress compresses the two inputs into one output. All the inputs and + // outputs are of the same size, which is the block size. See [BlockSize]. + Compress(left []byte, right []byte) (compressed []byte, err error) + // BlockSize returns the blocks size. + BlockSize() int +} diff --git a/hash/merkle-damgard.go b/hash/merkle-damgard.go index 6d832048e9..9fb445725d 100644 --- a/hash/merkle-damgard.go +++ b/hash/merkle-damgard.go @@ -1,14 +1,9 @@ package hash -// CompressionFunction is a 2 to 1 function -type CompressionFunction interface { - Apply([]byte, []byte) ([]byte, error) // TODO @Tabaie @ThomasPiellard better name - BlockSize() int -} type merkleDamgardHasher struct { state []byte iv []byte - f CompressionFunction + f Compresser } // Write implements hash.Write @@ -18,7 +13,7 @@ func (h *merkleDamgardHasher) Write(p []byte) (n int, err error) { if len(p) < blockSize { p = append(make([]byte, blockSize-len(p), blockSize), p...) } - if h.state, err = h.f.Apply(h.state, p[:blockSize]); err != nil { + if h.state, err = h.f.Compress(h.state, p[:blockSize]); err != nil { return } n += blockSize @@ -60,7 +55,7 @@ func (h *merkleDamgardHasher) SetState(state []byte) error { // WARNING: The padding performed by the resulting hasher is trivial. // It simply left zero-pads the last block of input // THIS IS NOT COLLISION RESISTANT FOR GENERIC DATA -func NewMerkleDamgardHasher(f CompressionFunction, initialState []byte) StateStorer { +func NewMerkleDamgardHasher(f Compresser, initialState []byte) StateStorer { return &merkleDamgardHasher{ state: initialState, iv: initialState, diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index 6fd7b6d0e0..4c7b85306e 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -255,17 +255,18 @@ func (h *Hash) Permutation(input []fr.Element) error { return nil } -// Apply implements hash.CompressionFunction, as a generic wrapper for Permutation -func (h *Hash) Apply(a []byte, b []byte) ([]byte, error) { +// Compress applies the permutation on left and right and returns the right lane +// of the result. +func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { if h.params.t != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element - if err := x[0].SetBytesCanonical(a); err != nil { + if err := x[0].SetBytesCanonical(left); err != nil { return nil, err } - if err := x[1].SetBytesCanonical(b); err != nil { + if err := x[1].SetBytesCanonical(right); err != nil { return nil, err } if err := h.Permutation(x[:]); err != nil { From 4b2437a3d12f53fba824452122bee50be1354ce1 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 16:41:45 +0000 Subject: [PATCH 11/21] docs: package docs --- hash/doc.go | 20 ++++++++++++-------- hash/merkle-damgard.go | 19 ++++++++++++++----- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/hash/doc.go b/hash/doc.go index b1ed8c91c3..9b86cacd6d 100644 --- a/hash/doc.go +++ b/hash/doc.go @@ -1,14 +1,18 @@ -// Package hash provides MiMC hash function defined over implemented curves +// Package hash provides algebraic hash function defined over implemented curves // -// This package is kept for backwards compatibility. The recommended way to -// initialize hash function is to directly use the constructors in the -// corresponding packages (e.g. ecc/bn254/fr/mimc). Using the direct -// constructors allows to apply options for altering the hash function behavior -// (endianness, input splicing etc.) and returns more specific types with -// additional methods. +// This package is kept for backwards compatibility for initializing hash +// functions directly. The recommended way to initialize hash function is to +// directly use the constructors in the corresponding packages (e.g. +// ecc/bn254/fr/mimc). Using the direct constructors allows to apply options for +// altering the hash function behavior (endianness, input splicing etc.) and +// returns more specific types with additional methods. // // See [Importing hash functions] below for more information. // +// This package also provides a construction for a generic hash function from +// the compression primitive. See the interface [Compresser] and the +// corresponding initialization function [NewMerkleDamgardHasher]. +// // # Importing hash functions // // The package follows registration pattern for importing hash functions. To @@ -58,7 +62,7 @@ // The MiMC hash function is defined over a field. The input to the hash // function is a byte slice. The byte slice is interpreted as a sequence of // field elements. Due to this interpretation, the input byte slice length must -// be multiple of the field modulus size. And every secuence of byte slice for a +// be multiple of the field modulus size. And every sequence of byte slice for a // single field element must be strictly less than the field modulus. // // See open issues: diff --git a/hash/merkle-damgard.go b/hash/merkle-damgard.go index 9fb445725d..08c79ddffc 100644 --- a/hash/merkle-damgard.go +++ b/hash/merkle-damgard.go @@ -50,11 +50,20 @@ func (h *merkleDamgardHasher) SetState(state []byte) error { return nil } -// NewMerkleDamgardHasher transforms a 2-1 one-way function into a hash -// initialState is a value whose preimage is not known -// WARNING: The padding performed by the resulting hasher is trivial. -// It simply left zero-pads the last block of input -// THIS IS NOT COLLISION RESISTANT FOR GENERIC DATA +// NewMerkleDamgardHasher transforms a 2-1 one-way compression function into a +// hash function using a Merkle-Damgard construction. The resulting hash +// function has a block size equal to the block size of compression function. +// +// NB! The construction does not perform explicit padding on the input data. The +// last block of input data is zero-padded to full block size. This means that +// the construction is not collision resistant for generic data as the digest of +// input and input concatenated with zeros (up to the same number of total +// blocks) is same. For collision resistance the caller should perform explicit +// padding on the input data. +// +// The value initialState is provided as initial input to the compression +// function. Its preimage should not be known and thus it should be generated +// using a deterministic method. func NewMerkleDamgardHasher(f Compresser, initialState []byte) StateStorer { return &merkleDamgardHasher{ state: initialState, From 8d349012e792e6cd9139c76a0547a1a88353a267 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 17:55:29 +0000 Subject: [PATCH 12/21] docs: add package docs --- ecc/bls12-377/fr/poseidon2/doc.go | 17 +++++++++++++++++ ecc/bls12-377/fr/poseidon2/poseidon2.go | 4 ---- ecc/bls12-381/fr/poseidon2/doc.go | 17 +++++++++++++++++ ecc/bls12-381/fr/poseidon2/poseidon2.go | 4 ---- ecc/bls24-315/fr/poseidon2/doc.go | 17 +++++++++++++++++ ecc/bls24-315/fr/poseidon2/poseidon2.go | 4 ---- ecc/bls24-317/fr/poseidon2/doc.go | 17 +++++++++++++++++ ecc/bls24-317/fr/poseidon2/poseidon2.go | 4 ---- ecc/bn254/fr/poseidon2/doc.go | 17 +++++++++++++++++ ecc/bn254/fr/poseidon2/poseidon2.go | 4 ---- ecc/bw6-633/fr/poseidon2/doc.go | 17 +++++++++++++++++ ecc/bw6-633/fr/poseidon2/poseidon2.go | 4 ---- ecc/bw6-761/fr/poseidon2/doc.go | 17 +++++++++++++++++ ecc/bw6-761/fr/poseidon2/poseidon2.go | 4 ---- .../generator/crypto/hash/poseidon2/generate.go | 1 + .../crypto/hash/poseidon2/template/doc.go.tmpl | 12 ++++++++++++ .../hash/poseidon2/template/poseidon2.go.tmpl | 4 ---- 17 files changed, 132 insertions(+), 32 deletions(-) create mode 100644 ecc/bls12-377/fr/poseidon2/doc.go create mode 100644 ecc/bls12-381/fr/poseidon2/doc.go create mode 100644 ecc/bls24-315/fr/poseidon2/doc.go create mode 100644 ecc/bls24-317/fr/poseidon2/doc.go create mode 100644 ecc/bn254/fr/poseidon2/doc.go create mode 100644 ecc/bw6-633/fr/poseidon2/doc.go create mode 100644 ecc/bw6-761/fr/poseidon2/doc.go create mode 100644 internal/generator/crypto/hash/poseidon2/template/doc.go.tmpl diff --git a/ecc/bls12-377/fr/poseidon2/doc.go b/ecc/bls12-377/fr/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/ecc/bls12-377/fr/poseidon2/doc.go @@ -0,0 +1,17 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 54439bf42c..f02e0b658b 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -15,10 +15,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bls12-381/fr/poseidon2/doc.go b/ecc/bls12-381/fr/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/ecc/bls12-381/fr/poseidon2/doc.go @@ -0,0 +1,17 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index 45e7185d62..964026a0fb 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -15,10 +15,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bls24-315/fr/poseidon2/doc.go b/ecc/bls24-315/fr/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/ecc/bls24-315/fr/poseidon2/doc.go @@ -0,0 +1,17 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index 4fcb216ce0..bb177f9522 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -15,10 +15,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bls24-317/fr/poseidon2/doc.go b/ecc/bls24-317/fr/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/ecc/bls24-317/fr/poseidon2/doc.go @@ -0,0 +1,17 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index b3bf2df2ec..9083b50f38 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -15,10 +15,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bn254/fr/poseidon2/doc.go b/ecc/bn254/fr/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/ecc/bn254/fr/poseidon2/doc.go @@ -0,0 +1,17 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index 505764d8b3..fba5ae40c5 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -15,10 +15,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bw6-633/fr/poseidon2/doc.go b/ecc/bw6-633/fr/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/ecc/bw6-633/fr/poseidon2/doc.go @@ -0,0 +1,17 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index 7da6f2bb4f..3c8c9ee8b0 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -15,10 +15,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bw6-761/fr/poseidon2/doc.go b/ecc/bw6-761/fr/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/ecc/bw6-761/fr/poseidon2/doc.go @@ -0,0 +1,17 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index 797067e314..e95b3c7648 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -15,10 +15,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/internal/generator/crypto/hash/poseidon2/generate.go b/internal/generator/crypto/hash/poseidon2/generate.go index 2c05bb08eb..0267fe2d83 100644 --- a/internal/generator/crypto/hash/poseidon2/generate.go +++ b/internal/generator/crypto/hash/poseidon2/generate.go @@ -11,6 +11,7 @@ func Generate(conf config.Curve, baseDir string, bgen *bavard.BatchGenerator) er conf.Package = "poseidon2" entries := []bavard.Entry{ + {File: filepath.Join(baseDir, "doc.go"), Templates: []string{"doc.go.tmpl"}}, {File: filepath.Join(baseDir, "poseidon2.go"), Templates: []string{"poseidon2.go.tmpl"}}, {File: filepath.Join(baseDir, "poseidon2_test.go"), Templates: []string{"poseidon2.test.go.tmpl"}}, } diff --git a/internal/generator/crypto/hash/poseidon2/template/doc.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/doc.go.tmpl new file mode 100644 index 0000000000..3897419232 --- /dev/null +++ b/internal/generator/crypto/hash/poseidon2/template/doc.go.tmpl @@ -0,0 +1,12 @@ +// Package poseidon2 implements the Poseidon2 permutation +// +// Poseidon2 permutation is a cryptographic permutation for algebraic hashes. +// See the [original paper] by Grassi, Khovratovich and Schofnegger for the full details. +// +// This implementation is based on the [reference implementation] from +// HorizenLabs. See the [specifications] for parameter choices. +// +// [reference implementation]: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs +// [specifications]: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf +// [original paper]: https://eprint.iacr.org/2023/323.pdf +package poseidon2 \ No newline at end of file diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index 4c7b85306e..155f3e2236 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -8,10 +8,6 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) -// reference implementation: https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs -// specifications: https://github.com/argumentcomputer/neptune/blob/main/spec/poseidon_spec.pdf -// original paper: https://eprint.iacr.org/2023/323.pdf - // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) From 58013e4e8d2542a89a3073174cc57d81f9d6131f Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 17:59:08 +0000 Subject: [PATCH 13/21] feat: disable cases except T=2,3 --- .../hash/poseidon2/template/poseidon2.go.tmpl | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index 155f3e2236..3973b96bda 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -19,9 +19,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -36,6 +33,9 @@ type Hash struct { // NewHash returns a new hash instance allowing to apply the poseidon2 permutation func NewHash(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -179,27 +179,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } From 37e19890bdfc3e0d4c669386b5c98c8f4ec4e9f1 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 17:59:48 +0000 Subject: [PATCH 14/21] refactor: rename hash to permutation --- .../crypto/hash/poseidon2/template/poseidon2.go.tmpl | 4 ++-- .../crypto/hash/poseidon2/template/poseidon2.test.go.tmpl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index 3973b96bda..0c7d5f2cca 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -31,8 +31,8 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { if t < 2 || t > 3 { panic("only t=2,3 is supported") } diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl index 809fc11c67..11379c82cb 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl @@ -27,7 +27,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -48,7 +48,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() From 7f7fb1c90abeebd7f70b78b73f6ae84d31c07212 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 18:00:40 +0000 Subject: [PATCH 15/21] chore: go generate --- ecc/bls12-377/fr/poseidon2/poseidon2.go | 36 +++++++++++--------- ecc/bls12-377/fr/poseidon2/poseidon2_test.go | 4 +-- ecc/bls12-381/fr/poseidon2/poseidon2.go | 36 +++++++++++--------- ecc/bls12-381/fr/poseidon2/poseidon2_test.go | 4 +-- ecc/bls24-315/fr/poseidon2/poseidon2.go | 36 +++++++++++--------- ecc/bls24-315/fr/poseidon2/poseidon2_test.go | 4 +-- ecc/bls24-317/fr/poseidon2/poseidon2.go | 36 +++++++++++--------- ecc/bls24-317/fr/poseidon2/poseidon2_test.go | 4 +-- ecc/bn254/fr/poseidon2/poseidon2.go | 36 +++++++++++--------- ecc/bn254/fr/poseidon2/poseidon2_test.go | 4 +-- ecc/bw6-633/fr/poseidon2/poseidon2.go | 36 +++++++++++--------- ecc/bw6-633/fr/poseidon2/poseidon2_test.go | 4 +-- ecc/bw6-761/fr/poseidon2/poseidon2.go | 36 +++++++++++--------- ecc/bw6-761/fr/poseidon2/poseidon2_test.go | 4 +-- 14 files changed, 147 insertions(+), 133 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index f02e0b658b..6de5bcdb40 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -26,9 +26,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -41,8 +38,11 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -175,27 +175,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2_test.go b/ecc/bls12-377/fr/poseidon2/poseidon2_test.go index c123dee64d..f06925afbf 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2_test.go @@ -34,7 +34,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +55,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index 964026a0fb..23d9a3770c 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -26,9 +26,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -41,8 +38,11 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -173,27 +173,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2_test.go b/ecc/bls12-381/fr/poseidon2/poseidon2_test.go index 4851c090ee..9a505669f5 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2_test.go @@ -34,7 +34,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +55,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index bb177f9522..0f8062e9cc 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -26,9 +26,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -41,8 +38,11 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -173,27 +173,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2_test.go b/ecc/bls24-315/fr/poseidon2/poseidon2_test.go index 41da3d5728..d4c8cbbdee 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2_test.go @@ -34,7 +34,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +55,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 9083b50f38..78cda7a396 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -26,9 +26,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -41,8 +38,11 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -174,27 +174,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2_test.go b/ecc/bls24-317/fr/poseidon2/poseidon2_test.go index eb107b7a64..7ccf7952a0 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2_test.go @@ -34,7 +34,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +55,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index fba5ae40c5..45b2761e70 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -26,9 +26,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -41,8 +38,11 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -173,27 +173,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } diff --git a/ecc/bn254/fr/poseidon2/poseidon2_test.go b/ecc/bn254/fr/poseidon2/poseidon2_test.go index 712f04accf..4085194357 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2_test.go +++ b/ecc/bn254/fr/poseidon2/poseidon2_test.go @@ -34,7 +34,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +55,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index 3c8c9ee8b0..f2d1ff814c 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -26,9 +26,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -41,8 +38,11 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -173,27 +173,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2_test.go b/ecc/bw6-633/fr/poseidon2/poseidon2_test.go index 3effc4fc52..0022d8b80f 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2_test.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2_test.go @@ -34,7 +34,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +55,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index e95b3c7648..8045412d2c 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -26,9 +26,6 @@ type parameters struct { // number of partial rounds rP int - // diagonal elements of the internal matrices, minus one - DiagInternalMatrices []fr.Element - // round keys roundKeys [][]fr.Element } @@ -41,8 +38,11 @@ type Hash struct { params parameters } -// NewHash returns a new hash instance allowing to apply the poseidon2 permutation -func NewHash(t, rf, rp int, seed string) Hash { +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int, seed string) Hash { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } params := parameters{t: t, rF: rf, rP: rp} params.roundKeys = InitRC(seed, rf, rp, t) res := Hash{params: params} @@ -173,27 +173,29 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Hash) matMulInternalInPlace(input []fr.Element) { - if h.params.t == 2 { + switch h.params.t { + case 2: var sum fr.Element sum.Add(&input[0], &input[1]) input[0].Add(&input[0], &sum) input[1].Double(&input[1]).Add(&input[1], &sum) - } else if h.params.t == 3 { + case 3: var sum fr.Element sum.Add(&input[0], &input[1]).Add(&sum, &input[2]) input[0].Add(&input[0], &sum) input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &sum) - } else { - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.t; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.t; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) - } + default: + // var sum fr.Element + // sum.Set(&input[0]) + // for i := 1; i < h.params.t; i++ { + // sum.Add(&sum, &input[i]) + // } + // for i := 0; i < h.params.t; i++ { + // input[i].Mul(&input[i], &h.params.diagInternalMatrices[i]). + // Add(&input[i], &sum) + // } + panic("only T=2,3 is supported") } } diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2_test.go b/ecc/bw6-761/fr/poseidon2/poseidon2_test.go index f615ff8dfc..2e377b6c04 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2_test.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2_test.go @@ -34,7 +34,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewHash(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56, "seed") var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +55,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewHash(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56, "seed") var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() From fad1a4ec2f0c3c631f2c1d78ede43ec96e901c7c Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 18:01:46 +0000 Subject: [PATCH 16/21] fix: spell error --- hash/doc.go | 2 +- hash/interface.go | 6 +++--- hash/merkle-damgard.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hash/doc.go b/hash/doc.go index 9b86cacd6d..04d4fdfe55 100644 --- a/hash/doc.go +++ b/hash/doc.go @@ -10,7 +10,7 @@ // See [Importing hash functions] below for more information. // // This package also provides a construction for a generic hash function from -// the compression primitive. See the interface [Compresser] and the +// the compression primitive. See the interface [Compressor] and the // corresponding initialization function [NewMerkleDamgardHasher]. // // # Importing hash functions diff --git a/hash/interface.go b/hash/interface.go index c6670f3dc9..34422ede6e 100644 --- a/hash/interface.go +++ b/hash/interface.go @@ -15,7 +15,7 @@ type StateStorer interface { SetState(state []byte) error } -// Compresser is a 2-1 one-way function. It takes two inputs and compresses +// Compressor is a 2-1 one-way function. It takes two inputs and compresses // them into one output. The inputs and outputs are all of the same size, which // is the block size. See [BlockSize]. // @@ -23,9 +23,9 @@ type StateStorer interface { // be unique for different inputs. The output is guaranteed to be the same for // the same inputs. // -// The Compresser is used in the Merkle-Damgard construction to build a hash +// The Compressor is used in the Merkle-Damgard construction to build a hash // function. -type Compresser interface { +type Compressor interface { // Compress compresses the two inputs into one output. All the inputs and // outputs are of the same size, which is the block size. See [BlockSize]. Compress(left []byte, right []byte) (compressed []byte, err error) diff --git a/hash/merkle-damgard.go b/hash/merkle-damgard.go index 08c79ddffc..2ee1215eae 100644 --- a/hash/merkle-damgard.go +++ b/hash/merkle-damgard.go @@ -3,7 +3,7 @@ package hash type merkleDamgardHasher struct { state []byte iv []byte - f Compresser + f Compressor } // Write implements hash.Write @@ -64,7 +64,7 @@ func (h *merkleDamgardHasher) SetState(state []byte) error { // The value initialState is provided as initial input to the compression // function. Its preimage should not be known and thus it should be generated // using a deterministic method. -func NewMerkleDamgardHasher(f Compresser, initialState []byte) StateStorer { +func NewMerkleDamgardHasher(f Compressor, initialState []byte) StateStorer { return &merkleDamgardHasher{ state: initialState, iv: initialState, From 6f5a265ddb7c55edc3ba1a8fa310c045cb613750 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 22:52:37 +0000 Subject: [PATCH 17/21] refactor: make sbox degree constant --- ecc/bls12-377/fr/poseidon2/poseidon2.go | 11 +++++++++++ ecc/bls12-381/fr/poseidon2/poseidon2.go | 11 +++++++++++ ecc/bls24-315/fr/poseidon2/poseidon2.go | 11 +++++++++++ ecc/bls24-317/fr/poseidon2/poseidon2.go | 11 +++++++++++ ecc/bn254/fr/poseidon2/poseidon2.go | 11 +++++++++++ ecc/bw6-633/fr/poseidon2/poseidon2.go | 11 +++++++++++ ecc/bw6-761/fr/poseidon2/poseidon2.go | 11 +++++++++++ .../hash/poseidon2/template/poseidon2.go.tmpl | 17 +++++++++++++++++ 8 files changed, 94 insertions(+) diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 6de5bcdb40..3614237b35 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -15,6 +15,17 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + d = 17 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index 23d9a3770c..20ba0ff228 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -15,6 +15,17 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + d = 5 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index 0f8062e9cc..a337ae955d 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -15,6 +15,17 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + d = 5 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 78cda7a396..8b512eeb2c 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -15,6 +15,17 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + d = 7 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index 45b2761e70..d1a76eb0b2 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -15,6 +15,17 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + d = 5 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index f2d1ff814c..9a1ccf08d4 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -15,6 +15,17 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + d = 5 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index 8045412d2c..dc3bf8e1a9 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -15,6 +15,17 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + d = 5 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index 0c7d5f2cca..a4f617a47c 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -8,6 +8,23 @@ var ( ErrInvalidSizebuffer = errors.New("the size of the input should match the size of the hash buffer") ) +const ( + // d is the degree of the sBox + {{- if eq .Name "bls12-377" }} + d = 17 + {{ else if eq .Name "bls24-317" }} + d = 7 + {{ else }} + d = 5 + {{ end -}} +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + // parameters describing the poseidon2 implementation type parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) From e57cabb6e8174531030c9c6d494be66609173284 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 23:45:30 +0000 Subject: [PATCH 18/21] refactor: Poseidon package --- ecc/bls12-377/fr/poseidon2/poseidon2.go | 147 ++++++++++------- .../hash/poseidon2/template/poseidon2.go.tmpl | 153 +++++++++++------- .../poseidon2/template/poseidon2.test.go.tmpl | 6 +- 3 files changed, 192 insertions(+), 114 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 3614237b35..4e2904eb4a 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -7,6 +7,8 @@ package poseidon2 import ( "errors" + "fmt" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "golang.org/x/crypto/sha3" ) @@ -26,43 +28,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-BLS12-377[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -71,37 +79,66 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf / 2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp + rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters } +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res +} + + // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) @@ -123,7 +160,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -147,34 +184,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -185,8 +222,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -213,45 +250,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -262,8 +299,8 @@ func (h *Hash) Permutation(input []fr.Element) error { // Compress applies the permutation on left and right and returns the right lane // of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index a4f617a47c..f5958ccd27 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -1,6 +1,9 @@ import ( "errors" + "fmt" + "golang.org/x/crypto/sha3" + "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr" ) @@ -25,43 +28,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-{{.EnumID}}[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -70,37 +79,66 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf/2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp+rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters } +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res +} + + // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) {{ if eq .Name "bls12-377" }} @@ -133,7 +171,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -157,34 +195,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -195,8 +233,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -223,45 +261,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -271,9 +309,10 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element @@ -289,4 +328,4 @@ func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { } res := x[1].Bytes() return res[:], nil -} \ No newline at end of file +} diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl index 11379c82cb..601aef3988 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.test.go.tmpl @@ -5,6 +5,8 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") + var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -27,7 +29,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -48,7 +50,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() From fd958f16870bf1e1fe81a7181df3a69b3e921ae9 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 23:46:15 +0000 Subject: [PATCH 19/21] chore: go generate --- ecc/bls12-377/fr/poseidon2/poseidon2.go | 9 +- ecc/bls12-377/fr/poseidon2/poseidon2_test.go | 5 +- ecc/bls12-381/fr/poseidon2/poseidon2.go | 152 ++++++++++++------- ecc/bls12-381/fr/poseidon2/poseidon2_test.go | 5 +- ecc/bls24-315/fr/poseidon2/poseidon2.go | 152 ++++++++++++------- ecc/bls24-315/fr/poseidon2/poseidon2_test.go | 5 +- ecc/bls24-317/fr/poseidon2/poseidon2.go | 152 ++++++++++++------- ecc/bls24-317/fr/poseidon2/poseidon2_test.go | 5 +- ecc/bn254/fr/poseidon2/poseidon2.go | 152 ++++++++++++------- ecc/bn254/fr/poseidon2/poseidon2_test.go | 5 +- ecc/bw6-633/fr/poseidon2/poseidon2.go | 152 ++++++++++++------- ecc/bw6-633/fr/poseidon2/poseidon2_test.go | 5 +- ecc/bw6-761/fr/poseidon2/poseidon2.go | 152 ++++++++++++------- ecc/bw6-761/fr/poseidon2/poseidon2_test.go | 5 +- 14 files changed, 596 insertions(+), 360 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 4e2904eb4a..0f8bd70b60 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -9,8 +9,9 @@ import ( "errors" "fmt" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "golang.org/x/crypto/sha3" + + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" ) var ( @@ -65,7 +66,7 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string } func (p *Parameters) seedString() string { - return fmt.Sprintf("Poseidon2-BLS12-377[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) + return fmt.Sprintf("Poseidon2-BLS12_377[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } // initRC initiate round keys. Only one entry is non zero for the internal @@ -136,7 +137,6 @@ func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { return res } - // sBox applies the sBox on buffer[index] func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element @@ -298,7 +298,8 @@ func (h *Permutation) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2_test.go b/ecc/bls12-377/fr/poseidon2/poseidon2_test.go index f06925afbf..a62c7af9a3 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2_test.go @@ -12,6 +12,7 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -34,7 +35,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +56,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index 20ba0ff228..12c2145fc8 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -7,8 +7,11 @@ package poseidon2 import ( "errors" - "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "fmt" + "golang.org/x/crypto/sha3" + + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" ) var ( @@ -26,43 +29,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-BLS12_381[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -71,37 +80,65 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf / 2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp + rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters +} + +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res } // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) @@ -121,7 +158,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -145,34 +182,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -183,8 +220,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -211,45 +248,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -259,9 +296,10 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2_test.go b/ecc/bls12-381/fr/poseidon2/poseidon2_test.go index 9a505669f5..5f38fe725b 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2_test.go @@ -12,6 +12,7 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -34,7 +35,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +56,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index a337ae955d..45f03958f1 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -7,8 +7,11 @@ package poseidon2 import ( "errors" - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + "fmt" + "golang.org/x/crypto/sha3" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" ) var ( @@ -26,43 +29,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-BLS24_315[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -71,37 +80,65 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf / 2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp + rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters +} + +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res } // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) @@ -121,7 +158,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -145,34 +182,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -183,8 +220,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -211,45 +248,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -259,9 +296,10 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2_test.go b/ecc/bls24-315/fr/poseidon2/poseidon2_test.go index d4c8cbbdee..37d3b61052 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2_test.go @@ -12,6 +12,7 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -34,7 +35,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +56,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 8b512eeb2c..87d84f4b09 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -7,8 +7,11 @@ package poseidon2 import ( "errors" - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" + "fmt" + "golang.org/x/crypto/sha3" + + "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" ) var ( @@ -26,43 +29,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-BLS24_317[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -71,37 +80,65 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf / 2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp + rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters +} + +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res } // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) @@ -122,7 +159,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -146,34 +183,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -184,8 +221,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -212,45 +249,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -260,9 +297,10 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2_test.go b/ecc/bls24-317/fr/poseidon2/poseidon2_test.go index 7ccf7952a0..5e1e8082c3 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2_test.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2_test.go @@ -12,6 +12,7 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -34,7 +35,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +56,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index d1a76eb0b2..c9505d2907 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -7,8 +7,11 @@ package poseidon2 import ( "errors" - "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "fmt" + "golang.org/x/crypto/sha3" + + "github.com/consensys/gnark-crypto/ecc/bn254/fr" ) var ( @@ -26,43 +29,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-BN254[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -71,37 +80,65 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf / 2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp + rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters +} + +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res } // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) @@ -121,7 +158,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -145,34 +182,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -183,8 +220,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -211,45 +248,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -259,9 +296,10 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bn254/fr/poseidon2/poseidon2_test.go b/ecc/bn254/fr/poseidon2/poseidon2_test.go index 4085194357..2f056ef0b9 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2_test.go +++ b/ecc/bn254/fr/poseidon2/poseidon2_test.go @@ -12,6 +12,7 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -34,7 +35,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +56,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index 9a1ccf08d4..73e32a1b24 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -7,8 +7,11 @@ package poseidon2 import ( "errors" - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" + "fmt" + "golang.org/x/crypto/sha3" + + "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" ) var ( @@ -26,43 +29,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-BW6_633[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -71,37 +80,65 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf / 2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp + rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters +} + +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res } // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) @@ -121,7 +158,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -145,34 +182,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -183,8 +220,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -211,45 +248,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -259,9 +296,10 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2_test.go b/ecc/bw6-633/fr/poseidon2/poseidon2_test.go index 0022d8b80f..c7a2ddfd97 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2_test.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2_test.go @@ -12,6 +12,7 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -34,7 +35,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +56,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index dc3bf8e1a9..deaa04a5c9 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -7,8 +7,11 @@ package poseidon2 import ( "errors" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "fmt" + "golang.org/x/crypto/sha3" + + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" ) var ( @@ -26,43 +29,49 @@ func DegreeSBox() int { return d } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the Poseidon2 implementation. Use [NewParameters] or +// [NewParametersWithSeed] to initialize a new set of parameters to +// deterministically precompute the round keys. +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - t int + Width int // number of full rounds (even number) - rF int + NbFullRounds int // number of partial rounds - rP int + NbPartialRounds int - // round keys - roundKeys [][]fr.Element + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element } -// Hash stores the buffer of the poseidon2 permutation and provides poseidon2 permutation -// methods on the buffer -type Hash struct { +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the seed which is a digest of the parameters and curve ID. +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + seed := p.seedString() + p.initRC(seed) + return &p +} - // parameters describing the - params parameters +// NewParametersWithSeed returns a new set of parameters for the Poseidon2 permutation. +// After creating the parameters, the round keys are initialized deterministically +// from the given seed. +func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string) *Parameters { + p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} + p.initRC(seed) + return &p } -// NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, seed string) Hash { - if t < 2 || t > 3 { - panic("only t=2,3 is supported") - } - params := parameters{t: t, rF: rf, rP: rp} - params.roundKeys = InitRC(seed, rf, rp, t) - res := Hash{params: params} - return res +func (p *Parameters) seedString() string { + return fmt.Sprintf("Poseidon2-BW6_761[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } -// InitRC initiate round keys. Only one entry is non zero for the internal +// initRC initiate round keys. Only one entry is non zero for the internal // rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func InitRC(seed string, rf, rp, t int) [][]fr.Element { +func (p *Parameters) initRC(seed string) { bseed := ([]byte)(seed) hash := sha3.NewLegacyKeccak256() @@ -71,37 +80,65 @@ func InitRC(seed string, rf, rp, t int) [][]fr.Element { hash.Reset() _, _ = hash.Write(rnd) - roundKeys := make([][]fr.Element, rf+rp) - for i := 0; i < rf/2; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + roundKeys := make([][]fr.Element, p.NbFullRounds+p.NbPartialRounds) + for i := 0; i < p.NbFullRounds/2; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - for i := rf / 2; i < rp+rf/2; i++ { + for i := p.NbFullRounds / 2; i < p.NbPartialRounds+p.NbFullRounds/2; i++ { roundKeys[i] = make([]fr.Element, 1) rnd = hash.Sum(nil) roundKeys[i][0].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } - for i := rp + rf/2; i < rp+rf; i++ { - roundKeys[i] = make([]fr.Element, t) - for j := 0; j < t; j++ { + for i := p.NbPartialRounds + p.NbFullRounds/2; i < p.NbPartialRounds+p.NbFullRounds; i++ { + roundKeys[i] = make([]fr.Element, p.Width) + for j := 0; j < p.Width; j++ { rnd = hash.Sum(nil) roundKeys[i][j].SetBytes(rnd) hash.Reset() _, _ = hash.Write(rnd) } } - return roundKeys + p.RoundKeys = roundKeys +} + +// Permutation stores the buffer of the Poseidon2 permutation and provides +// Poseidon2 permutation methods on the buffer +type Permutation struct { + // parameters describing the instance + params *Parameters +} + +// NewPermutation returns a new Poseidon2 permutation instance. +func NewPermutation(t, rf, rp int) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParameters(t, rf, rp) + res := &Permutation{params: params} + return res +} + +// NewPermutationWithSeed returns a new Poseidon2 permutation instance with a +// given seed. +func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t < 2 || t > 3 { + panic("only t=2,3 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res } // sBox applies the sBox on buffer[index] -func (h *Hash) sBox(index int, input []fr.Element) { +func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) @@ -121,7 +158,7 @@ func (h *Hash) sBox(index int, input []fr.Element) { // (1 1 4 6) // on chunks of 4 elemts on each part of the buffer // see https://eprint.iacr.org/2023/323.pdf appendix B for the addition chain -func (h *Hash) matMulM4InPlace(s []fr.Element) { +func (h *Permutation) matMulM4InPlace(s []fr.Element) { c := len(s) / 4 for i := 0; i < c; i++ { var t0, t1, t2, t3, t4, t5, t6, t7 fr.Element @@ -145,34 +182,34 @@ func (h *Hash) matMulM4InPlace(s []fr.Element) { // // when T=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf -func (h *Hash) matMulExternalInPlace(input []fr.Element) { +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - if h.params.t == 2 { + if h.params.Width == 2 { var tmp fr.Element tmp.Add(&input[0], &input[1]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) - } else if h.params.t == 3 { + } else if h.params.Width == 3 { var tmp fr.Element tmp.Add(&input[0], &input[1]). Add(&tmp, &input[2]) input[0].Add(&tmp, &input[0]) input[1].Add(&tmp, &input[1]) input[2].Add(&tmp, &input[2]) - } else if h.params.t == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]fr.Element, 4) - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0].Add(&tmp[0], &input[4*i]) tmp[1].Add(&tmp[1], &input[4*i+1]) tmp[2].Add(&tmp[2], &input[4*i+2]) tmp[3].Add(&tmp[3], &input[4*i+3]) } - for i := 0; i < h.params.t/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i].Add(&input[4*i], &tmp[0]) input[4*i+1].Add(&input[4*i], &tmp[1]) input[4*i+2].Add(&input[4*i], &tmp[2]) @@ -183,8 +220,8 @@ func (h *Hash) matMulExternalInPlace(input []fr.Element) { // when T=2,3 the matrix are respectibely [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, -func (h *Hash) matMulInternalInPlace(input []fr.Element) { - switch h.params.t { +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { case 2: var sum fr.Element sum.Add(&input[0], &input[1]) @@ -211,45 +248,45 @@ func (h *Hash) matMulInternalInPlace(input []fr.Element) { } // addRoundKeyInPlace adds the round-th key to the buffer -func (h *Hash) addRoundKeyInPlace(round int, input []fr.Element) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i].Add(&input[i], &h.params.roundKeys[round][i]) +func (h *Permutation) addRoundKeyInPlace(round int, input []fr.Element) { + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i].Add(&input[i], &h.params.RoundKeys[round][i]) } } -func (h *Hash) BlockSize() int { +func (h *Permutation) BlockSize() int { return fr.Bytes } // Permutation applies the permutation on input, and stores the result in input. -func (h *Hash) Permutation(input []fr.Element) error { - if len(input) != h.params.t { +func (h *Permutation) Permutation(input []fr.Element) error { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.rF / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.rP; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.rP; i < h.params.rF+h.params.rP; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.t; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -259,9 +296,10 @@ func (h *Hash) Permutation(input []fr.Element) error { } // Compress applies the permutation on left and right and returns the right lane -// of the result. -func (h *Hash) Compress(left []byte, right []byte) ([]byte, error) { - if h.params.t != 2 { +// of the result. Panics if the permutation instance is not initialized with a +// width of 2. +func (h *Permutation) Compress(left []byte, right []byte) ([]byte, error) { + if h.params.Width != 2 { return nil, errors.New("need a 2-1 function") } var x [2]fr.Element diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2_test.go b/ecc/bw6-761/fr/poseidon2/poseidon2_test.go index 2e377b6c04..59e8706952 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2_test.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2_test.go @@ -12,6 +12,7 @@ import ( ) func TestExternalMatrix(t *testing.T) { + t.Skip("skipping test - it is initialized for width=4 for which we don't have the diagonal matrix") var expected [4][4]fr.Element expected[0][0].SetUint64(5) @@ -34,7 +35,7 @@ func TestExternalMatrix(t *testing.T) { expected[3][2].SetUint64(7) expected[3][3].SetUint64(6) - h := NewPermutation(4, 8, 56, "seed") + h := NewPermutation(4, 8, 56) var tmp [4]fr.Element for i := 0; i < 4; i++ { for j := 0; j < 4; j++ { @@ -55,7 +56,7 @@ func TestExternalMatrix(t *testing.T) { } func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56, "seed") + h := NewPermutation(3, 8, 56) var tmp [3]fr.Element tmp[0].SetRandom() tmp[1].SetRandom() From fdbe62487ab0916c1cc27adabc775347090ccc61 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Tue, 4 Feb 2025 23:54:37 +0000 Subject: [PATCH 20/21] refactor: make parameter representation public --- ecc/bls12-377/fr/poseidon2/poseidon2.go | 6 ++++-- ecc/bls12-381/fr/poseidon2/poseidon2.go | 6 ++++-- ecc/bls24-315/fr/poseidon2/poseidon2.go | 6 ++++-- ecc/bls24-317/fr/poseidon2/poseidon2.go | 6 ++++-- ecc/bn254/fr/poseidon2/poseidon2.go | 6 ++++-- ecc/bw6-633/fr/poseidon2/poseidon2.go | 6 ++++-- ecc/bw6-761/fr/poseidon2/poseidon2.go | 6 ++++-- .../crypto/hash/poseidon2/template/poseidon2.go.tmpl | 6 ++++-- 8 files changed, 32 insertions(+), 16 deletions(-) diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 0f8bd70b60..a4f8ef3970 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -51,7 +51,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -65,7 +65,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-BLS12_377[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index 12c2145fc8..cc4229c02c 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -51,7 +51,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -65,7 +65,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-BLS12_381[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index 45f03958f1..a6f5485363 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -51,7 +51,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -65,7 +65,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-BLS24_315[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 87d84f4b09..bdb6b72a64 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -51,7 +51,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -65,7 +65,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-BLS24_317[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index c9505d2907..e75614ff00 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -51,7 +51,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -65,7 +65,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-BN254[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index 73e32a1b24..4d432e3aa3 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -51,7 +51,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -65,7 +65,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-BW6_633[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index deaa04a5c9..0f70e03d75 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -51,7 +51,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -65,7 +65,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-BW6_761[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index f5958ccd27..7134941c45 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -50,7 +50,7 @@ type Parameters struct { // from the seed which is a digest of the parameters and curve ID. func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} - seed := p.seedString() + seed := p.String() p.initRC(seed) return &p } @@ -64,7 +64,9 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed string return &p } -func (p *Parameters) seedString() string { +// String returns a string representation of the parameters. It is unique for +// specific parameters and curve. +func (p *Parameters) String() string { return fmt.Sprintf("Poseidon2-{{.EnumID}}[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) } From ba65635a5f68a36ad8bccdcea5687ee6bbe014dc Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Wed, 5 Feb 2025 00:00:59 +0000 Subject: [PATCH 21/21] refactor: move gates to separate package --- .../fr/poseidon2/gkrgates/gkrgates.go | 193 +++++++++++++++++ ecc/bls12-377/fr/poseidon2/hash.go | 203 ++---------------- 2 files changed, 206 insertions(+), 190 deletions(-) create mode 100644 ecc/bls12-377/fr/poseidon2/gkrgates/gkrgates.go diff --git a/ecc/bls12-377/fr/poseidon2/gkrgates/gkrgates.go b/ecc/bls12-377/fr/poseidon2/gkrgates/gkrgates.go new file mode 100644 index 0000000000..9785856185 --- /dev/null +++ b/ecc/bls12-377/fr/poseidon2/gkrgates/gkrgates.go @@ -0,0 +1,193 @@ +// Package gkrgates implements the Poseidon2 permutation gate for GKR +// +// This implementation is based on the [poseidon2] package, but exposes the +// primitives as gates for inclusion in GKR circuits. + +// TODO(@Tabaie @ThomasPiellard) generify once Poseidon2 parameters are known for all curves +package gkrgates + +import ( + "fmt" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/gkr" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" +) + +// The GKR gates needed for proving Poseidon2 permutations + +// extKeySBoxGate applies the external matrix mul, then adds the round key, then applies the sBox +// because of its symmetry, we don't need to define distinct x1 and x2 versions of it +type extKeySBoxGate struct { + roundKey fr.Element +} + +func (g *extKeySBoxGate) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + + x[0]. + Double(&x[0]). + Add(&x[0], &x[1]). + Add(&x[0], &g.roundKey) + return sBox2(x[0]) +} + +func (g *extKeySBoxGate) Degree() int { + return poseidon2.DegreeSBox() +} + +// for x1, the partial round gates are identical to full round gates +// for x2, the partial round gates are just a linear combination +// TODO @Tabaie eliminate the x2 partial round gates and have the x1 gates depend on i - rf/2 or so previous x1's + +// extGate2 applies the external matrix mul, outputting the second element of the result +type extGate2 struct{} + +func (extGate2) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[1]. + Double(&x[1]). + Add(&x[1], &x[0]) + return x[1] +} + +func (g extGate2) Degree() int { + return 1 +} + +// intGate2 applies the internal matrix mul, returning the second element +type intGate2 struct { +} + +func (g intGate2) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[0].Add(&x[0], &x[1]) + x[1]. + Double(&x[1]). + Add(&x[1], &x[0]) + return x[1] +} + +func (g intGate2) Degree() int { + return 1 +} + +// intKeySBoxGateFr applies the second row of internal matrix mul, then adds the round key, then applies the sBox +type intKeySBoxGate2 struct { + roundKey fr.Element +} + +func (g *intKeySBoxGate2) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[0].Add(&x[0], &x[1]) + x[1]. + Double(&x[1]). + Add(&x[1], &x[0]). + Add(&x[1], &g.roundKey) + + return sBox2(x[1]) +} + +func (g *intKeySBoxGate2) Degree() int { + return poseidon2.DegreeSBox() +} + +type extGate struct{} + +func (g extGate) Evaluate(x ...fr.Element) fr.Element { + if len(x) != 2 { + panic("expected 2 inputs") + } + x[0]. + Double(&x[0]). + Add(&x[0], &x[1]) + return x[0] +} + +func (g extGate) Degree() int { + return 1 +} + +// sBox2 is Hash.sBox for t=2 +func sBox2(x fr.Element) fr.Element { + var y fr.Element + y.Square(&x).Square(&y).Square(&y).Square(&y).Mul(&x, &y) + return y +} + +var initOnce sync.Once + +// RegisterGkrGates registers the Poseidon2 permutation gates for GKR +func RegisterGkrGates() { + initOnce.Do( + func() { + p := poseidon2.NewDefaultParameters() + halfRf := p.NbFullRounds / 2 + + gateNameX := func(i int) string { + return fmt.Sprintf("x-round=%d%s", i, p.String()) + } + gateNameY := func(i int) string { + return fmt.Sprintf("y-round=%d%s", i, p.String()) + } + + fullRound := func(i int) { + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ + roundKey: p.RoundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = &extKeySBoxGate{ + roundKey: p.RoundKeys[i][1], + } + } + + for i := range halfRf { + fullRound(i) + } + + { // i = halfRf: first partial round + i := halfRf + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ + roundKey: p.RoundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = extGate2{} + } + + for i := halfRf + 1; i < halfRf+p.NbPartialRounds; i++ { + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ // for x1, intKeySBox is identical to extKeySBox + roundKey: p.RoundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = intGate2{} + + } + + { + i := halfRf + p.NbPartialRounds + gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ + roundKey: p.RoundKeys[i][0], + } + + gkr.Gates[gateNameY(i)] = &intKeySBoxGate2{ + roundKey: p.RoundKeys[i][1], + } + } + + for i := halfRf + p.NbPartialRounds + 1; i < p.NbPartialRounds+p.NbFullRounds; i++ { + fullRound(i) + } + + gkr.Gates[gateNameY(p.NbPartialRounds+p.NbFullRounds)] = extGate{} + }, + ) +} diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 80562c5d77..c08cfcdb62 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -1,208 +1,31 @@ package poseidon2 import ( - "fmt" "hash" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/gkr" gnarkHash "github.com/consensys/gnark-crypto/hash" ) -// NewPoseidon2 returns a Poseidon2 hasher -// TODO @Tabaie @ThomasPiellard Generify once Poseidon2 parameters are known for all curves -func NewPoseidon2() gnarkHash.StateStorer { +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + // TODO @Tabaie @ThomasPiellard Generify once Poseidon2 parameters are known for all curves return gnarkHash.NewMerkleDamgardHasher( - &Hash{params: params()}, make([]byte, fr.Bytes)) + &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) } -const ( - seed = "Poseidon2 hash for BLS12_377 with t=2, rF=6, rP=26, d=17" - d = 17 -) - -func params() parameters { - return parameters{ - t: 2, - rF: 6, - rP: 26, - roundKeys: InitRC(seed, 6, 26, 2), - } +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// The default parameters are: +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 26 +func NewDefaultParameters() *Parameters { + return NewParameters(2, 6, 26) } func init() { gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS12_377, func() hash.Hash { - return NewPoseidon2() + return NewMerkleDamgardHasher() }) } - -// The GKR gates needed for proving Poseidon2 permutations -// TODO @Tabaie @ThomasPiellard generify once Poseidon2 parameters are known for all curves - -// extKeySBoxGate applies the external matrix mul, then adds the round key, then applies the sBox -// because of its symmetry, we don't need to define distinct x1 and x2 versions of it -type extKeySBoxGate struct { - roundKey fr.Element -} - -func (g *extKeySBoxGate) Evaluate(x ...fr.Element) fr.Element { - if len(x) != 2 { - panic("expected 2 inputs") - } - - x[0]. - Double(&x[0]). - Add(&x[0], &x[1]). - Add(&x[0], &g.roundKey) - return sBox2(x[0]) -} - -func (g *extKeySBoxGate) Degree() int { - return d -} - -// for x1, the partial round gates are identical to full round gates -// for x2, the partial round gates are just a linear combination -// TODO @Tabaie eliminate the x2 partial round gates and have the x1 gates depend on i - rf/2 or so previous x1's - -// extGate2 applies the external matrix mul, outputting the second element of the result -type extGate2 struct{} - -func (extGate2) Evaluate(x ...fr.Element) fr.Element { - if len(x) != 2 { - panic("expected 2 inputs") - } - x[1]. - Double(&x[1]). - Add(&x[1], &x[0]) - return x[1] -} - -func (g extGate2) Degree() int { - return 1 -} - -// intGate2 applies the internal matrix mul, returning the second element -type intGate2 struct { -} - -func (g intGate2) Evaluate(x ...fr.Element) fr.Element { - if len(x) != 2 { - panic("expected 2 inputs") - } - x[0].Add(&x[0], &x[1]) - x[1]. - Double(&x[1]). - Add(&x[1], &x[0]) - return x[1] -} - -func (g intGate2) Degree() int { - return 1 -} - -// intKeySBoxGateFr applies the second row of internal matrix mul, then adds the round key, then applies the sBox -type intKeySBoxGate2 struct { - roundKey fr.Element -} - -func (g *intKeySBoxGate2) Evaluate(x ...fr.Element) fr.Element { - if len(x) != 2 { - panic("expected 2 inputs") - } - x[0].Add(&x[0], &x[1]) - x[1]. - Double(&x[1]). - Add(&x[1], &x[0]). - Add(&x[1], &g.roundKey) - - return sBox2(x[1]) -} - -func (g *intKeySBoxGate2) Degree() int { - return d -} - -type extGate struct{} - -func (g extGate) Evaluate(x ...fr.Element) fr.Element { - if len(x) != 2 { - panic("expected 2 inputs") - } - x[0]. - Double(&x[0]). - Add(&x[0], &x[1]) - return x[0] -} - -func (g extGate) Degree() int { - return 1 -} - -// sBox2 is Hash.sBox for t=2 -func sBox2(x fr.Element) fr.Element { - var y fr.Element - y.Square(&x).Square(&y).Square(&y).Square(&y).Mul(&x, &y) - return y -} - -func DefineGkrGates() { - p := params() - halfRf := p.rF / 2 - - gateNameX := func(i int) string { - return fmt.Sprintf("x-round=%d%s", i, seed) - } - gateNameY := func(i int) string { - return fmt.Sprintf("y-round=%d%s", i, seed) - } - - fullRound := func(i int) { - gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ - roundKey: p.roundKeys[i][0], - } - - gkr.Gates[gateNameY(i)] = &extKeySBoxGate{ - roundKey: p.roundKeys[i][1], - } - } - - for i := range halfRf { - fullRound(i) - } - - { // i = halfRf: first partial round - i := halfRf - gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ - roundKey: p.roundKeys[i][0], - } - - gkr.Gates[gateNameY(i)] = extGate2{} - } - - for i := halfRf + 1; i < halfRf+p.rP; i++ { - gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ // for x1, intKeySBox is identical to extKeySBox - roundKey: p.roundKeys[i][0], - } - - gkr.Gates[gateNameY(i)] = intGate2{} - - } - - { - i := halfRf + p.rP - gkr.Gates[gateNameX(i)] = &extKeySBoxGate{ - roundKey: p.roundKeys[i][0], - } - - gkr.Gates[gateNameY(i)] = &intKeySBoxGate2{ - roundKey: p.roundKeys[i][1], - } - } - - for i := halfRf + p.rP + 1; i < p.rP+p.rF; i++ { - fullRound(i) - } - - gkr.Gates[gateNameY(p.rP+p.rF)] = extGate{} -}