From e8e4c35a8966f9e5ffdf320abe5cb48bfd4cfb1c Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 7 Feb 2025 13:28:35 -0500 Subject: [PATCH 01/20] feat: poseidon2 for koala-bear and baby-bear --- field/babybear/poseidon2/doc.go | 15 + field/babybear/poseidon2/hash.go | 31 ++ field/babybear/poseidon2/poseidon2.go | 320 ++++++++++++++++++++ field/babybear/poseidon2/poseidon2_test.go | 66 ++++ field/koalabear/poseidon2/doc.go | 15 + field/koalabear/poseidon2/hash.go | 31 ++ field/koalabear/poseidon2/poseidon2.go | 320 ++++++++++++++++++++ field/koalabear/poseidon2/poseidon2_test.go | 66 ++++ hash/hashes.go | 10 + 9 files changed, 874 insertions(+) create mode 100644 field/babybear/poseidon2/doc.go create mode 100644 field/babybear/poseidon2/hash.go create mode 100644 field/babybear/poseidon2/poseidon2.go create mode 100644 field/babybear/poseidon2/poseidon2_test.go create mode 100644 field/koalabear/poseidon2/doc.go create mode 100644 field/koalabear/poseidon2/hash.go create mode 100644 field/koalabear/poseidon2/poseidon2.go create mode 100644 field/koalabear/poseidon2/poseidon2_test.go diff --git a/field/babybear/poseidon2/doc.go b/field/babybear/poseidon2/doc.go new file mode 100644 index 0000000000..07d7d09033 --- /dev/null +++ b/field/babybear/poseidon2/doc.go @@ -0,0 +1,15 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// 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/field/babybear/poseidon2/hash.go b/field/babybear/poseidon2/hash.go new file mode 100644 index 0000000000..e5753fe96c --- /dev/null +++ b/field/babybear/poseidon2/hash.go @@ -0,0 +1,31 @@ +package poseidon2 + +import ( + "hash" + + fr "github.com/consensys/gnark-crypto/field/babybear" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// 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( + &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// The default parameters are: +// - width: 8 +// - nbFullRounds: 6 +// - nbPartialRounds: 10 +func NewDefaultParameters() *Parameters { + return NewParameters(8, 6, 10) +} + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BABYBEAR, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go new file mode 100644 index 0000000000..84670b84b5 --- /dev/null +++ b/field/babybear/poseidon2/poseidon2.go @@ -0,0 +1,320 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "errors" + "fmt" + + "golang.org/x/crypto/sha3" + + fr "github.com/consensys/gnark-crypto/field/babybear" +) + +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. 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)) + Width int + + // number of full rounds (even number) + NbFullRounds int + + // number of partial rounds + NbPartialRounds int + + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element +} + +// 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.String() + p.initRC(seed) + return &p +} + +// 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 +} + +// 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-BABYBEAR[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 +// rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 +func (p *Parameters) initRC(seed string) { + + bseed := ([]byte)(seed) + hash := sha3.NewLegacyKeccak256() + _, _ = hash.Write(bseed) + rnd := hash.Sum(nil) // pre hash before use + hash.Reset() + _, _ = hash.Write(rnd) + + 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 := 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 := 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) + } + } + 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 *Permutation) sBox(index int, input []fr.Element) { + var tmp fr.Element + tmp.Set(&input[index]) + + // sbox degree is 17 + input[index].Square(&input[index]). + Square(&input[index]). + Square(&input[index]). + Square(&input[index]). + Mul(&input[index], &tmp) + +} + +// matMulM4 computes +// s <- M4*s +// where M4= +// (5 7 1 3) +// (4 6 1 1) +// (1 3 5 7) +// (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 *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 + t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 + t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 + t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 + t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 + t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 + t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 + t6.Add(&t3, &t5) // t3+t4 + t7.Add(&t2, &t4) // t2+t4 + s[4*i].Set(&t6) + s[4*i+1].Set(&t5) + s[4*i+2].Set(&t7) + s[4*i+3].Set(&t4) + } +} + +// 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) +// see https://eprint.iacr.org/2023/323.pdf +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { + + 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.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.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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[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 *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { + 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) + 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) + 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") + } +} + +// addRoundKeyInPlace adds the round-th key to the buffer +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 *Permutation) BlockSize() int { + return fr.Bytes +} + +// Permutation applies the permutation on input, and stores the result in input. +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.NbFullRounds / 2 + for i := 0; i < rf; i++ { + // one round = matMulExternal(sBox_Full(addRoundKey)) + h.addRoundKeyInPlace(i, input) + for j := 0; j < h.params.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + 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.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.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + return nil +} + +// Compress applies the permutation on left and right and returns the right lane +// 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 + + if err := x[0].SetBytesCanonical(left); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(right); 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/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go new file mode 100644 index 0000000000..a002f49759 --- /dev/null +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -0,0 +1,66 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "testing" + + fr "github.com/consensys/gnark-crypto/field/koalabear" +) + +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) + expected[0][1].SetUint64(4) + expected[0][2].SetUint64(1) + expected[0][3].SetUint64(1) + + expected[1][0].SetUint64(7) + expected[1][1].SetUint64(6) + expected[1][2].SetUint64(3) + expected[1][3].SetUint64(1) + + expected[2][0].SetUint64(1) + expected[2][1].SetUint64(1) + expected[2][2].SetUint64(5) + expected[2][3].SetUint64(4) + + expected[3][0].SetUint64(3) + expected[3][1].SetUint64(1) + expected[3][2].SetUint64(7) + expected[3][3].SetUint64(6) + + h := NewPermutation(4, 8, 56) + var tmp [4]fr.Element + for i := 0; i < 4; i++ { + for j := 0; j < 4; j++ { + tmp[j].SetUint64(0) + if i == j { + tmp[j].SetOne() + } + } + // h.Write(tmp[:]) + h.matMulExternalInPlace(tmp[:]) + for j := 0; j < 4; j++ { + if !tmp[j].Equal(&expected[i][j]) { + t.Fatal("error matMul4") + } + } + } + +} + +func BenchmarkPoseidon2(b *testing.B) { + h := NewPermutation(3, 8, 56) + var tmp [3]fr.Element + tmp[0].SetRandom() + tmp[1].SetRandom() + tmp[2].SetRandom() + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} diff --git a/field/koalabear/poseidon2/doc.go b/field/koalabear/poseidon2/doc.go new file mode 100644 index 0000000000..07d7d09033 --- /dev/null +++ b/field/koalabear/poseidon2/doc.go @@ -0,0 +1,15 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// 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/field/koalabear/poseidon2/hash.go b/field/koalabear/poseidon2/hash.go new file mode 100644 index 0000000000..ff0f19cfcd --- /dev/null +++ b/field/koalabear/poseidon2/hash.go @@ -0,0 +1,31 @@ +package poseidon2 + +import ( + "hash" + + fr "github.com/consensys/gnark-crypto/field/koalabear" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// 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( + &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// The default parameters are: +// - width: 8 +// - nbFullRounds: 6 +// - nbPartialRounds: 21 +func NewDefaultParameters() *Parameters { + return NewParameters(8, 6, 21) +} + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_KOALABEAR, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go new file mode 100644 index 0000000000..d76a2d5768 --- /dev/null +++ b/field/koalabear/poseidon2/poseidon2.go @@ -0,0 +1,320 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "errors" + "fmt" + + "golang.org/x/crypto/sha3" + + fr "github.com/consensys/gnark-crypto/field/koalabear" +) + +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 = 3 +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + +// 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)) + Width int + + // number of full rounds (even number) + NbFullRounds int + + // number of partial rounds + NbPartialRounds int + + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element +} + +// 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.String() + p.initRC(seed) + return &p +} + +// 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 +} + +// 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-KOALABEAR[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 +// rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 +func (p *Parameters) initRC(seed string) { + + bseed := ([]byte)(seed) + hash := sha3.NewLegacyKeccak256() + _, _ = hash.Write(bseed) + rnd := hash.Sum(nil) // pre hash before use + hash.Reset() + _, _ = hash.Write(rnd) + + 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 := 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 := 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) + } + } + 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 *Permutation) sBox(index int, input []fr.Element) { + var tmp fr.Element + tmp.Set(&input[index]) + + // sbox degree is 17 + input[index].Square(&input[index]). + Square(&input[index]). + Square(&input[index]). + Square(&input[index]). + Mul(&input[index], &tmp) + +} + +// matMulM4 computes +// s <- M4*s +// where M4= +// (5 7 1 3) +// (4 6 1 1) +// (1 3 5 7) +// (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 *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 + t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 + t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 + t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 + t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 + t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 + t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 + t6.Add(&t3, &t5) // t3+t4 + t7.Add(&t2, &t4) // t2+t4 + s[4*i].Set(&t6) + s[4*i+1].Set(&t5) + s[4*i+2].Set(&t7) + s[4*i+3].Set(&t4) + } +} + +// 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) +// see https://eprint.iacr.org/2023/323.pdf +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { + + 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.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.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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[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 *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { + 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) + 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) + 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") + } +} + +// addRoundKeyInPlace adds the round-th key to the buffer +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 *Permutation) BlockSize() int { + return fr.Bytes +} + +// Permutation applies the permutation on input, and stores the result in input. +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.NbFullRounds / 2 + for i := 0; i < rf; i++ { + // one round = matMulExternal(sBox_Full(addRoundKey)) + h.addRoundKeyInPlace(i, input) + for j := 0; j < h.params.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + 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.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.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + return nil +} + +// Compress applies the permutation on left and right and returns the right lane +// 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 + + if err := x[0].SetBytesCanonical(left); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(right); 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/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go new file mode 100644 index 0000000000..a002f49759 --- /dev/null +++ b/field/koalabear/poseidon2/poseidon2_test.go @@ -0,0 +1,66 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "testing" + + fr "github.com/consensys/gnark-crypto/field/koalabear" +) + +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) + expected[0][1].SetUint64(4) + expected[0][2].SetUint64(1) + expected[0][3].SetUint64(1) + + expected[1][0].SetUint64(7) + expected[1][1].SetUint64(6) + expected[1][2].SetUint64(3) + expected[1][3].SetUint64(1) + + expected[2][0].SetUint64(1) + expected[2][1].SetUint64(1) + expected[2][2].SetUint64(5) + expected[2][3].SetUint64(4) + + expected[3][0].SetUint64(3) + expected[3][1].SetUint64(1) + expected[3][2].SetUint64(7) + expected[3][3].SetUint64(6) + + h := NewPermutation(4, 8, 56) + var tmp [4]fr.Element + for i := 0; i < 4; i++ { + for j := 0; j < 4; j++ { + tmp[j].SetUint64(0) + if i == j { + tmp[j].SetOne() + } + } + // h.Write(tmp[:]) + h.matMulExternalInPlace(tmp[:]) + for j := 0; j < 4; j++ { + if !tmp[j].Equal(&expected[i][j]) { + t.Fatal("error matMul4") + } + } + } + +} + +func BenchmarkPoseidon2(b *testing.B) { + h := NewPermutation(3, 8, 56) + var tmp [3]fr.Element + tmp[0].SetRandom() + tmp[1].SetRandom() + tmp[2].SetRandom() + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} diff --git a/hash/hashes.go b/hash/hashes.go index 82a2992700..07842ee54c 100644 --- a/hash/hashes.go +++ b/hash/hashes.go @@ -42,6 +42,10 @@ const ( MIMC_GRUMPKIN // POSEIDON2_BLS12_377 is the Poseidon2 hash function for the BLS12-377 curve. POSEIDON2_BLS12_377 + // POSEIDON2_KOALABEAR is the Poseidon2 hash function for the KoalaBear field. + POSEIDON2_KOALABEAR + // POSEIDON2_BABYBEAR is the Poseidon2 hash function for the KoalaBear field. + POSEIDON2_BABYBEAR maxHash ) @@ -57,6 +61,8 @@ var digestSize = []uint8{ MIMC_BW6_633: 80, MIMC_GRUMPKIN: 32, POSEIDON2_BLS12_377: 48, + POSEIDON2_KOALABEAR: 4, + POSEIDON2_BABYBEAR: 4, } // New initializes the hash function. This is a convenience function which does @@ -97,6 +103,10 @@ func (m Hash) String() string { return "MIMC_GRUMPKIN" case POSEIDON2_BLS12_377: return "POSEIDON2_BLS12_377" + case POSEIDON2_KOALABEAR: + return "POSEIDON2_KOALABEAR" + case POSEIDON2_BABYBEAR: + return "POSEIDON2_BABYBEAR" default: return "unknown hash function" } From 11970dc4d074f3a310fbab5c57a0671afaab6e19 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 7 Feb 2025 14:22:58 -0500 Subject: [PATCH 02/20] refactor(baby/koala-bear): increase width --- field/babybear/poseidon2/hash.go | 6 +++--- field/koalabear/poseidon2/hash.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/field/babybear/poseidon2/hash.go b/field/babybear/poseidon2/hash.go index e5753fe96c..ec4f8e969f 100644 --- a/field/babybear/poseidon2/hash.go +++ b/field/babybear/poseidon2/hash.go @@ -17,11 +17,11 @@ func NewMerkleDamgardHasher() gnarkHash.StateStorer { // NewParameters returns a new set of parameters for the Poseidon2 permutation. // The default parameters are: -// - width: 8 +// - width: 16 // - nbFullRounds: 6 -// - nbPartialRounds: 10 +// - nbPartialRounds: 12 func NewDefaultParameters() *Parameters { - return NewParameters(8, 6, 10) + return NewParameters(16, 6, 12) } func init() { diff --git a/field/koalabear/poseidon2/hash.go b/field/koalabear/poseidon2/hash.go index ff0f19cfcd..967d1aec0e 100644 --- a/field/koalabear/poseidon2/hash.go +++ b/field/koalabear/poseidon2/hash.go @@ -17,11 +17,11 @@ func NewMerkleDamgardHasher() gnarkHash.StateStorer { // NewParameters returns a new set of parameters for the Poseidon2 permutation. // The default parameters are: -// - width: 8 +// - width: 16 // - nbFullRounds: 6 // - nbPartialRounds: 21 func NewDefaultParameters() *Parameters { - return NewParameters(8, 6, 21) + return NewParameters(16, 6, 21) } func init() { From a6c7acbdeab7ec0e0b11f72afb28074ea53b49e6 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 7 Feb 2025 14:25:06 -0500 Subject: [PATCH 03/20] fix(poseidon2): fr in babybear test --- field/babybear/poseidon2/poseidon2_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go index a002f49759..f94dcf97d9 100644 --- a/field/babybear/poseidon2/poseidon2_test.go +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -6,7 +6,7 @@ package poseidon2 import ( "testing" - fr "github.com/consensys/gnark-crypto/field/koalabear" + fr "github.com/consensys/gnark-crypto/field/babybear" ) func TestExternalMatrix(t *testing.T) { From f5f1fb493a1c3946a4062b08dd89f608ae95502a Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 7 Feb 2025 17:32:31 -0500 Subject: [PATCH 04/20] feat: poseidon2 for goldilocks --- field/goldilocks/poseidon2/doc.go | 15 + field/goldilocks/poseidon2/hash.go | 31 ++ field/goldilocks/poseidon2/poseidon2.go | 320 +++++++++++++++++++ field/goldilocks/poseidon2/poseidon2_test.go | 66 ++++ hash/hashes.go | 29 +- 5 files changed, 449 insertions(+), 12 deletions(-) create mode 100644 field/goldilocks/poseidon2/doc.go create mode 100644 field/goldilocks/poseidon2/hash.go create mode 100644 field/goldilocks/poseidon2/poseidon2.go create mode 100644 field/goldilocks/poseidon2/poseidon2_test.go diff --git a/field/goldilocks/poseidon2/doc.go b/field/goldilocks/poseidon2/doc.go new file mode 100644 index 0000000000..07d7d09033 --- /dev/null +++ b/field/goldilocks/poseidon2/doc.go @@ -0,0 +1,15 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// 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/field/goldilocks/poseidon2/hash.go b/field/goldilocks/poseidon2/hash.go new file mode 100644 index 0000000000..e48c529132 --- /dev/null +++ b/field/goldilocks/poseidon2/hash.go @@ -0,0 +1,31 @@ +package poseidon2 + +import ( + "hash" + + fr "github.com/consensys/gnark-crypto/field/goldilocks" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// 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( + &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// The default parameters are: +// - width: 4 +// - nbFullRounds: 6 +// - nbPartialRounds: 17 +func NewDefaultParameters() *Parameters { + return NewParameters(3, 6, 17) +} + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_GOLDILOCKS, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/field/goldilocks/poseidon2/poseidon2.go b/field/goldilocks/poseidon2/poseidon2.go new file mode 100644 index 0000000000..cc543331f3 --- /dev/null +++ b/field/goldilocks/poseidon2/poseidon2.go @@ -0,0 +1,320 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "errors" + "fmt" + + "golang.org/x/crypto/sha3" + + fr "github.com/consensys/gnark-crypto/field/goldilocks" +) + +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. 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)) + Width int + + // number of full rounds (even number) + NbFullRounds int + + // number of partial rounds + NbPartialRounds int + + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element +} + +// 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.String() + p.initRC(seed) + return &p +} + +// 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 +} + +// 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-GOLDILOCKS[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 +// rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 +func (p *Parameters) initRC(seed string) { + + bseed := ([]byte)(seed) + hash := sha3.NewLegacyKeccak256() + _, _ = hash.Write(bseed) + rnd := hash.Sum(nil) // pre hash before use + hash.Reset() + _, _ = hash.Write(rnd) + + 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 := 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 := 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) + } + } + 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 *Permutation) sBox(index int, input []fr.Element) { + var tmp fr.Element + tmp.Set(&input[index]) + + // sbox degree is 17 + input[index].Square(&input[index]). + Square(&input[index]). + Square(&input[index]). + Square(&input[index]). + Mul(&input[index], &tmp) + +} + +// matMulM4 computes +// s <- M4*s +// where M4= +// (5 7 1 3) +// (4 6 1 1) +// (1 3 5 7) +// (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 *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 + t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 + t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 + t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 + t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 + t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 + t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 + t6.Add(&t3, &t5) // t3+t4 + t7.Add(&t2, &t4) // t2+t4 + s[4*i].Set(&t6) + s[4*i+1].Set(&t5) + s[4*i+2].Set(&t7) + s[4*i+3].Set(&t4) + } +} + +// 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) +// see https://eprint.iacr.org/2023/323.pdf +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { + + 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.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.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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[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 *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { + 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) + 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) + 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") + } +} + +// addRoundKeyInPlace adds the round-th key to the buffer +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 *Permutation) BlockSize() int { + return fr.Bytes +} + +// Permutation applies the permutation on input, and stores the result in input. +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.NbFullRounds / 2 + for i := 0; i < rf; i++ { + // one round = matMulExternal(sBox_Full(addRoundKey)) + h.addRoundKeyInPlace(i, input) + for j := 0; j < h.params.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + 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.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.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + return nil +} + +// Compress applies the permutation on left and right and returns the right lane +// 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 + + if err := x[0].SetBytesCanonical(left); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(right); 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/field/goldilocks/poseidon2/poseidon2_test.go b/field/goldilocks/poseidon2/poseidon2_test.go new file mode 100644 index 0000000000..61abe9ca9a --- /dev/null +++ b/field/goldilocks/poseidon2/poseidon2_test.go @@ -0,0 +1,66 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "testing" + + fr "github.com/consensys/gnark-crypto/field/goldilocks" +) + +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) + expected[0][1].SetUint64(4) + expected[0][2].SetUint64(1) + expected[0][3].SetUint64(1) + + expected[1][0].SetUint64(7) + expected[1][1].SetUint64(6) + expected[1][2].SetUint64(3) + expected[1][3].SetUint64(1) + + expected[2][0].SetUint64(1) + expected[2][1].SetUint64(1) + expected[2][2].SetUint64(5) + expected[2][3].SetUint64(4) + + expected[3][0].SetUint64(3) + expected[3][1].SetUint64(1) + expected[3][2].SetUint64(7) + expected[3][3].SetUint64(6) + + h := NewPermutation(4, 8, 56) + var tmp [4]fr.Element + for i := 0; i < 4; i++ { + for j := 0; j < 4; j++ { + tmp[j].SetUint64(0) + if i == j { + tmp[j].SetOne() + } + } + // h.Write(tmp[:]) + h.matMulExternalInPlace(tmp[:]) + for j := 0; j < 4; j++ { + if !tmp[j].Equal(&expected[i][j]) { + t.Fatal("error matMul4") + } + } + } + +} + +func BenchmarkPoseidon2(b *testing.B) { + h := NewPermutation(3, 8, 56) + var tmp [3]fr.Element + tmp[0].SetRandom() + tmp[1].SetRandom() + tmp[2].SetRandom() + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} diff --git a/hash/hashes.go b/hash/hashes.go index 07842ee54c..592f66d4f6 100644 --- a/hash/hashes.go +++ b/hash/hashes.go @@ -44,25 +44,28 @@ const ( POSEIDON2_BLS12_377 // POSEIDON2_KOALABEAR is the Poseidon2 hash function for the KoalaBear field. POSEIDON2_KOALABEAR - // POSEIDON2_BABYBEAR is the Poseidon2 hash function for the KoalaBear field. + // POSEIDON2_BABYBEAR is the Poseidon2 hash function for the BabyBear field. POSEIDON2_BABYBEAR + // POSEIDON2_GOLDILOCKS is the Poseidon2 hash function for the Goldilocks field. + POSEIDON2_GOLDILOCKS 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_GRUMPKIN: 32, - POSEIDON2_BLS12_377: 48, - POSEIDON2_KOALABEAR: 4, - POSEIDON2_BABYBEAR: 4, + 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_GRUMPKIN: 32, + POSEIDON2_BLS12_377: 48, + POSEIDON2_KOALABEAR: 4, + POSEIDON2_BABYBEAR: 4, + POSEIDON2_GOLDILOCKS: 8, } // New initializes the hash function. This is a convenience function which does @@ -107,6 +110,8 @@ func (m Hash) String() string { return "POSEIDON2_KOALABEAR" case POSEIDON2_BABYBEAR: return "POSEIDON2_BABYBEAR" + case POSEIDON2_GOLDILOCKS: + return "POSEIDON2_GOLDILOCKS" default: return "unknown hash function" } From b7eca49873e23973d508e415f615fae42fb6b3db Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 10 Feb 2025 13:46:12 -0500 Subject: [PATCH 05/20] fix(poseidon2/koala-baby-bears): internal matrix --- field/babybear/poseidon2/hash.go | 19 +++++- field/babybear/poseidon2/poseidon2.go | 52 ++++++---------- field/babybear/poseidon2/poseidon2_test.go | 66 --------------------- field/goldilocks/poseidon2/hash.go | 2 +- field/koalabear/poseidon2/hash.go | 19 +++++- field/koalabear/poseidon2/poseidon2.go | 52 ++++++---------- field/koalabear/poseidon2/poseidon2_test.go | 66 --------------------- 7 files changed, 75 insertions(+), 201 deletions(-) delete mode 100644 field/babybear/poseidon2/poseidon2_test.go delete mode 100644 field/koalabear/poseidon2/poseidon2_test.go diff --git a/field/babybear/poseidon2/hash.go b/field/babybear/poseidon2/hash.go index ec4f8e969f..d4ba6c5cdb 100644 --- a/field/babybear/poseidon2/hash.go +++ b/field/babybear/poseidon2/hash.go @@ -21,7 +21,24 @@ func NewMerkleDamgardHasher() gnarkHash.StateStorer { // - nbFullRounds: 6 // - nbPartialRounds: 12 func NewDefaultParameters() *Parameters { - return NewParameters(16, 6, 12) + var diagonal [16]fr.Element + diagonal[0].SetString("1337680893") + diagonal[1].SetString("1849524340") + diagonal[2].SetString("945441141") + diagonal[3].SetString("110332385") + diagonal[4].SetString("981849927") + diagonal[5].SetString("511933108") + diagonal[6].SetString("1289844587") + diagonal[7].SetString("896077849") + diagonal[8].SetString("971707481") + diagonal[9].SetString("121792757") + diagonal[10].SetString("1598170707") + diagonal[11].SetString("1688648703") + diagonal[12].SetString("26932898") + diagonal[13].SetString("1760625654") + diagonal[14].SetString("1480423439") + diagonal[15].SetString("1903270600") + return NewParameters(16, 6, 21, diagonal) } func init() { diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index 84670b84b5..36a9ca5988 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -42,12 +42,15 @@ type Parameters struct { // derived round keys from the parameter seed and curve ID RoundKeys [][]fr.Element + + // diagonal of the internal matrix + DiagInternalMatrices [16]fr.Element } // 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 { +func NewParameters(width, nbFullRounds, nbPartialRounds int, diagInternalMatrices [16]fr.Element) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} seed := p.String() p.initRC(seed) @@ -117,11 +120,8 @@ type Permutation struct { } // 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) +func NewPermutation(t, rf, rp int, diag [16]fr.Element) *Permutation { + params := NewParameters(t, rf, rp, diag) res := &Permutation{params: params} return res } @@ -129,9 +129,6 @@ func NewPermutation(t, rf, rp int) *Permutation { // 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 @@ -142,7 +139,7 @@ func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) - // sbox degree is 17 + // sbox degree is 7 input[index].Square(&input[index]). Square(&input[index]). Square(&input[index]). @@ -223,29 +220,18 @@ func (h *Permutation) 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 *Permutation) matMulInternalInPlace(input []fr.Element) { - switch h.params.Width { - 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) - 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) - 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") + // TODO: use optimized diagonal as in plonky3 + // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/baby-bear/src/poseidon2.rs + // + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). + Add(&input[i], &sum) } } diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go deleted file mode 100644 index f94dcf97d9..0000000000 --- a/field/babybear/poseidon2/poseidon2_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020-2025 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -package poseidon2 - -import ( - "testing" - - fr "github.com/consensys/gnark-crypto/field/babybear" -) - -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) - expected[0][1].SetUint64(4) - expected[0][2].SetUint64(1) - expected[0][3].SetUint64(1) - - expected[1][0].SetUint64(7) - expected[1][1].SetUint64(6) - expected[1][2].SetUint64(3) - expected[1][3].SetUint64(1) - - expected[2][0].SetUint64(1) - expected[2][1].SetUint64(1) - expected[2][2].SetUint64(5) - expected[2][3].SetUint64(4) - - expected[3][0].SetUint64(3) - expected[3][1].SetUint64(1) - expected[3][2].SetUint64(7) - expected[3][3].SetUint64(6) - - h := NewPermutation(4, 8, 56) - var tmp [4]fr.Element - for i := 0; i < 4; i++ { - for j := 0; j < 4; j++ { - tmp[j].SetUint64(0) - if i == j { - tmp[j].SetOne() - } - } - // h.Write(tmp[:]) - h.matMulExternalInPlace(tmp[:]) - for j := 0; j < 4; j++ { - if !tmp[j].Equal(&expected[i][j]) { - t.Fatal("error matMul4") - } - } - } - -} - -func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56) - var tmp [3]fr.Element - tmp[0].SetRandom() - tmp[1].SetRandom() - tmp[2].SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - h.Permutation(tmp[:]) - } -} diff --git a/field/goldilocks/poseidon2/hash.go b/field/goldilocks/poseidon2/hash.go index e48c529132..9b0ec03a03 100644 --- a/field/goldilocks/poseidon2/hash.go +++ b/field/goldilocks/poseidon2/hash.go @@ -17,7 +17,7 @@ func NewMerkleDamgardHasher() gnarkHash.StateStorer { // NewParameters returns a new set of parameters for the Poseidon2 permutation. // The default parameters are: -// - width: 4 +// - width: 3 // - nbFullRounds: 6 // - nbPartialRounds: 17 func NewDefaultParameters() *Parameters { diff --git a/field/koalabear/poseidon2/hash.go b/field/koalabear/poseidon2/hash.go index 967d1aec0e..cd7621e9ff 100644 --- a/field/koalabear/poseidon2/hash.go +++ b/field/koalabear/poseidon2/hash.go @@ -21,7 +21,24 @@ func NewMerkleDamgardHasher() gnarkHash.StateStorer { // - nbFullRounds: 6 // - nbPartialRounds: 21 func NewDefaultParameters() *Parameters { - return NewParameters(16, 6, 21) + var diagonal [16]fr.Element + diagonal[0].SetString("2046570709") + diagonal[1].SetString("758836515") + diagonal[2].SetString("1294135") + diagonal[3].SetString("1937509553") + diagonal[4].SetString("2128865551") + diagonal[5].SetString("1697674041") + diagonal[6].SetString("1520666926") + diagonal[7].SetString("1558715341") + diagonal[8].SetString("1137246046") + diagonal[9].SetString("1320163102") + diagonal[10].SetString("1968225990") + diagonal[11].SetString("234409115") + diagonal[12].SetString("946626146") + diagonal[13].SetString("566947014") + diagonal[14].SetString("2115407367") + diagonal[15].SetString("1174021429") + return NewParameters(16, 6, 21, diagonal) } func init() { diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index d76a2d5768..e1d5ee7f0f 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -42,12 +42,15 @@ type Parameters struct { // derived round keys from the parameter seed and curve ID RoundKeys [][]fr.Element + + // diagonal of the internal matrix + DiagInternalMatrices [16]fr.Element } // 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 { +func NewParameters(width, nbFullRounds, nbPartialRounds int, diagInternalMatrices [16]fr.Element) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} seed := p.String() p.initRC(seed) @@ -117,11 +120,8 @@ type Permutation struct { } // 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) +func NewPermutation(t, rf, rp int, diag [16]fr.Element) *Permutation { + params := NewParameters(t, rf, rp, diag) res := &Permutation{params: params} return res } @@ -129,9 +129,6 @@ func NewPermutation(t, rf, rp int) *Permutation { // 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 @@ -142,7 +139,7 @@ func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) - // sbox degree is 17 + // sbox degree is 3 input[index].Square(&input[index]). Square(&input[index]). Square(&input[index]). @@ -223,29 +220,18 @@ func (h *Permutation) 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 *Permutation) matMulInternalInPlace(input []fr.Element) { - switch h.params.Width { - 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) - 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) - 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") + // TODO: use optimized diagonal as in plonky3 + // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/koala-bear/src/poseidon2.rs + // + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). + Add(&input[i], &sum) } } diff --git a/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go deleted file mode 100644 index a002f49759..0000000000 --- a/field/koalabear/poseidon2/poseidon2_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020-2025 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -package poseidon2 - -import ( - "testing" - - fr "github.com/consensys/gnark-crypto/field/koalabear" -) - -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) - expected[0][1].SetUint64(4) - expected[0][2].SetUint64(1) - expected[0][3].SetUint64(1) - - expected[1][0].SetUint64(7) - expected[1][1].SetUint64(6) - expected[1][2].SetUint64(3) - expected[1][3].SetUint64(1) - - expected[2][0].SetUint64(1) - expected[2][1].SetUint64(1) - expected[2][2].SetUint64(5) - expected[2][3].SetUint64(4) - - expected[3][0].SetUint64(3) - expected[3][1].SetUint64(1) - expected[3][2].SetUint64(7) - expected[3][3].SetUint64(6) - - h := NewPermutation(4, 8, 56) - var tmp [4]fr.Element - for i := 0; i < 4; i++ { - for j := 0; j < 4; j++ { - tmp[j].SetUint64(0) - if i == j { - tmp[j].SetOne() - } - } - // h.Write(tmp[:]) - h.matMulExternalInPlace(tmp[:]) - for j := 0; j < 4; j++ { - if !tmp[j].Equal(&expected[i][j]) { - t.Fatal("error matMul4") - } - } - } - -} - -func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56) - var tmp [3]fr.Element - tmp[0].SetRandom() - tmp[1].SetRandom() - tmp[2].SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - h.Permutation(tmp[:]) - } -} From 31b68d8563a19aba70eddf2165afcbdfe1b5f6e2 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 11 Feb 2025 12:50:25 -0500 Subject: [PATCH 06/20] feat(koala/baby): t=24 for sponge poseidon2 --- field/babybear/poseidon2/hash.go | 82 +++-- field/babybear/poseidon2/poseidon2.go | 108 ++++--- field/goldilocks/poseidon2/doc.go | 15 - field/goldilocks/poseidon2/hash.go | 31 -- field/goldilocks/poseidon2/poseidon2.go | 320 ------------------- field/goldilocks/poseidon2/poseidon2_test.go | 66 ---- field/koalabear/poseidon2/hash.go | 81 +++-- field/koalabear/poseidon2/poseidon2.go | 104 +++--- 8 files changed, 231 insertions(+), 576 deletions(-) delete mode 100644 field/goldilocks/poseidon2/doc.go delete mode 100644 field/goldilocks/poseidon2/hash.go delete mode 100644 field/goldilocks/poseidon2/poseidon2.go delete mode 100644 field/goldilocks/poseidon2/poseidon2_test.go diff --git a/field/babybear/poseidon2/hash.go b/field/babybear/poseidon2/hash.go index d4ba6c5cdb..ce06348594 100644 --- a/field/babybear/poseidon2/hash.go +++ b/field/babybear/poseidon2/hash.go @@ -16,33 +16,71 @@ func NewMerkleDamgardHasher() gnarkHash.StateStorer { } // NewParameters returns a new set of parameters for the Poseidon2 permutation. -// The default parameters are: -// - width: 16 -// - nbFullRounds: 6 -// - nbPartialRounds: 12 +// The default parameters are, +// +// 1. for compression: +// - width: 16 +// - nbFullRounds: 6 +// - nbPartialRounds: 12 +// +// 2. for sponge: +// - width: 24 +// - nbFullRounds: 6 +// - nbPartialRounds: 19 func NewDefaultParameters() *Parameters { - var diagonal [16]fr.Element - diagonal[0].SetString("1337680893") - diagonal[1].SetString("1849524340") - diagonal[2].SetString("945441141") - diagonal[3].SetString("110332385") - diagonal[4].SetString("981849927") - diagonal[5].SetString("511933108") - diagonal[6].SetString("1289844587") - diagonal[7].SetString("896077849") - diagonal[8].SetString("971707481") - diagonal[9].SetString("121792757") - diagonal[10].SetString("1598170707") - diagonal[11].SetString("1688648703") - diagonal[12].SetString("26932898") - diagonal[13].SetString("1760625654") - diagonal[14].SetString("1480423439") - diagonal[15].SetString("1903270600") - return NewParameters(16, 6, 21, diagonal) + return NewParameters(16, 6, 12) + // return NewParameters(24, 6, 19) } +var diag16 [16]fr.Element +var diag24 [24]fr.Element + func init() { gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BABYBEAR, func() hash.Hash { return NewMerkleDamgardHasher() }) + + // diagonal of internal matrix when Width=16 + diag16[0].SetString("1526245758") + diag16[1].SetString("1181039355") + diag16[2].SetString("294525134") + diag16[3].SetString("1187117748") + diag16[4].SetString("1982584076") + diag16[5].SetString("1805711612") + diag16[6].SetString("1185627262") + diag16[7].SetString("856598545") + diag16[8].SetString("1032717003") + diag16[9].SetString("1019193515") + diag16[10].SetString("1622854951") + diag16[11].SetString("1458848858") + diag16[12].SetString("145265389") + diag16[13].SetString("1220122282") + diag16[14].SetString("1647429377") + diag16[15].SetString("462996148") + + // diagonal of internal matrix when Width=24 + diag24[0].SetString("1151545613") + diag24[1].SetString("1378328160") + diag24[2].SetString("304145285") + diag24[3].SetString("1170753223") + diag24[4].SetString("1173904120") + diag24[5].SetString("382806505") + diag24[6].SetString("1018843483") + diag24[7].SetString("326347087") + diag24[8].SetString("1688249811") + diag24[9].SetString("92343735") + diag24[10].SetString("1989067774") + diag24[11].SetString("426627352") + diag24[12].SetString("1043427554") + diag24[13].SetString("1350803455") + diag24[14].SetString("685360948") + diag24[15].SetString("992522095") + diag24[16].SetString("461201261") + diag24[17].SetString("546901022") + diag24[18].SetString("1924506406") + diag24[19].SetString("1858498981") + diag24[20].SetString("227302405") + diag24[21].SetString("642805551") + diag24[22].SetString("522204904") + diag24[23].SetString("1738963060") } diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index 36a9ca5988..68ec8a1289 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -18,7 +18,7 @@ var ( const ( // d is the degree of the sBox - d = 7 + d = 3 ) // DegreeSBox returns the degree of the sBox function used in the Poseidon2 @@ -42,15 +42,12 @@ type Parameters struct { // derived round keys from the parameter seed and curve ID RoundKeys [][]fr.Element - - // diagonal of the internal matrix - DiagInternalMatrices [16]fr.Element } // 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, diagInternalMatrices [16]fr.Element) *Parameters { +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} seed := p.String() p.initRC(seed) @@ -120,8 +117,11 @@ type Permutation struct { } // NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, diag [16]fr.Element) *Permutation { - params := NewParameters(t, rf, rp, diag) +func NewPermutation(t, rf, rp int) *Permutation { + if t != 16 && t != 24 { + panic("only Width=16,24 are supported") + } + params := NewParameters(t, rf, rp) res := &Permutation{params: params} return res } @@ -129,6 +129,9 @@ func NewPermutation(t, rf, rp int, diag [16]fr.Element) *Permutation { // NewPermutationWithSeed returns a new Poseidon2 permutation instance with a // given seed. func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t != 16 && t != 24 { + panic("only Width=16,24 are supported") + } params := NewParametersWithSeed(t, rf, rp, seed) res := &Permutation{params: params} return res @@ -139,7 +142,7 @@ func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) - // sbox degree is 7 + // sbox degree is 3 input[index].Square(&input[index]). Square(&input[index]). Square(&input[index]). @@ -183,55 +186,58 @@ func (h *Permutation) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - 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.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.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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + // 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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[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 *Permutation) matMulInternalInPlace(input []fr.Element) { - // TODO: use optimized diagonal as in plonky3 - // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/baby-bear/src/poseidon2.rs - // - // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.Width; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.Width; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) + switch h.params.Width { + case 16: + // TODO: use optimized diagonal as in plonky3 + // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/baby-bear/src/poseidon2.rs + // + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/2^27, -1/2^8, -1/16, -1/2^27] + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag16[i]). + Add(&input[i], &sum) + } + case 24: + // TODO: use optimized diagonal as in plonky3 + // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/baby-bear/src/poseidon2.rs + // + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/2^7, 1/2^9, 1/2^27, -1/2^8, -1/4, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^27] + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag24[i]). + Add(&input[i], &sum) + } + default: + panic("only t=16,24 are supported") } } diff --git a/field/goldilocks/poseidon2/doc.go b/field/goldilocks/poseidon2/doc.go deleted file mode 100644 index 07d7d09033..0000000000 --- a/field/goldilocks/poseidon2/doc.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020-2025 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// 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/field/goldilocks/poseidon2/hash.go b/field/goldilocks/poseidon2/hash.go deleted file mode 100644 index 9b0ec03a03..0000000000 --- a/field/goldilocks/poseidon2/hash.go +++ /dev/null @@ -1,31 +0,0 @@ -package poseidon2 - -import ( - "hash" - - fr "github.com/consensys/gnark-crypto/field/goldilocks" - gnarkHash "github.com/consensys/gnark-crypto/hash" -) - -// 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( - &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) -} - -// NewParameters returns a new set of parameters for the Poseidon2 permutation. -// The default parameters are: -// - width: 3 -// - nbFullRounds: 6 -// - nbPartialRounds: 17 -func NewDefaultParameters() *Parameters { - return NewParameters(3, 6, 17) -} - -func init() { - gnarkHash.RegisterHash(gnarkHash.POSEIDON2_GOLDILOCKS, func() hash.Hash { - return NewMerkleDamgardHasher() - }) -} diff --git a/field/goldilocks/poseidon2/poseidon2.go b/field/goldilocks/poseidon2/poseidon2.go deleted file mode 100644 index cc543331f3..0000000000 --- a/field/goldilocks/poseidon2/poseidon2.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2020-2025 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -package poseidon2 - -import ( - "errors" - "fmt" - - "golang.org/x/crypto/sha3" - - fr "github.com/consensys/gnark-crypto/field/goldilocks" -) - -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. 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)) - Width int - - // number of full rounds (even number) - NbFullRounds int - - // number of partial rounds - NbPartialRounds int - - // derived round keys from the parameter seed and curve ID - RoundKeys [][]fr.Element -} - -// 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.String() - p.initRC(seed) - return &p -} - -// 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 -} - -// 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-GOLDILOCKS[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 -// rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 -func (p *Parameters) initRC(seed string) { - - bseed := ([]byte)(seed) - hash := sha3.NewLegacyKeccak256() - _, _ = hash.Write(bseed) - rnd := hash.Sum(nil) // pre hash before use - hash.Reset() - _, _ = hash.Write(rnd) - - 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 := 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 := 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) - } - } - 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 *Permutation) sBox(index int, input []fr.Element) { - var tmp fr.Element - tmp.Set(&input[index]) - - // sbox degree is 17 - input[index].Square(&input[index]). - Square(&input[index]). - Square(&input[index]). - Square(&input[index]). - Mul(&input[index], &tmp) - -} - -// matMulM4 computes -// s <- M4*s -// where M4= -// (5 7 1 3) -// (4 6 1 1) -// (1 3 5 7) -// (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 *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 - t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 - t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 - t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 - t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 - t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 - t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 - t6.Add(&t3, &t5) // t3+t4 - t7.Add(&t2, &t4) // t2+t4 - s[4*i].Set(&t6) - s[4*i+1].Set(&t5) - s[4*i+2].Set(&t7) - s[4*i+3].Set(&t4) - } -} - -// 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) -// see https://eprint.iacr.org/2023/323.pdf -func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - - 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.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.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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[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 *Permutation) matMulInternalInPlace(input []fr.Element) { - switch h.params.Width { - 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) - 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) - 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") - } -} - -// addRoundKeyInPlace adds the round-th key to the buffer -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 *Permutation) BlockSize() int { - return fr.Bytes -} - -// Permutation applies the permutation on input, and stores the result in input. -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.NbFullRounds / 2 - for i := 0; i < rf; i++ { - // one round = matMulExternal(sBox_Full(addRoundKey)) - h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.Width; j++ { - h.sBox(j, input) - } - h.matMulExternalInPlace(input) - } - - 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.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.Width; j++ { - h.sBox(j, input) - } - h.matMulExternalInPlace(input) - } - - return nil -} - -// Compress applies the permutation on left and right and returns the right lane -// 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 - - if err := x[0].SetBytesCanonical(left); err != nil { - return nil, err - } - if err := x[1].SetBytesCanonical(right); 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/field/goldilocks/poseidon2/poseidon2_test.go b/field/goldilocks/poseidon2/poseidon2_test.go deleted file mode 100644 index 61abe9ca9a..0000000000 --- a/field/goldilocks/poseidon2/poseidon2_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020-2025 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -package poseidon2 - -import ( - "testing" - - fr "github.com/consensys/gnark-crypto/field/goldilocks" -) - -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) - expected[0][1].SetUint64(4) - expected[0][2].SetUint64(1) - expected[0][3].SetUint64(1) - - expected[1][0].SetUint64(7) - expected[1][1].SetUint64(6) - expected[1][2].SetUint64(3) - expected[1][3].SetUint64(1) - - expected[2][0].SetUint64(1) - expected[2][1].SetUint64(1) - expected[2][2].SetUint64(5) - expected[2][3].SetUint64(4) - - expected[3][0].SetUint64(3) - expected[3][1].SetUint64(1) - expected[3][2].SetUint64(7) - expected[3][3].SetUint64(6) - - h := NewPermutation(4, 8, 56) - var tmp [4]fr.Element - for i := 0; i < 4; i++ { - for j := 0; j < 4; j++ { - tmp[j].SetUint64(0) - if i == j { - tmp[j].SetOne() - } - } - // h.Write(tmp[:]) - h.matMulExternalInPlace(tmp[:]) - for j := 0; j < 4; j++ { - if !tmp[j].Equal(&expected[i][j]) { - t.Fatal("error matMul4") - } - } - } - -} - -func BenchmarkPoseidon2(b *testing.B) { - h := NewPermutation(3, 8, 56) - var tmp [3]fr.Element - tmp[0].SetRandom() - tmp[1].SetRandom() - tmp[2].SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - h.Permutation(tmp[:]) - } -} diff --git a/field/koalabear/poseidon2/hash.go b/field/koalabear/poseidon2/hash.go index cd7621e9ff..0a46a2124e 100644 --- a/field/koalabear/poseidon2/hash.go +++ b/field/koalabear/poseidon2/hash.go @@ -16,33 +16,70 @@ func NewMerkleDamgardHasher() gnarkHash.StateStorer { } // NewParameters returns a new set of parameters for the Poseidon2 permutation. -// The default parameters are: -// - width: 16 -// - nbFullRounds: 6 -// - nbPartialRounds: 21 +// The default parameters are, +// +// 1. for compression: +// - width: 16 +// - nbFullRounds: 6 +// - nbPartialRounds: 21 +// +// 2. for sponge: +// - width: 24 +// - nbFullRounds: 6 +// - nbPartialRounds: 21 func NewDefaultParameters() *Parameters { - var diagonal [16]fr.Element - diagonal[0].SetString("2046570709") - diagonal[1].SetString("758836515") - diagonal[2].SetString("1294135") - diagonal[3].SetString("1937509553") - diagonal[4].SetString("2128865551") - diagonal[5].SetString("1697674041") - diagonal[6].SetString("1520666926") - diagonal[7].SetString("1558715341") - diagonal[8].SetString("1137246046") - diagonal[9].SetString("1320163102") - diagonal[10].SetString("1968225990") - diagonal[11].SetString("234409115") - diagonal[12].SetString("946626146") - diagonal[13].SetString("566947014") - diagonal[14].SetString("2115407367") - diagonal[15].SetString("1174021429") - return NewParameters(16, 6, 21, diagonal) + return NewParameters(16, 6, 21) } +var diag16 [16]fr.Element +var diag24 [24]fr.Element + func init() { gnarkHash.RegisterHash(gnarkHash.POSEIDON2_KOALABEAR, func() hash.Hash { return NewMerkleDamgardHasher() }) + + // diagonal of internal matrix when Width=16 + diag16[0].SetString("2046570709") + diag16[1].SetString("758836515") + diag16[2].SetString("1294135") + diag16[3].SetString("1937509553") + diag16[4].SetString("2128865551") + diag16[5].SetString("1697674041") + diag16[6].SetString("1520666926") + diag16[7].SetString("1558715341") + diag16[8].SetString("1137246046") + diag16[9].SetString("1320163102") + diag16[10].SetString("1968225990") + diag16[11].SetString("234409115") + diag16[12].SetString("946626146") + diag16[13].SetString("566947014") + diag16[14].SetString("2115407367") + diag16[15].SetString("1174021429") + + // diagonal of internal matrix when Width=24 + diag24[0].SetString("1308809838") + diag24[1].SetString("928239151") + diag24[2].SetString("495882907") + diag24[3].SetString("593757554") + diag24[4].SetString("593757554") + diag24[5].SetString("567559762") + diag24[6].SetString("1572388064") + diag24[7].SetString("1076816199") + diag24[8].SetString("652906069") + diag24[9].SetString("1871203714") + diag24[10].SetString("358820701") + diag24[11].SetString("1696335905") + diag24[12].SetString("1771481038") + diag24[13].SetString("67413782") + diag24[14].SetString("1765474848") + diag24[15].SetString("235952237") + diag24[16].SetString("111716804") + diag24[17].SetString("213766759") + diag24[18].SetString("656473656") + diag24[19].SetString("1879962596") + diag24[20].SetString("1762516106") + diag24[21].SetString("197180297") + diag24[22].SetString("2061316832") + diag24[23].SetString("1833346861") } diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index e1d5ee7f0f..d79a6dc790 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -42,15 +42,12 @@ type Parameters struct { // derived round keys from the parameter seed and curve ID RoundKeys [][]fr.Element - - // diagonal of the internal matrix - DiagInternalMatrices [16]fr.Element } // 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, diagInternalMatrices [16]fr.Element) *Parameters { +func NewParameters(width, nbFullRounds, nbPartialRounds int) *Parameters { p := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} seed := p.String() p.initRC(seed) @@ -120,8 +117,11 @@ type Permutation struct { } // NewPermutation returns a new Poseidon2 permutation instance. -func NewPermutation(t, rf, rp int, diag [16]fr.Element) *Permutation { - params := NewParameters(t, rf, rp, diag) +func NewPermutation(t, rf, rp int) *Permutation { + if t != 16 && t != 24 { + panic("only Width=16,24 are supported") + } + params := NewParameters(t, rf, rp) res := &Permutation{params: params} return res } @@ -129,6 +129,9 @@ func NewPermutation(t, rf, rp int, diag [16]fr.Element) *Permutation { // NewPermutationWithSeed returns a new Poseidon2 permutation instance with a // given seed. func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { + if t != 16 && t != 24 { + panic("only Width=16,24 are supported") + } params := NewParametersWithSeed(t, rf, rp, seed) res := &Permutation{params: params} return res @@ -183,55 +186,58 @@ func (h *Permutation) matMulM4InPlace(s []fr.Element) { // see https://eprint.iacr.org/2023/323.pdf func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - 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.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.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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + // 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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[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 *Permutation) matMulInternalInPlace(input []fr.Element) { - // TODO: use optimized diagonal as in plonky3 - // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/koala-bear/src/poseidon2.rs - // - // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] - var sum fr.Element - sum.Set(&input[0]) - for i := 1; i < h.params.Width; i++ { - sum.Add(&sum, &input[i]) - } - for i := 0; i < h.params.Width; i++ { - input[i].Mul(&input[i], &h.params.DiagInternalMatrices[i]). - Add(&input[i], &sum) + switch h.params.Width { + case 16: + // TODO: use optimized diagonal as in plonky3 + // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/koala-bear/src/poseidon2.rs + // + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag16[i]). + Add(&input[i], &sum) + } + case 24: + // TODO: use optimized diagonal as in plonky3 + // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/koala-bear/src/poseidon2.rs + // + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24] + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag24[i]). + Add(&input[i], &sum) + } + default: + panic("only t=16,24 are supported") } } From 365fe695ee2cc82d012672b31eee01b820ea4de1 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 12 Feb 2025 12:33:36 -0500 Subject: [PATCH 07/20] perf(poseidon2/koala-baby): change diag constants --- field/babybear/poseidon2/hash.go | 81 +++++++++++++------------- field/babybear/poseidon2/poseidon2.go | 10 +--- field/koalabear/poseidon2/hash.go | 81 +++++++++++++------------- field/koalabear/poseidon2/poseidon2.go | 10 +--- 4 files changed, 86 insertions(+), 96 deletions(-) diff --git a/field/babybear/poseidon2/hash.go b/field/babybear/poseidon2/hash.go index ce06348594..fb9988fdb8 100644 --- a/field/babybear/poseidon2/hash.go +++ b/field/babybear/poseidon2/hash.go @@ -10,7 +10,6 @@ import ( // 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( &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) } @@ -41,46 +40,46 @@ func init() { }) // diagonal of internal matrix when Width=16 - diag16[0].SetString("1526245758") - diag16[1].SetString("1181039355") - diag16[2].SetString("294525134") - diag16[3].SetString("1187117748") - diag16[4].SetString("1982584076") - diag16[5].SetString("1805711612") - diag16[6].SetString("1185627262") - diag16[7].SetString("856598545") - diag16[8].SetString("1032717003") - diag16[9].SetString("1019193515") - diag16[10].SetString("1622854951") - diag16[11].SetString("1458848858") - diag16[12].SetString("145265389") - diag16[13].SetString("1220122282") - diag16[14].SetString("1647429377") - diag16[15].SetString("462996148") + diag16[0].SetString("2130706431") + diag16[1].SetString("1") + diag16[2].SetString("2") + diag16[3].SetString("1065353217") + diag16[4].SetString("3") + diag16[5].SetString("4") + diag16[6].SetString("1065353216") + diag16[7].SetString("2130706430") + diag16[8].SetString("2130706429") + diag16[9].SetString("2122383361") + diag16[10].SetString("1598029825") + diag16[11].SetString("1864368129") + diag16[12].SetString("1864368113") + diag16[13].SetString("8323072") + diag16[14].SetString("133169152") + diag16[15].SetString("266338320") // diagonal of internal matrix when Width=24 - diag24[0].SetString("1151545613") - diag24[1].SetString("1378328160") - diag24[2].SetString("304145285") - diag24[3].SetString("1170753223") - diag24[4].SetString("1173904120") - diag24[5].SetString("382806505") - diag24[6].SetString("1018843483") - diag24[7].SetString("326347087") - diag24[8].SetString("1688249811") - diag24[9].SetString("92343735") - diag24[10].SetString("1989067774") - diag24[11].SetString("426627352") - diag24[12].SetString("1043427554") - diag24[13].SetString("1350803455") - diag24[14].SetString("685360948") - diag24[15].SetString("992522095") - diag24[16].SetString("461201261") - diag24[17].SetString("546901022") - diag24[18].SetString("1924506406") - diag24[19].SetString("1858498981") - diag24[20].SetString("227302405") - diag24[21].SetString("642805551") - diag24[22].SetString("522204904") - diag24[23].SetString("1738963060") + diag24[0].SetString("2130706431") + diag24[1].SetString("1") + diag24[2].SetString("2") + diag24[3].SetString("1065353217") + diag24[4].SetString("3") + diag24[5].SetString("4") + diag24[6].SetString("1065353216") + diag24[7].SetString("2130706430") + diag24[8].SetString("2130706429") + diag24[9].SetString("2122383361") + diag24[10].SetString("1598029825") + diag24[11].SetString("1864368129") + diag24[12].SetString("1997537281") + diag24[13].SetString("2114060289") + diag24[14].SetString("2126544897") + diag24[15].SetString("1864368113") + diag24[16].SetString("8323072") + diag24[17].SetString("532676608") + diag24[18].SetString("266338304") + diag24[19].SetString("133169152") + diag24[20].SetString("66584576") + diag24[21].SetString("33292288") + diag24[22].SetString("16646144") + diag24[23].SetString("266338320") } diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index 68ec8a1289..677b396bd1 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -209,9 +209,7 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 16: - // TODO: use optimized diagonal as in plonky3 - // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/baby-bear/src/poseidon2.rs - // + // TODO: optimize multiplication by diag16 // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/2^27, -1/2^8, -1/16, -1/2^27] var sum fr.Element sum.Set(&input[0]) @@ -223,9 +221,7 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { Add(&input[i], &sum) } case 24: - // TODO: use optimized diagonal as in plonky3 - // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/baby-bear/src/poseidon2.rs - // + // TODO: optimize multiplication by diag24 // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/2^7, 1/2^9, 1/2^27, -1/2^8, -1/4, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^27] var sum fr.Element sum.Set(&input[0]) @@ -237,7 +233,7 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { Add(&input[i], &sum) } default: - panic("only t=16,24 are supported") + panic("only Width=16,24 are supported") } } diff --git a/field/koalabear/poseidon2/hash.go b/field/koalabear/poseidon2/hash.go index 0a46a2124e..13cd84c639 100644 --- a/field/koalabear/poseidon2/hash.go +++ b/field/koalabear/poseidon2/hash.go @@ -10,7 +10,6 @@ import ( // 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( &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) } @@ -40,46 +39,46 @@ func init() { }) // diagonal of internal matrix when Width=16 - diag16[0].SetString("2046570709") - diag16[1].SetString("758836515") - diag16[2].SetString("1294135") - diag16[3].SetString("1937509553") - diag16[4].SetString("2128865551") - diag16[5].SetString("1697674041") - diag16[6].SetString("1520666926") - diag16[7].SetString("1558715341") - diag16[8].SetString("1137246046") - diag16[9].SetString("1320163102") - diag16[10].SetString("1968225990") - diag16[11].SetString("234409115") - diag16[12].SetString("946626146") - diag16[13].SetString("566947014") - diag16[14].SetString("2115407367") - diag16[15].SetString("1174021429") + diag16[0].SetString("2130706431") + diag16[1].SetString("1") + diag16[2].SetString("2") + diag16[3].SetString("1065353217") + diag16[4].SetString("3") + diag16[5].SetString("4") + diag16[6].SetString("1065353216") + diag16[7].SetString("2130706430") + diag16[8].SetString("2130706429") + diag16[9].SetString("2122383361") + diag16[10].SetString("1864368129") + diag16[11].SetString("2130706306") + diag16[12].SetString("8323072") + diag16[13].SetString("266338304") + diag16[14].SetString("133169152") + diag16[15].SetString("127") // diagonal of internal matrix when Width=24 - diag24[0].SetString("1308809838") - diag24[1].SetString("928239151") - diag24[2].SetString("495882907") - diag24[3].SetString("593757554") - diag24[4].SetString("593757554") - diag24[5].SetString("567559762") - diag24[6].SetString("1572388064") - diag24[7].SetString("1076816199") - diag24[8].SetString("652906069") - diag24[9].SetString("1871203714") - diag24[10].SetString("358820701") - diag24[11].SetString("1696335905") - diag24[12].SetString("1771481038") - diag24[13].SetString("67413782") - diag24[14].SetString("1765474848") - diag24[15].SetString("235952237") - diag24[16].SetString("111716804") - diag24[17].SetString("213766759") - diag24[18].SetString("656473656") - diag24[19].SetString("1879962596") - diag24[20].SetString("1762516106") - diag24[21].SetString("197180297") - diag24[22].SetString("2061316832") - diag24[23].SetString("1833346861") + diag24[0].SetString("2130706431") + diag24[1].SetString("1") + diag24[2].SetString("2") + diag24[3].SetString("1065353217") + diag24[4].SetString("3") + diag24[5].SetString("4") + diag24[6].SetString("1065353216") + diag24[7].SetString("2130706430") + diag24[8].SetString("2130706429") + diag24[9].SetString("2122383361") + diag24[10].SetString("1598029825") + diag24[11].SetString("1864368129") + diag24[12].SetString("1997537281") + diag24[13].SetString("2064121857") + diag24[14].SetString("2097414145") + diag24[15].SetString("2130706306") + diag24[16].SetString("8323072") + diag24[17].SetString("266338304") + diag24[18].SetString("133169152") + diag24[19].SetString("66584576") + diag24[20].SetString("33292288") + diag24[21].SetString("16646144") + diag24[22].SetString("4161536") + diag24[23].SetString("127") } diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index d79a6dc790..ea79a9f759 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -209,9 +209,7 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 16: - // TODO: use optimized diagonal as in plonky3 - // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/koala-bear/src/poseidon2.rs - // + // TODO: optimize multiplication by diag16 // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] var sum fr.Element sum.Set(&input[0]) @@ -223,9 +221,7 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { Add(&input[i], &sum) } case 24: - // TODO: use optimized diagonal as in plonky3 - // https://github.com/Plonky3/Plonky3/blob/95f9774c435a629a7331d4fbabdb3f5a2b300de0/koala-bear/src/poseidon2.rs - // + // TODO: optimize multiplication by diag24 // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24] var sum fr.Element sum.Set(&input[0]) @@ -237,7 +233,7 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { Add(&input[i], &sum) } default: - panic("only t=16,24 are supported") + panic("only Width=16,24 are supported") } } From e662ed67e61956fab9cb2a6100959d7dc9df16b6 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 12 Feb 2025 16:51:30 -0500 Subject: [PATCH 08/20] test: add regression tests --- field/babybear/poseidon2/poseidon2.go | 9 +- field/babybear/poseidon2/poseidon2_test.go | 141 ++++++++++++++++++++ field/koalabear/poseidon2/poseidon2.go | 9 +- field/koalabear/poseidon2/poseidon2_test.go | 141 ++++++++++++++++++++ 4 files changed, 286 insertions(+), 14 deletions(-) create mode 100644 field/babybear/poseidon2/poseidon2_test.go create mode 100644 field/koalabear/poseidon2/poseidon2_test.go diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index 677b396bd1..a9654bb3e4 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -179,13 +179,9 @@ func (h *Permutation) 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=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when Width = 0 mod 4, the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) @@ -204,8 +200,7 @@ func (h *Permutation) 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, +// when Width = 0 mod 4 the matrix is filled with ones except on the diagonal func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 16: diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go new file mode 100644 index 0000000000..e7f8fa73f5 --- /dev/null +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -0,0 +1,141 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "testing" + + fr "github.com/consensys/gnark-crypto/field/babybear" +) + +func TestPoseidon2Width16(t *testing.T) { + var input, expected [16]fr.Element + input[0].SetUint64(894848333) + input[1].SetUint64(1437655012) + input[2].SetUint64(1200606629) + input[3].SetUint64(1690012884) + input[4].SetUint64(71131202) + input[5].SetUint64(1749206695) + input[6].SetUint64(1717947831) + input[7].SetUint64(120589055) + input[8].SetUint64(19776022) + input[9].SetUint64(42382981) + input[10].SetUint64(1831865506) + input[11].SetUint64(724844064) + input[12].SetUint64(171220207) + input[13].SetUint64(1299207443) + input[14].SetUint64(227047920) + input[15].SetUint64(1783754913) + + expected[0].SetUint64(1932932418) + expected[1].SetUint64(829595901) + expected[2].SetUint64(263235839) + expected[3].SetUint64(1724803087) + expected[4].SetUint64(398013359) + expected[5].SetUint64(1307942763) + expected[6].SetUint64(741582701) + expected[7].SetUint64(189884028) + expected[8].SetUint64(140646799) + expected[9].SetUint64(1050576203) + expected[10].SetUint64(484216141) + expected[11].SetUint64(1945783389) + expected[12].SetUint64(1579834536) + expected[13].SetUint64(476498019) + expected[14].SetUint64(1923403878) + expected[15].SetUint64(1371705205) + + h := NewPermutation(16, 6, 12) + h.Permutation(input[:]) + for i := 0; i < h.params.Width; i++ { + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + +func TestPoseidon2Width24(t *testing.T) { + var input, expected [24]fr.Element + input[0].SetUint64(89488333) + input[1].SetUint64(143755012) + input[2].SetUint64(120006629) + input[3].SetUint64(169012884) + input[4].SetUint64(7113202) + input[5].SetUint64(174906695) + input[6].SetUint64(171747831) + input[7].SetUint64(12059055) + input[8].SetUint64(1977022) + input[9].SetUint64(4238981) + input[10].SetUint64(183865506) + input[11].SetUint64(72444064) + input[12].SetUint64(17120207) + input[13].SetUint64(129207443) + input[14].SetUint64(22747920) + input[15].SetUint64(178754913) + input[16].SetUint64(89448333) + input[17].SetUint64(143655012) + input[18].SetUint64(120606629) + input[19].SetUint64(169012884) + input[20].SetUint64(7111202) + input[21].SetUint64(174206695) + input[22].SetUint64(171947831) + input[23].SetUint64(12089055) + + expected[0].SetUint64(1220503920) + expected[1].SetUint64(1996302679) + expected[2].SetUint64(775035593) + expected[3].SetUint64(1067096005) + expected[4].SetUint64(306060441) + expected[5].SetUint64(1081859200) + expected[6].SetUint64(1873858035) + expected[7].SetUint64(152652526) + expected[8].SetUint64(1038715770) + expected[9].SetUint64(1814514529) + expected[10].SetUint64(593247443) + expected[11].SetUint64(885307855) + expected[12].SetUint64(736525514) + expected[13].SetUint64(1512324273) + expected[14].SetUint64(291057187) + expected[15].SetUint64(583117599) + expected[16].SetUint64(821267793) + expected[17].SetUint64(1597066552) + expected[18].SetUint64(375799466) + expected[19].SetUint64(667859878) + expected[20].SetUint64(354796343) + expected[21].SetUint64(1130595102) + expected[22].SetUint64(1922593937) + expected[23].SetUint64(201388428) + + h := NewPermutation(24, 6, 19) + h.Permutation(input[:]) + for i := 0; i < h.params.Width; i++ { + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + +// bench +func BenchmarkPoseidon2Width16(b *testing.B) { + h := NewPermutation(16, 6, 12) + var tmp [16]fr.Element + for i := 0; i < 16; i++ { + tmp[i].SetRandom() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} + +func BenchmarkPoseidon2Width24(b *testing.B) { + h := NewPermutation(24, 6, 19) + var tmp [24]fr.Element + for i := 0; i < 24; i++ { + tmp[i].SetRandom() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index ea79a9f759..9b9619cbcf 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -179,13 +179,9 @@ func (h *Permutation) 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=0[4], the buffer is multiplied by circ(2M4,M4,..,M4) +// when Width = 0 mod 4, the buffer is multiplied by circ(2M4,M4,..,M4) // see https://eprint.iacr.org/2023/323.pdf func (h *Permutation) matMulExternalInPlace(input []fr.Element) { - // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) @@ -204,8 +200,7 @@ func (h *Permutation) 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, +// when Width = 0 mod 4 the matrix is filled with ones except on the diagonal func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 16: diff --git a/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go new file mode 100644 index 0000000000..8f1e264395 --- /dev/null +++ b/field/koalabear/poseidon2/poseidon2_test.go @@ -0,0 +1,141 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "testing" + + fr "github.com/consensys/gnark-crypto/field/koalabear" +) + +func TestPoseidon2Width16(t *testing.T) { + var input, expected [16]fr.Element + input[0].SetUint64(894848333) + input[1].SetUint64(1437655012) + input[2].SetUint64(1200606629) + input[3].SetUint64(1690012884) + input[4].SetUint64(71131202) + input[5].SetUint64(1749206695) + input[6].SetUint64(1717947831) + input[7].SetUint64(120589055) + input[8].SetUint64(19776022) + input[9].SetUint64(42382981) + input[10].SetUint64(1831865506) + input[11].SetUint64(724844064) + input[12].SetUint64(171220207) + input[13].SetUint64(1299207443) + input[14].SetUint64(227047920) + input[15].SetUint64(1783754913) + + expected[0].SetUint64(1716108683) + expected[1].SetUint64(1764791125) + expected[2].SetUint64(71140124) + expected[3].SetUint64(832416356) + expected[4].SetUint64(1404922729) + expected[5].SetUint64(1453605171) + expected[6].SetUint64(1890660603) + expected[7].SetUint64(521230402) + expected[8].SetUint64(862072475) + expected[9].SetUint64(910754917) + expected[10].SetUint64(1347810349) + expected[11].SetUint64(2109086581) + expected[12].SetUint64(140190770) + expected[13].SetUint64(188873212) + expected[14].SetUint64(625928644) + expected[15].SetUint64(1387204876) + + h := NewPermutation(16, 6, 21) + h.Permutation(input[:]) + for i := 0; i < h.params.Width; i++ { + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + +func TestPoseidon2Width24(t *testing.T) { + var input, expected [24]fr.Element + input[0].SetUint64(89488333) + input[1].SetUint64(143755012) + input[2].SetUint64(120006629) + input[3].SetUint64(169012884) + input[4].SetUint64(7113202) + input[5].SetUint64(174906695) + input[6].SetUint64(171747831) + input[7].SetUint64(12059055) + input[8].SetUint64(1977022) + input[9].SetUint64(4238981) + input[10].SetUint64(183865506) + input[11].SetUint64(72444064) + input[12].SetUint64(17120207) + input[13].SetUint64(129207443) + input[14].SetUint64(22747920) + input[15].SetUint64(178754913) + input[16].SetUint64(89448333) + input[17].SetUint64(143655012) + input[18].SetUint64(120606629) + input[19].SetUint64(169012884) + input[20].SetUint64(7111202) + input[21].SetUint64(174206695) + input[22].SetUint64(171947831) + input[23].SetUint64(12089055) + + expected[0].SetUint64(334703116) + expected[1].SetUint64(50967207) + expected[2].SetUint64(1125931834) + expected[3].SetUint64(2111316099) + expected[4].SetUint64(1702309986) + expected[5].SetUint64(1418574077) + expected[6].SetUint64(362832271) + expected[7].SetUint64(1348216536) + expected[8].SetUint64(967163082) + expected[9].SetUint64(683427173) + expected[10].SetUint64(1758391800) + expected[11].SetUint64(613069632) + expected[12].SetUint64(854941900) + expected[13].SetUint64(571205991) + expected[14].SetUint64(1646170618) + expected[15].SetUint64(500848450) + expected[16].SetUint64(976613027) + expected[17].SetUint64(692877118) + expected[18].SetUint64(1767841745) + expected[19].SetUint64(622519577) + expected[20].SetUint64(1579142654) + expected[21].SetUint64(1295406745) + expected[22].SetUint64(239664939) + expected[23].SetUint64(1225049204) + + h := NewPermutation(24, 6, 21) + h.Permutation(input[:]) + for i := 0; i < h.params.Width; i++ { + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + +// bench +func BenchmarkPoseidon2Width16(b *testing.B) { + h := NewPermutation(16, 6, 21) + var tmp [16]fr.Element + for i := 0; i < 16; i++ { + tmp[i].SetRandom() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} + +func BenchmarkPoseidon2Width24(b *testing.B) { + h := NewPermutation(24, 6, 21) + var tmp [24]fr.Element + for i := 0; i < 24; i++ { + tmp[i].SetRandom() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} From ce98a3ca5160c3bfc4c32886bf4c9f6099f42d92 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 12 Feb 2025 18:07:24 -0500 Subject: [PATCH 09/20] perf(poseidon2/koala-baby): optimize multiplications by some constants --- field/babybear/poseidon2/hash.go | 80 +++++++++++----------- field/babybear/poseidon2/poseidon2.go | 28 +++++++- field/babybear/poseidon2/poseidon2_test.go | 80 +++++++++++----------- field/koalabear/poseidon2/poseidon2.go | 28 +++++++- 4 files changed, 132 insertions(+), 84 deletions(-) diff --git a/field/babybear/poseidon2/hash.go b/field/babybear/poseidon2/hash.go index fb9988fdb8..50d61c7ba5 100644 --- a/field/babybear/poseidon2/hash.go +++ b/field/babybear/poseidon2/hash.go @@ -40,46 +40,46 @@ func init() { }) // diagonal of internal matrix when Width=16 - diag16[0].SetString("2130706431") - diag16[1].SetString("1") - diag16[2].SetString("2") - diag16[3].SetString("1065353217") - diag16[4].SetString("3") - diag16[5].SetString("4") - diag16[6].SetString("1065353216") - diag16[7].SetString("2130706430") - diag16[8].SetString("2130706429") - diag16[9].SetString("2122383361") - diag16[10].SetString("1598029825") - diag16[11].SetString("1864368129") - diag16[12].SetString("1864368113") - diag16[13].SetString("8323072") - diag16[14].SetString("133169152") - diag16[15].SetString("266338320") + diag16[0].SetUint64(2013265919) + diag16[1].SetUint64(1) + diag16[2].SetUint64(2) + diag16[3].SetUint64(1006632961) + diag16[4].SetUint64(3) + diag16[5].SetUint64(4) + diag16[6].SetUint64(1006632960) + diag16[7].SetUint64(2013265918) + diag16[8].SetUint64(2013265917) + diag16[9].SetUint64(2005401601) + diag16[10].SetUint64(1509949441) + diag16[11].SetUint64(1761607681) + diag16[12].SetUint64(2013265906) + diag16[13].SetUint64(7864320) + diag16[14].SetUint64(125829120) + diag16[15].SetUint64(15) // diagonal of internal matrix when Width=24 - diag24[0].SetString("2130706431") - diag24[1].SetString("1") - diag24[2].SetString("2") - diag24[3].SetString("1065353217") - diag24[4].SetString("3") - diag24[5].SetString("4") - diag24[6].SetString("1065353216") - diag24[7].SetString("2130706430") - diag24[8].SetString("2130706429") - diag24[9].SetString("2122383361") - diag24[10].SetString("1598029825") - diag24[11].SetString("1864368129") - diag24[12].SetString("1997537281") - diag24[13].SetString("2114060289") - diag24[14].SetString("2126544897") - diag24[15].SetString("1864368113") - diag24[16].SetString("8323072") - diag24[17].SetString("532676608") - diag24[18].SetString("266338304") - diag24[19].SetString("133169152") - diag24[20].SetString("66584576") - diag24[21].SetString("33292288") - diag24[22].SetString("16646144") - diag24[23].SetString("266338320") + diag24[0].SetUint64(2013265919) + diag24[1].SetUint64(1) + diag24[2].SetUint64(2) + diag24[3].SetUint64(1006632961) + diag24[4].SetUint64(3) + diag24[5].SetUint64(4) + diag24[6].SetUint64(1006632960) + diag24[7].SetUint64(2013265918) + diag24[8].SetUint64(2013265917) + diag24[9].SetUint64(2005401601) + diag24[10].SetUint64(1509949441) + diag24[11].SetUint64(1761607681) + diag24[12].SetUint64(1887436801) + diag24[13].SetUint64(1997537281) + diag24[14].SetUint64(2009333761) + diag24[15].SetUint64(2013265906) + diag24[16].SetUint64(7864320) + diag24[17].SetUint64(503316480) + diag24[18].SetUint64(251658240) + diag24[19].SetUint64(125829120) + diag24[20].SetUint64(62914560) + diag24[21].SetUint64(31457280) + diag24[22].SetUint64(15728640) + diag24[23].SetUint64(15) } diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index a9654bb3e4..5a5197829a 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -211,7 +211,19 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.Width; i++ { + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + for i := 9; i < h.params.Width; i++ { input[i].Mul(&input[i], &diag16[i]). Add(&input[i], &sum) } @@ -223,7 +235,19 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.Width; i++ { + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + for i := 9; i < h.params.Width; i++ { input[i].Mul(&input[i], &diag24[i]). Add(&input[i], &sum) } diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go index e7f8fa73f5..f2e6517c54 100644 --- a/field/babybear/poseidon2/poseidon2_test.go +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -28,22 +28,22 @@ func TestPoseidon2Width16(t *testing.T) { input[14].SetUint64(227047920) input[15].SetUint64(1783754913) - expected[0].SetUint64(1932932418) - expected[1].SetUint64(829595901) - expected[2].SetUint64(263235839) - expected[3].SetUint64(1724803087) - expected[4].SetUint64(398013359) - expected[5].SetUint64(1307942763) - expected[6].SetUint64(741582701) - expected[7].SetUint64(189884028) - expected[8].SetUint64(140646799) - expected[9].SetUint64(1050576203) - expected[10].SetUint64(484216141) - expected[11].SetUint64(1945783389) - expected[12].SetUint64(1579834536) - expected[13].SetUint64(476498019) - expected[14].SetUint64(1923403878) - expected[15].SetUint64(1371705205) + expected[0].SetUint64(1585626702) + expected[1].SetUint64(682812408) + expected[2].SetUint64(989203631) + expected[3].SetUint64(672122406) + expected[4].SetUint64(1733441971) + expected[5].SetUint64(830627677) + expected[6].SetUint64(1137018900) + expected[7].SetUint64(819937675) + expected[8].SetUint64(293904298) + expected[9].SetUint64(1404355925) + expected[10].SetUint64(1710747148) + expected[11].SetUint64(1393665923) + expected[12].SetUint64(921824392) + expected[13].SetUint64(19010098) + expected[14].SetUint64(325401321) + expected[15].SetUint64(8320096) h := NewPermutation(16, 6, 12) h.Permutation(input[:]) @@ -81,30 +81,30 @@ func TestPoseidon2Width24(t *testing.T) { input[22].SetUint64(171947831) input[23].SetUint64(12089055) - expected[0].SetUint64(1220503920) - expected[1].SetUint64(1996302679) - expected[2].SetUint64(775035593) - expected[3].SetUint64(1067096005) - expected[4].SetUint64(306060441) - expected[5].SetUint64(1081859200) - expected[6].SetUint64(1873858035) - expected[7].SetUint64(152652526) - expected[8].SetUint64(1038715770) - expected[9].SetUint64(1814514529) - expected[10].SetUint64(593247443) - expected[11].SetUint64(885307855) - expected[12].SetUint64(736525514) - expected[13].SetUint64(1512324273) - expected[14].SetUint64(291057187) - expected[15].SetUint64(583117599) - expected[16].SetUint64(821267793) - expected[17].SetUint64(1597066552) - expected[18].SetUint64(375799466) - expected[19].SetUint64(667859878) - expected[20].SetUint64(354796343) - expected[21].SetUint64(1130595102) - expected[22].SetUint64(1922593937) - expected[23].SetUint64(201388428) + expected[0].SetUint64(691622623) + expected[1].SetUint64(1949384312) + expected[2].SetUint64(1088494054) + expected[3].SetUint64(301498194) + expected[4].SetUint64(1974342399) + expected[5].SetUint64(1218838167) + expected[6].SetUint64(357947909) + expected[7].SetUint64(1584217970) + expected[8].SetUint64(1302225983) + expected[9].SetUint64(546721751) + expected[10].SetUint64(1699097414) + expected[11].SetUint64(912101554) + expected[12].SetUint64(1420765283) + expected[13].SetUint64(665261051) + expected[14].SetUint64(1817636714) + expected[15].SetUint64(1030640854) + expected[16].SetUint64(1229702735) + expected[17].SetUint64(474198503) + expected[18].SetUint64(1626574166) + expected[19].SetUint64(839578306) + expected[20].SetUint64(1091316332) + expected[21].SetUint64(335812100) + expected[22].SetUint64(1488187763) + expected[23].SetUint64(701191903) h := NewPermutation(24, 6, 19) h.Permutation(input[:]) diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index 9b9619cbcf..7a7f181a8e 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -211,7 +211,19 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.Width; i++ { + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + for i := 9; i < h.params.Width; i++ { input[i].Mul(&input[i], &diag16[i]). Add(&input[i], &sum) } @@ -223,7 +235,19 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } - for i := 0; i < h.params.Width; i++ { + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + for i := 9; i < h.params.Width; i++ { input[i].Mul(&input[i], &diag24[i]). Add(&input[i], &sum) } From 9efa963670f20b98d95d03500e0a9a2f385778ac Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 13 Feb 2025 15:50:05 -0500 Subject: [PATCH 10/20] perf(poseidon2/koala-baby): mul by -1/2^n --- field/babybear/element_purego.go | 11 ++++++ field/babybear/poseidon2/poseidon2.go | 42 ++++++++++++++++++----- field/koalabear/element_purego.go | 11 ++++++ field/koalabear/poseidon2/poseidon2.go | 46 ++++++++++++++++++++------ 4 files changed, 90 insertions(+), 20 deletions(-) diff --git a/field/babybear/element_purego.go b/field/babybear/element_purego.go index d75894d966..141861c6cc 100644 --- a/field/babybear/element_purego.go +++ b/field/babybear/element_purego.go @@ -26,6 +26,17 @@ func MulBy13(x *Element) { x.Mul(x, &y) } +// Mul2ExpNegN multiplies x by -1/2^n +// +// Since the Montgomery constant is 2^32, the Montgomery form of 1/2^n is +// 2^{32-n}. Montgomery reduction works provided the input is < 2^32 so this +// works for 0 <= n <= 32. +func (z *Element) Mul2ExpNegN(x *Element, n uint32) *Element { + v := uint64(x[0]) << (32 - n) + z[0] = montReduce(v) + return z +} + func fromMont(z *Element) { _fromMontGeneric(z) } diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index 5a5197829a..b44f9cf45f 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -204,7 +204,7 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 16: - // TODO: optimize multiplication by diag16 + // mul by diag16: // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/2^27, -1/2^8, -1/16, -1/2^27] var sum fr.Element sum.Set(&input[0]) @@ -223,10 +223,18 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[6].Sub(&sum, &temp) input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) - for i := 9; i < h.params.Width; i++ { - input[i].Mul(&input[i], &diag16[i]). - Add(&input[i], &sum) - } + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 2)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 3)) + input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 27)) + input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 8)) + input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) + input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 27)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag16[i]). + // Add(&input[i], &sum) + // } case 24: // TODO: optimize multiplication by diag24 // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/2^7, 1/2^9, 1/2^27, -1/2^8, -1/4, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^27] @@ -247,10 +255,26 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[6].Sub(&sum, &temp) input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) - for i := 9; i < h.params.Width; i++ { - input[i].Mul(&input[i], &diag24[i]). - Add(&input[i], &sum) - } + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 2)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 3)) + input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 4)) + input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 7)) + input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 9)) + input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 27)) + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 2)) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 3)) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 4)) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 5)) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 6)) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 7)) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 27)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag24[i]). + // Add(&input[i], &sum) + // } default: panic("only Width=16,24 are supported") } diff --git a/field/koalabear/element_purego.go b/field/koalabear/element_purego.go index 4e808b511c..3f40f28b71 100644 --- a/field/koalabear/element_purego.go +++ b/field/koalabear/element_purego.go @@ -26,6 +26,17 @@ func MulBy13(x *Element) { x.Mul(x, &y) } +// Mul2ExpNegN multiplies x by -1/2^n +// +// Since the Montgomery constant is 2^32, the Montgomery form of 1/2^n is +// 2^{32-n}. Montgomery reduction works provided the input is < 2^32 so this +// works for 0 <= n <= 32. +func (z *Element) Mul2ExpNegN(x *Element, n uint32) *Element { + v := uint64(x[0]) << (32 - n) + z[0] = montReduce(v) + return z +} + func fromMont(z *Element) { _fromMontGeneric(z) } diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index 7a7f181a8e..c50656d5e7 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -204,13 +204,13 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 16: - // TODO: optimize multiplication by diag16 - // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] var sum fr.Element sum.Set(&input[0]) for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } + // mul by diag16: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] var temp fr.Element input[0].Sub(&sum, temp.Double(&input[0])) input[1].Add(&sum, &input[1]) @@ -223,12 +223,20 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[6].Sub(&sum, &temp) input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) - for i := 9; i < h.params.Width; i++ { - input[i].Mul(&input[i], &diag16[i]). - Add(&input[i], &sum) - } + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 3)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 24)) + input[12].Sub(&sum, temp.Mul2ExpNegN(&input[12], 8)) + input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 3)) + input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) + input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 24)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag16[i]). + // Add(&input[i], &sum) + // } case 24: - // TODO: optimize multiplication by diag24 + // mul by diag24: // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24] var sum fr.Element sum.Set(&input[0]) @@ -247,10 +255,26 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[6].Sub(&sum, &temp) input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) - for i := 9; i < h.params.Width; i++ { - input[i].Mul(&input[i], &diag24[i]). - Add(&input[i], &sum) - } + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 2)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 3)) + input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 4)) + input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 5)) + input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 6)) + input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 24)) + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 3)) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 4)) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 5)) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 6)) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 7)) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 9)) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 24)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag24[i]). + // Add(&input[i], &sum) + // } default: panic("only Width=16,24 are supported") } From 560fb44da3c700c7c37e9cb83f9381e0b6a6bc94 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 13 Feb 2025 17:36:41 -0500 Subject: [PATCH 11/20] fix: sbox --- field/babybear/poseidon2/poseidon2.go | 19 +++-- field/koalabear/poseidon2/poseidon2.go | 3 - field/koalabear/poseidon2/poseidon2_test.go | 80 ++++++++++----------- 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index b44f9cf45f..4990b9cf34 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -18,7 +18,7 @@ var ( const ( // d is the degree of the sBox - d = 3 + d = 7 ) // DegreeSBox returns the degree of the sBox function used in the Poseidon2 @@ -139,15 +139,14 @@ func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { // sBox applies the sBox on buffer[index] func (h *Permutation) sBox(index int, input []fr.Element) { - var tmp fr.Element - tmp.Set(&input[index]) - - // sbox degree is 3 - input[index].Square(&input[index]). - Square(&input[index]). - Square(&input[index]). - Square(&input[index]). - Mul(&input[index], &tmp) + var tmp1, tmp2 fr.Element + tmp1.Set(&input[index]) + tmp2.Square(&input[index]) + + // sbox degree is 7 + input[index].Square(&tmp2). + Mul(&input[index], &tmp1). + Mul(&input[index], &tmp2) } diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index c50656d5e7..adbd5ae83d 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -144,9 +144,6 @@ func (h *Permutation) sBox(index int, input []fr.Element) { // sbox degree is 3 input[index].Square(&input[index]). - Square(&input[index]). - Square(&input[index]). - Square(&input[index]). Mul(&input[index], &tmp) } diff --git a/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go index 8f1e264395..5295eedd70 100644 --- a/field/koalabear/poseidon2/poseidon2_test.go +++ b/field/koalabear/poseidon2/poseidon2_test.go @@ -28,22 +28,22 @@ func TestPoseidon2Width16(t *testing.T) { input[14].SetUint64(227047920) input[15].SetUint64(1783754913) - expected[0].SetUint64(1716108683) - expected[1].SetUint64(1764791125) - expected[2].SetUint64(71140124) - expected[3].SetUint64(832416356) - expected[4].SetUint64(1404922729) - expected[5].SetUint64(1453605171) - expected[6].SetUint64(1890660603) - expected[7].SetUint64(521230402) - expected[8].SetUint64(862072475) - expected[9].SetUint64(910754917) - expected[10].SetUint64(1347810349) - expected[11].SetUint64(2109086581) - expected[12].SetUint64(140190770) - expected[13].SetUint64(188873212) - expected[14].SetUint64(625928644) - expected[15].SetUint64(1387204876) + expected[0].SetUint64(715050894) + expected[1].SetUint64(46053073) + expected[2].SetUint64(1871489379) + expected[3].SetUint64(80574331) + expected[4].SetUint64(918832986) + expected[5].SetUint64(249835165) + expected[6].SetUint64(2075271471) + expected[7].SetUint64(284356423) + expected[8].SetUint64(1722012246) + expected[9].SetUint64(1053014425) + expected[10].SetUint64(747744298) + expected[11].SetUint64(1087535683) + expected[12].SetUint64(1042120606) + expected[13].SetUint64(373122785) + expected[14].SetUint64(67852658) + expected[15].SetUint64(407644043) h := NewPermutation(16, 6, 21) h.Permutation(input[:]) @@ -81,30 +81,30 @@ func TestPoseidon2Width24(t *testing.T) { input[22].SetUint64(171947831) input[23].SetUint64(12089055) - expected[0].SetUint64(334703116) - expected[1].SetUint64(50967207) - expected[2].SetUint64(1125931834) - expected[3].SetUint64(2111316099) - expected[4].SetUint64(1702309986) - expected[5].SetUint64(1418574077) - expected[6].SetUint64(362832271) - expected[7].SetUint64(1348216536) - expected[8].SetUint64(967163082) - expected[9].SetUint64(683427173) - expected[10].SetUint64(1758391800) - expected[11].SetUint64(613069632) - expected[12].SetUint64(854941900) - expected[13].SetUint64(571205991) - expected[14].SetUint64(1646170618) - expected[15].SetUint64(500848450) - expected[16].SetUint64(976613027) - expected[17].SetUint64(692877118) - expected[18].SetUint64(1767841745) - expected[19].SetUint64(622519577) - expected[20].SetUint64(1579142654) - expected[21].SetUint64(1295406745) - expected[22].SetUint64(239664939) - expected[23].SetUint64(1225049204) + expected[0].SetUint64(1167703879) + expected[1].SetUint64(298104798) + expected[2].SetUint64(1345209527) + expected[3].SetUint64(81383207) + expected[4].SetUint64(1387678226) + expected[5].SetUint64(518079145) + expected[6].SetUint64(1565183874) + expected[7].SetUint64(301357554) + expected[8].SetUint64(869441538) + expected[9].SetUint64(2130548890) + expected[10].SetUint64(1046947186) + expected[11].SetUint64(1913827299) + expected[12].SetUint64(1269945069) + expected[13].SetUint64(400345988) + expected[14].SetUint64(1447450717) + expected[15].SetUint64(183624397) + expected[16].SetUint64(1181407857) + expected[17].SetUint64(311808776) + expected[18].SetUint64(1358913505) + expected[19].SetUint64(95087185) + expected[20].SetUint64(249194062) + expected[21].SetUint64(1510301414) + expected[22].SetUint64(426699710) + expected[23].SetUint64(1293579823) h := NewPermutation(24, 6, 21) h.Permutation(input[:]) From c15ba6ba02174fce20e9c3ea6f9550e111ec0bfc Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 13 Feb 2025 18:08:40 -0500 Subject: [PATCH 12/20] perf: use better MDS 4x4 matrix --- field/babybear/poseidon2/poseidon2.go | 45 ++++++------ field/babybear/poseidon2/poseidon2_test.go | 80 ++++++++++----------- field/koalabear/poseidon2/poseidon2.go | 41 +++++------ field/koalabear/poseidon2/poseidon2_test.go | 80 ++++++++++----------- 4 files changed, 124 insertions(+), 122 deletions(-) diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index 4990b9cf34..d36ff639c0 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -153,28 +153,29 @@ func (h *Permutation) sBox(index int, input []fr.Element) { // matMulM4 computes // s <- M4*s // where M4= -// (5 7 1 3) -// (4 6 1 1) -// (1 3 5 7) -// (1 1 4 6) +// (2 3 1 1) +// (1 2 3 1) +// (1 1 2 3) +// (3 1 1 2) // 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 +// for the addition chain, see: +// https://github.com/Plonky3/Plonky3/blob/f91c76545cf5c4ae9182897bcc557715817bcbdc/poseidon2/src/external.rs#L43 +// this MDS matrix is more efficient than +// https://eprint.iacr.org/2023/323.pdf appendix Bb 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 - t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 - t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 - t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 - t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 - t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 - t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 - t6.Add(&t3, &t5) // t3+t4 - t7.Add(&t2, &t4) // t2+t4 - s[4*i].Set(&t6) - s[4*i+1].Set(&t5) - s[4*i+2].Set(&t7) - s[4*i+3].Set(&t4) + var t01, t23, t0123, t01123, t01233 fr.Element + t01.Add(&s[4*i], &s[4*i+1]) + t23.Add(&s[4*i+2], &s[4*i+3]) + t0123.Add(&t01, &t23) + t01123.Add(&t0123, &s[4*i+1]) + t01233.Add(&t0123, &s[4*i+3]) + // The order here is important. Need to overwrite x[0] and x[2] after x[1] and x[3]. + s[4*i+3].Double(&s[4*i]).Add(&s[4*i+3], &t01233) + s[4*i+1].Double(&s[4*i+2]).Add(&s[4*i+1], &t01123) + s[4*i].Add(&t01, &t01123) + s[4*i+2].Add(&t23, &t01233) } } @@ -203,13 +204,13 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 16: - // mul by diag16: - // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/2^27, -1/2^8, -1/16, -1/2^27] var sum fr.Element sum.Set(&input[0]) for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } + // mul by diag16: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/2^27, -1/2^8, -1/16, -1/2^27] var temp fr.Element input[0].Sub(&sum, temp.Double(&input[0])) input[1].Add(&sum, &input[1]) @@ -235,13 +236,13 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { // Add(&input[i], &sum) // } case 24: - // TODO: optimize multiplication by diag24 - // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/2^7, 1/2^9, 1/2^27, -1/2^8, -1/4, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^27] var sum fr.Element sum.Set(&input[0]) for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } + // mul by diag24: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/2^7, 1/2^9, 1/2^27, -1/2^8, -1/4, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^27] var temp fr.Element input[0].Sub(&sum, temp.Double(&input[0])) input[1].Add(&sum, &input[1]) diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go index f2e6517c54..1c805619c4 100644 --- a/field/babybear/poseidon2/poseidon2_test.go +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -28,22 +28,22 @@ func TestPoseidon2Width16(t *testing.T) { input[14].SetUint64(227047920) input[15].SetUint64(1783754913) - expected[0].SetUint64(1585626702) - expected[1].SetUint64(682812408) - expected[2].SetUint64(989203631) - expected[3].SetUint64(672122406) - expected[4].SetUint64(1733441971) - expected[5].SetUint64(830627677) - expected[6].SetUint64(1137018900) - expected[7].SetUint64(819937675) - expected[8].SetUint64(293904298) - expected[9].SetUint64(1404355925) - expected[10].SetUint64(1710747148) - expected[11].SetUint64(1393665923) - expected[12].SetUint64(921824392) - expected[13].SetUint64(19010098) - expected[14].SetUint64(325401321) - expected[15].SetUint64(8320096) + expected[0].SetUint64(686004988) + expected[1].SetUint64(1993393854) + expected[2].SetUint64(649198673) + expected[3].SetUint64(535971544) + expected[4].SetUint64(1366826513) + expected[5].SetUint64(660949458) + expected[6].SetUint64(1330020198) + expected[7].SetUint64(1216793069) + expected[8].SetUint64(1685289707) + expected[9].SetUint64(979412652) + expected[10].SetUint64(1648483392) + expected[11].SetUint64(1535256263) + expected[12].SetUint64(471827715) + expected[13].SetUint64(1779216581) + expected[14].SetUint64(435021400) + expected[15].SetUint64(321794271) h := NewPermutation(16, 6, 12) h.Permutation(input[:]) @@ -81,30 +81,30 @@ func TestPoseidon2Width24(t *testing.T) { input[22].SetUint64(171947831) input[23].SetUint64(12089055) - expected[0].SetUint64(691622623) - expected[1].SetUint64(1949384312) - expected[2].SetUint64(1088494054) - expected[3].SetUint64(301498194) - expected[4].SetUint64(1974342399) - expected[5].SetUint64(1218838167) - expected[6].SetUint64(357947909) - expected[7].SetUint64(1584217970) - expected[8].SetUint64(1302225983) - expected[9].SetUint64(546721751) - expected[10].SetUint64(1699097414) - expected[11].SetUint64(912101554) - expected[12].SetUint64(1420765283) - expected[13].SetUint64(665261051) - expected[14].SetUint64(1817636714) - expected[15].SetUint64(1030640854) - expected[16].SetUint64(1229702735) - expected[17].SetUint64(474198503) - expected[18].SetUint64(1626574166) - expected[19].SetUint64(839578306) - expected[20].SetUint64(1091316332) - expected[21].SetUint64(335812100) - expected[22].SetUint64(1488187763) - expected[23].SetUint64(701191903) + expected[0].SetUint64(22227966) + expected[1].SetUint64(1524139987) + expected[2].SetUint64(1236795493) + expected[3].SetUint64(417488017) + expected[4].SetUint64(303052642) + expected[5].SetUint64(1804964663) + expected[6].SetUint64(1517620169) + expected[7].SetUint64(698312693) + expected[8].SetUint64(630746735) + expected[9].SetUint64(119392835) + expected[10].SetUint64(1845314262) + expected[11].SetUint64(1026006786) + expected[12].SetUint64(457007576) + expected[13].SetUint64(1958919597) + expected[14].SetUint64(1671575103) + expected[15].SetUint64(852267627) + expected[16].SetUint64(1466425946) + expected[17].SetUint64(955072046) + expected[18].SetUint64(667727552) + expected[19].SetUint64(1861685997) + expected[20].SetUint64(1412369238) + expected[21].SetUint64(901015338) + expected[22].SetUint64(613670844) + expected[23].SetUint64(1807629289) h := NewPermutation(24, 6, 19) h.Permutation(input[:]) diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index adbd5ae83d..612d515d75 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -151,28 +151,29 @@ func (h *Permutation) sBox(index int, input []fr.Element) { // matMulM4 computes // s <- M4*s // where M4= -// (5 7 1 3) -// (4 6 1 1) -// (1 3 5 7) -// (1 1 4 6) +// (2 3 1 1) +// (1 2 3 1) +// (1 1 2 3) +// (3 1 1 2) // 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 +// for the addition chain, see: +// https://github.com/Plonky3/Plonky3/blob/f91c76545cf5c4ae9182897bcc557715817bcbdc/poseidon2/src/external.rs#L43 +// this MDS matrix is more efficient than +// https://eprint.iacr.org/2023/323.pdf appendix Bb 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 - t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 - t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 - t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 - t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 - t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 - t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 - t6.Add(&t3, &t5) // t3+t4 - t7.Add(&t2, &t4) // t2+t4 - s[4*i].Set(&t6) - s[4*i+1].Set(&t5) - s[4*i+2].Set(&t7) - s[4*i+3].Set(&t4) + var t01, t23, t0123, t01123, t01233 fr.Element + t01.Add(&s[4*i], &s[4*i+1]) + t23.Add(&s[4*i+2], &s[4*i+3]) + t0123.Add(&t01, &t23) + t01123.Add(&t0123, &s[4*i+1]) + t01233.Add(&t0123, &s[4*i+3]) + // The order here is important. Need to overwrite x[0] and x[2] after x[1] and x[3]. + s[4*i+3].Double(&s[4*i]).Add(&s[4*i+3], &t01233) + s[4*i+1].Double(&s[4*i+2]).Add(&s[4*i+1], &t01123) + s[4*i].Add(&t01, &t01123) + s[4*i+2].Add(&t23, &t01233) } } @@ -233,13 +234,13 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { // Add(&input[i], &sum) // } case 24: - // mul by diag24: - // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24] var sum fr.Element sum.Set(&input[0]) for i := 1; i < h.params.Width; i++ { sum.Add(&sum, &input[i]) } + // mul by diag24: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24] var temp fr.Element input[0].Sub(&sum, temp.Double(&input[0])) input[1].Add(&sum, &input[1]) diff --git a/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go index 5295eedd70..b6b8182a7c 100644 --- a/field/koalabear/poseidon2/poseidon2_test.go +++ b/field/koalabear/poseidon2/poseidon2_test.go @@ -28,22 +28,22 @@ func TestPoseidon2Width16(t *testing.T) { input[14].SetUint64(227047920) input[15].SetUint64(1783754913) - expected[0].SetUint64(715050894) - expected[1].SetUint64(46053073) - expected[2].SetUint64(1871489379) - expected[3].SetUint64(80574331) - expected[4].SetUint64(918832986) - expected[5].SetUint64(249835165) - expected[6].SetUint64(2075271471) - expected[7].SetUint64(284356423) - expected[8].SetUint64(1722012246) - expected[9].SetUint64(1053014425) - expected[10].SetUint64(747744298) - expected[11].SetUint64(1087535683) - expected[12].SetUint64(1042120606) - expected[13].SetUint64(373122785) - expected[14].SetUint64(67852658) - expected[15].SetUint64(407644043) + expected[0].SetUint64(1068619505) + expected[1].SetUint64(351695634) + expected[2].SetUint64(60433440) + expected[3].SetUint64(528072817) + expected[4].SetUint64(590262369) + expected[5].SetUint64(2004044931) + expected[6].SetUint64(1712782737) + expected[7].SetUint64(49715681) + expected[8].SetUint64(580916736) + expected[9].SetUint64(1994699298) + expected[10].SetUint64(1703437104) + expected[11].SetUint64(40370048) + expected[12].SetUint64(2025966092) + expected[13].SetUint64(1309042221) + expected[14].SetUint64(1017780027) + expected[15].SetUint64(1485419404) h := NewPermutation(16, 6, 21) h.Permutation(input[:]) @@ -81,30 +81,30 @@ func TestPoseidon2Width24(t *testing.T) { input[22].SetUint64(171947831) input[23].SetUint64(12089055) - expected[0].SetUint64(1167703879) - expected[1].SetUint64(298104798) - expected[2].SetUint64(1345209527) - expected[3].SetUint64(81383207) - expected[4].SetUint64(1387678226) - expected[5].SetUint64(518079145) - expected[6].SetUint64(1565183874) - expected[7].SetUint64(301357554) - expected[8].SetUint64(869441538) - expected[9].SetUint64(2130548890) - expected[10].SetUint64(1046947186) - expected[11].SetUint64(1913827299) - expected[12].SetUint64(1269945069) - expected[13].SetUint64(400345988) - expected[14].SetUint64(1447450717) - expected[15].SetUint64(183624397) - expected[16].SetUint64(1181407857) - expected[17].SetUint64(311808776) - expected[18].SetUint64(1358913505) - expected[19].SetUint64(95087185) - expected[20].SetUint64(249194062) - expected[21].SetUint64(1510301414) - expected[22].SetUint64(426699710) - expected[23].SetUint64(1293579823) + expected[0].SetUint64(1694135297) + expected[1].SetUint64(2025543907) + expected[2].SetUint64(964978921) + expected[3].SetUint64(1324041389) + expected[4].SetUint64(179393796) + expected[5].SetUint64(510802406) + expected[6].SetUint64(1580943853) + expected[7].SetUint64(1940006321) + expected[8].SetUint64(1206484229) + expected[9].SetUint64(1537892839) + expected[10].SetUint64(477327853) + expected[11].SetUint64(836390321) + expected[12].SetUint64(25634930) + expected[13].SetUint64(357043540) + expected[14].SetUint64(1427184987) + expected[15].SetUint64(1786247455) + expected[16].SetUint64(883104312) + expected[17].SetUint64(1214512922) + expected[18].SetUint64(153947936) + expected[19].SetUint64(513010404) + expected[20].SetUint64(1944813172) + expected[21].SetUint64(145515349) + expected[22].SetUint64(1215656796) + expected[23].SetUint64(1574719264) h := NewPermutation(24, 6, 21) h.Permutation(input[:]) From c68373cdbdf91101524f9cc8f3019e8faa0026ae Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 14 Feb 2025 17:08:02 -0500 Subject: [PATCH 13/20] chore: generify poseidon2 for small fields --- field/babybear/element_purego.go | 2 + field/babybear/poseidon2/doc.go | 2 + field/babybear/poseidon2/poseidon2.go | 5 +- field/babybear/poseidon2/poseidon2_test.go | 80 +-- field/generator/generator.go | 7 + field/generator/generator_poseidon2.go | 47 ++ .../internal/templates/element/ops_purego.go | 15 + .../internal/templates/poseidon2/doc.go.tmpl | 12 + .../templates/poseidon2/poseidon2.go.tmpl | 497 ++++++++++++++++++ field/generator/options.go | 17 +- field/goldilocks/poseidon2/doc.go | 17 + field/goldilocks/poseidon2/hash.go | 44 ++ field/goldilocks/poseidon2/poseidon2.go | 296 +++++++++++ field/goldilocks/poseidon2/poseidon2_test.go | 52 ++ field/internal/main.go | 1 + field/koalabear/element_purego.go | 2 + field/koalabear/poseidon2/doc.go | 2 + field/koalabear/poseidon2/poseidon2.go | 5 +- field/koalabear/poseidon2/poseidon2_test.go | 80 +-- 19 files changed, 1096 insertions(+), 87 deletions(-) create mode 100644 field/generator/generator_poseidon2.go create mode 100644 field/generator/internal/templates/poseidon2/doc.go.tmpl create mode 100644 field/generator/internal/templates/poseidon2/poseidon2.go.tmpl create mode 100644 field/goldilocks/poseidon2/doc.go create mode 100644 field/goldilocks/poseidon2/hash.go create mode 100644 field/goldilocks/poseidon2/poseidon2.go create mode 100644 field/goldilocks/poseidon2/poseidon2_test.go diff --git a/field/babybear/element_purego.go b/field/babybear/element_purego.go index 141861c6cc..c945436e98 100644 --- a/field/babybear/element_purego.go +++ b/field/babybear/element_purego.go @@ -31,6 +31,8 @@ func MulBy13(x *Element) { // Since the Montgomery constant is 2^32, the Montgomery form of 1/2^n is // 2^{32-n}. Montgomery reduction works provided the input is < 2^32 so this // works for 0 <= n <= 32. +// +// N.B. n must be < 33. func (z *Element) Mul2ExpNegN(x *Element, n uint32) *Element { v := uint64(x[0]) << (32 - n) z[0] = montReduce(v) diff --git a/field/babybear/poseidon2/doc.go b/field/babybear/poseidon2/doc.go index 07d7d09033..c2f3d4688e 100644 --- a/field/babybear/poseidon2/doc.go +++ b/field/babybear/poseidon2/doc.go @@ -1,6 +1,8 @@ // 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. diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index d36ff639c0..d311937cf3 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -1,6 +1,8 @@ // 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 import ( @@ -66,7 +68,7 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed 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-BABYBEAR[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) + return fmt.Sprintf("Poseidon2-babybear[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 @@ -142,7 +144,6 @@ func (h *Permutation) sBox(index int, input []fr.Element) { var tmp1, tmp2 fr.Element tmp1.Set(&input[index]) tmp2.Square(&input[index]) - // sbox degree is 7 input[index].Square(&tmp2). Mul(&input[index], &tmp1). diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go index 1c805619c4..e70944e04f 100644 --- a/field/babybear/poseidon2/poseidon2_test.go +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -28,22 +28,22 @@ func TestPoseidon2Width16(t *testing.T) { input[14].SetUint64(227047920) input[15].SetUint64(1783754913) - expected[0].SetUint64(686004988) - expected[1].SetUint64(1993393854) - expected[2].SetUint64(649198673) - expected[3].SetUint64(535971544) - expected[4].SetUint64(1366826513) - expected[5].SetUint64(660949458) - expected[6].SetUint64(1330020198) - expected[7].SetUint64(1216793069) - expected[8].SetUint64(1685289707) - expected[9].SetUint64(979412652) - expected[10].SetUint64(1648483392) - expected[11].SetUint64(1535256263) - expected[12].SetUint64(471827715) - expected[13].SetUint64(1779216581) - expected[14].SetUint64(435021400) - expected[15].SetUint64(321794271) + expected[0].SetUint64(844700463) + expected[1].SetUint64(487744727) + expected[2].SetUint64(1398916173) + expected[3].SetUint64(26581195) + expected[4].SetUint64(20225485) + expected[5].SetUint64(1676535670) + expected[6].SetUint64(574441195) + expected[7].SetUint64(1215372138) + expected[8].SetUint64(222335125) + expected[9].SetUint64(1878645310) + expected[10].SetUint64(776550835) + expected[11].SetUint64(1417481778) + expected[12].SetUint64(1849058630) + expected[13].SetUint64(1492102894) + expected[14].SetUint64(390008419) + expected[15].SetUint64(1030939362) h := NewPermutation(16, 6, 12) h.Permutation(input[:]) @@ -81,30 +81,30 @@ func TestPoseidon2Width24(t *testing.T) { input[22].SetUint64(171947831) input[23].SetUint64(12089055) - expected[0].SetUint64(22227966) - expected[1].SetUint64(1524139987) - expected[2].SetUint64(1236795493) - expected[3].SetUint64(417488017) - expected[4].SetUint64(303052642) - expected[5].SetUint64(1804964663) - expected[6].SetUint64(1517620169) - expected[7].SetUint64(698312693) - expected[8].SetUint64(630746735) - expected[9].SetUint64(119392835) - expected[10].SetUint64(1845314262) - expected[11].SetUint64(1026006786) - expected[12].SetUint64(457007576) - expected[13].SetUint64(1958919597) - expected[14].SetUint64(1671575103) - expected[15].SetUint64(852267627) - expected[16].SetUint64(1466425946) - expected[17].SetUint64(955072046) - expected[18].SetUint64(667727552) - expected[19].SetUint64(1861685997) - expected[20].SetUint64(1412369238) - expected[21].SetUint64(901015338) - expected[22].SetUint64(613670844) - expected[23].SetUint64(1807629289) + expected[0].SetUint64(1305447350) + expected[1].SetUint64(675392180) + expected[2].SetUint64(675835241) + expected[3].SetUint64(1994335439) + expected[4].SetUint64(759610375) + expected[5].SetUint64(129555205) + expected[6].SetUint64(129998266) + expected[7].SetUint64(1448498464) + expected[8].SetUint64(1541233419) + expected[9].SetUint64(911178249) + expected[10].SetUint64(911621310) + expected[11].SetUint64(216855587) + expected[12].SetUint64(1806802708) + expected[13].SetUint64(1176747538) + expected[14].SetUint64(1177190599) + expected[15].SetUint64(482424876) + expected[16].SetUint64(1797470401) + expected[17].SetUint64(1167415231) + expected[18].SetUint64(1167858292) + expected[19].SetUint64(473092569) + expected[20].SetUint64(805435295) + expected[21].SetUint64(175380125) + expected[22].SetUint64(175823186) + expected[23].SetUint64(1494323384) h := NewPermutation(24, 6, 19) h.Permutation(input[:]) diff --git a/field/generator/generator.go b/field/generator/generator.go index d741244d91..ccee9fe148 100644 --- a/field/generator/generator.go +++ b/field/generator/generator.go @@ -60,6 +60,13 @@ func GenerateFF(F *config.Field, outputDir string, options ...Option) error { } } + // generate Poseidon2 + if cfg.HasPoseidon2() { + if err := generatePoseidon2(F, outputDir); err != nil { + return err + } + } + return runFormatters(outputDir) } diff --git a/field/generator/generator_poseidon2.go b/field/generator/generator_poseidon2.go new file mode 100644 index 0000000000..9582d7c554 --- /dev/null +++ b/field/generator/generator_poseidon2.go @@ -0,0 +1,47 @@ +package generator + +import ( + "path/filepath" + + "github.com/consensys/bavard" + "github.com/consensys/gnark-crypto/field/generator/config" +) + +func generatePoseidon2(F *config.Field, outputDir string) error { + + fieldImportPath, err := getImportPath(outputDir) + if err != nil { + return err + } + + outputDir = filepath.Join(outputDir, "poseidon2") + + entries := []bavard.Entry{ + {File: filepath.Join(outputDir, "doc.go"), Templates: []string{"doc.go.tmpl"}}, + {File: filepath.Join(outputDir, "poseidon2.go"), Templates: []string{"poseidon2.go.tmpl"}}, + } + + type poseidon2TemplateData struct { + FF string + FieldPackagePath string + } + + data := &poseidon2TemplateData{ + FF: F.PackageName, + FieldPackagePath: fieldImportPath, + } + + bgen := bavard.NewBatchGenerator("Consensys Software Inc.", 2020, "consensys/gnark-crypto") + + poseidon2TemplatesRootDir, err := findTemplatesRootDir() + if err != nil { + return err + } + poseidon2TemplatesRootDir = filepath.Join(poseidon2TemplatesRootDir, "poseidon2") + + if err := bgen.GenerateWithOptions(data, "poseidon2", poseidon2TemplatesRootDir, nil, entries...); err != nil { + return err + } + + return runFormatters(outputDir) +} diff --git a/field/generator/internal/templates/element/ops_purego.go b/field/generator/internal/templates/element/ops_purego.go index 53f257a089..31d45c8d92 100644 --- a/field/generator/internal/templates/element/ops_purego.go +++ b/field/generator/internal/templates/element/ops_purego.go @@ -36,6 +36,21 @@ func MulBy{{$i}}(x *{{$.ElementName}}) { {{- end}} +{{- if $.F31}} +// Mul2ExpNegN multiplies x by -1/2^n +// +// Since the Montgomery constant is 2^32, the Montgomery form of 1/2^n is +// 2^{32-n}. Montgomery reduction works provided the input is < 2^32 so this +// works for 0 <= n <= 32. +// +// N.B. n must be < 33. +func (z *Element) Mul2ExpNegN(x *Element, n uint32) *Element { + v := uint64(x[0]) << (32 - n) + z[0] = montReduce(v) + return z +} +{{- end}} + func fromMont(z *{{.ElementName}} ) { _fromMontGeneric(z) } diff --git a/field/generator/internal/templates/poseidon2/doc.go.tmpl b/field/generator/internal/templates/poseidon2/doc.go.tmpl new file mode 100644 index 0000000000..3897419232 --- /dev/null +++ b/field/generator/internal/templates/poseidon2/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/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl b/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl new file mode 100644 index 0000000000..9c052f2aca --- /dev/null +++ b/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl @@ -0,0 +1,497 @@ +import ( + "errors" + "fmt" + + "golang.org/x/crypto/sha3" + + fr "{{ .FieldPackagePath }}" +) + +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 or (eq .FF "babybear") (eq .FF "goldilocks")}} + d = 7 + {{ else }} + d = 3 + {{ end -}} +) + +// DegreeSBox returns the degree of the sBox function used in the Poseidon2 +// permutation. +func DegreeSBox() int { + return d +} + +// 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)) + Width int + + // number of full rounds (even number) + NbFullRounds int + + // number of partial rounds + NbPartialRounds int + + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element +} + +// 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.String() + p.initRC(seed) + return &p +} + +// 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 +} + +// 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-{{.FF}}[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 +// rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 +func (p *Parameters) initRC(seed string) { + + bseed := ([]byte)(seed) + hash := sha3.NewLegacyKeccak256() + _, _ = hash.Write(bseed) + rnd := hash.Sum(nil) // pre hash before use + hash.Reset() + _, _ = hash.Write(rnd) + + 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 := 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 := 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) + } + } + 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 or (eq .FF "babybear") (eq .FF "koalabear")}} + if t != 16 && t != 24 { + panic("only Width=16,24 are supported") + } + {{- else if eq .FF "goldilocks"}} + if t != 8 { + panic("only Width=8 is supported") + } + {{- end}} + 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 or (eq .FF "babybear") (eq .FF "koalabear")}} + if t != 16 && t != 24 { + panic("only Width=16,24 are supported") + } + {{- else if eq .FF "goldilocks"}} + if t != 8 { + panic("only Width=8 is supported") + } + {{- end}} + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res +} + + +// sBox applies the sBox on buffer[index] +func (h *Permutation) sBox(index int, input []fr.Element) { + {{- if or (eq .FF "babybear") (eq .FF "goldilocks")}} + var tmp1, tmp2 fr.Element + tmp1.Set(&input[index]) + tmp2.Square(&input[index]) + // sbox degree is 7 + input[index].Square(&tmp2). + Mul(&input[index], &tmp1). + Mul(&input[index], &tmp2) + {{ else }} + var tmp fr.Element + tmp.Set(&input[index]) + // sbox degree is 3 + input[index].Square(&input[index]). + Mul(&input[index], &tmp) + {{ end }} +} + +// matMulM4 computes +// s <- M4*s +// where M4= +{{- if eq .FF "goldilocks"}} +// (5 7 1 3) +// (4 6 1 1) +// (1 3 5 7) +// (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 *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 + t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 + t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 + t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 + t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 + t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 + t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 + t6.Add(&t3, &t5) // t3+t4 + t7.Add(&t2, &t4) // t2+t4 + s[4*i].Set(&t6) + s[4*i+1].Set(&t5) + s[4*i+2].Set(&t7) + s[4*i+3].Set(&t4) + } +} +{{ else }} +// (2 3 1 1) +// (1 2 3 1) +// (1 1 2 3) +// (3 1 1 2) +// on chunks of 4 elemts on each part of the buffer +// for the addition chain, see: +// https://github.com/Plonky3/Plonky3/blob/f91c76545cf5c4ae9182897bcc557715817bcbdc/poseidon2/src/external.rs#L43 +// this MDS matrix is more efficient than +// https://eprint.iacr.org/2023/323.pdf appendix Bb +func (h *Permutation) matMulM4InPlace(s []fr.Element) { + c := len(s) / 4 + for i := 0; i < c; i++ { + var t01, t23, t0123, t01123, t01233 fr.Element + t01.Add(&s[4*i], &s[4*i+1]) + t23.Add(&s[4*i+2], &s[4*i+3]) + t0123.Add(&t01, &t23) + t01123.Add(&t0123, &s[4*i+1]) + t01233.Add(&t0123, &s[4*i+3]) + // The order here is important. Need to overwrite x[0] and x[2] after x[1] and x[3]. + s[4*i+3].Double(&s[4*i]).Add(&s[4*i+3], &t01233) + s[4*i+1].Double(&s[4*i+2]).Add(&s[4*i+1], &t01123) + s[4*i].Add(&t01, &t01123) + s[4*i+2].Add(&t23, &t01233) + } +} +{{ end }} + +// when Width = 0 mod 4, the buffer is multiplied by circ(2M4,M4,..,M4) +// see https://eprint.iacr.org/2023/323.pdf +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { + // 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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[3]) + } +} + +// when Width = 0 mod 4 the matrix is filled with ones except on the diagonal +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { +{{- if eq .FF "goldilocks"}} + switch h.params.Width { + case 8: + // 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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[3]) + } + default: + panic("only Width=8 is supported") + } +{{- else if eq .FF "koalabear"}} + switch h.params.Width { + case 16: + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + // mul by diag16: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24] + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 3)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 24)) + input[12].Sub(&sum, temp.Mul2ExpNegN(&input[12], 8)) + input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 3)) + input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) + input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 24)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag16[i]). + // Add(&input[i], &sum) + // } + case 24: + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + // mul by diag24: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24] + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 2)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 3)) + input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 4)) + input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 5)) + input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 6)) + input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 24)) + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 3)) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 4)) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 5)) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 6)) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 7)) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 9)) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 24)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag24[i]). + // Add(&input[i], &sum) + // } + default: + panic("only Width=16,24 are supported") + } +{{- else if eq .FF "babybear"}} + switch h.params.Width { + case 16: + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + // mul by diag16: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/2^27, -1/2^8, -1/16, -1/2^27] + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 2)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 3)) + input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 27)) + input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 8)) + input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) + input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 27)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag16[i]). + // Add(&input[i], &sum) + // } + case 24: + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + // mul by diag24: + // [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/2^7, 1/2^9, 1/2^27, -1/2^8, -1/4, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^27] + var temp fr.Element + input[0].Sub(&sum, temp.Double(&input[0])) + input[1].Add(&sum, &input[1]) + input[2].Add(&sum, temp.Double(&input[2])) + temp.Set(&input[3]).Halve() + input[3].Add(&sum, &temp) + input[4].Add(&sum, temp.Double(&input[4]).Add(&temp, &input[4])) + input[5].Add(&sum, temp.Double(&input[5]).Double(&temp)) + temp.Set(&input[6]).Halve() + input[6].Sub(&sum, &temp) + input[7].Sub(&sum, temp.Double(&input[7]).Add(&temp, &input[7])) + input[8].Sub(&sum, temp.Double(&input[8]).Double(&temp)) + input[9].Add(&sum, temp.Mul2ExpNegN(&input[9], 8)) + input[10].Add(&sum, temp.Mul2ExpNegN(&input[10], 2)) + input[11].Add(&sum, temp.Mul2ExpNegN(&input[11], 3)) + input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 4)) + input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 7)) + input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 9)) + input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 27)) + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 2)) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 3)) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 4)) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 5)) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 6)) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 7)) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 27)) + // naive version: + // for i := 0; i < h.params.Width; i++ { + // input[i].Mul(&input[i], &diag24[i]). + // Add(&input[i], &sum) + // } + default: + panic("only Width=16,24 are supported") + } +{{- end}} +} + +// addRoundKeyInPlace adds the round-th key to the buffer +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 *Permutation) BlockSize() int { + return fr.Bytes +} + +// Permutation applies the permutation on input, and stores the result in input. +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.NbFullRounds / 2 + for i := 0; i < rf; i++ { + // one round = matMulExternal(sBox_Full(addRoundKey)) + h.addRoundKeyInPlace(i, input) + for j := 0; j < h.params.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + 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.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.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + return nil +} + +// Compress applies the permutation on left and right and returns the right lane +// 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 + + if err := x[0].SetBytesCanonical(left); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(right); 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/field/generator/options.go b/field/generator/options.go index 5ef420f950..e6b35056ea 100644 --- a/field/generator/options.go +++ b/field/generator/options.go @@ -7,9 +7,14 @@ import ( type Option func(*generatorConfig) type generatorConfig struct { - fftConfig *config.FFT - asmConfig *config.Assembly - withSIS bool + fftConfig *config.FFT + asmConfig *config.Assembly + withSIS bool + withPoseidon2 bool +} + +func (cfg *generatorConfig) HasPoseidon2() bool { + return cfg.withPoseidon2 } func (cfg *generatorConfig) HasSIS() bool { @@ -34,6 +39,12 @@ func WithSIS() Option { } } +func WithPoseidon2() Option { + return func(opt *generatorConfig) { + opt.withPoseidon2 = true + } +} + func WithFFT(cfg *config.FFT) Option { return func(opt *generatorConfig) { opt.fftConfig = cfg diff --git a/field/goldilocks/poseidon2/doc.go b/field/goldilocks/poseidon2/doc.go new file mode 100644 index 0000000000..c2f3d4688e --- /dev/null +++ b/field/goldilocks/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/field/goldilocks/poseidon2/hash.go b/field/goldilocks/poseidon2/hash.go new file mode 100644 index 0000000000..9301032b8c --- /dev/null +++ b/field/goldilocks/poseidon2/hash.go @@ -0,0 +1,44 @@ +package poseidon2 + +import ( + "hash" + + fr "github.com/consensys/gnark-crypto/field/goldilocks" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// NewParameters returns a new set of parameters for the Poseidon2 permutation. +// The default parameters are, +// +// - width: 8 +// - nbFullRounds: 6 +// - nbPartialRounds: 17 +func NewDefaultParameters() *Parameters { + return NewParameters(8, 6, 17) +} + +var diag8 [8]fr.Element + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BABYBEAR, func() hash.Hash { + return NewMerkleDamgardHasher() + }) + + // diagonal of internal matrix when Width=8 + // same as https://github.com/Plonky3/Plonky3/blob/f91c76545cf5c4ae9182897bcc557715817bcbdc/goldilocks/src/poseidon2.rs#L54 + diag8[0].SetUint64(0xa98811a1fed4e3a5) + diag8[1].SetUint64(0x1cc48b54f377e2a0) + diag8[2].SetUint64(0xe40cd4f6c5609a26) + diag8[3].SetUint64(0x11de79ebca97a4a3) + diag8[4].SetUint64(0x9177c73d8b7e929c) + diag8[5].SetUint64(0x2a6fe8085797e791) + diag8[6].SetUint64(0x3de6e93329f8d5ad) + diag8[7].SetUint64(0x3f7af9125da962fe) +} diff --git a/field/goldilocks/poseidon2/poseidon2.go b/field/goldilocks/poseidon2/poseidon2.go new file mode 100644 index 0000000000..1c32820e92 --- /dev/null +++ b/field/goldilocks/poseidon2/poseidon2.go @@ -0,0 +1,296 @@ +// 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 + +import ( + "errors" + "fmt" + + "golang.org/x/crypto/sha3" + + fr "github.com/consensys/gnark-crypto/field/goldilocks" +) + +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. 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)) + Width int + + // number of full rounds (even number) + NbFullRounds int + + // number of partial rounds + NbPartialRounds int + + // derived round keys from the parameter seed and curve ID + RoundKeys [][]fr.Element +} + +// 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.String() + p.initRC(seed) + return &p +} + +// 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 +} + +// 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-goldilocks[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 +// rounds, cf https://eprint.iacr.org/2023/323.pdf page 9 +func (p *Parameters) initRC(seed string) { + + bseed := ([]byte)(seed) + hash := sha3.NewLegacyKeccak256() + _, _ = hash.Write(bseed) + rnd := hash.Sum(nil) // pre hash before use + hash.Reset() + _, _ = hash.Write(rnd) + + 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 := 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 := 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) + } + } + 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 != 8 { + panic("only Width=8 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 != 8 { + panic("only Width=8 is supported") + } + params := NewParametersWithSeed(t, rf, rp, seed) + res := &Permutation{params: params} + return res +} + +// sBox applies the sBox on buffer[index] +func (h *Permutation) sBox(index int, input []fr.Element) { + var tmp1, tmp2 fr.Element + tmp1.Set(&input[index]) + tmp2.Square(&input[index]) + // sbox degree is 7 + input[index].Square(&tmp2). + Mul(&input[index], &tmp1). + Mul(&input[index], &tmp2) + +} + +// matMulM4 computes +// s <- M4*s +// where M4= +// (5 7 1 3) +// (4 6 1 1) +// (1 3 5 7) +// (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 *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 + t0.Add(&s[4*i], &s[4*i+1]) // s0+s1 + t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3 + t2.Double(&s[4*i+1]).Add(&t2, &t1) // 2s1+t1 + t3.Double(&s[4*i+3]).Add(&t3, &t0) // 2s3+t0 + t4.Double(&t1).Double(&t4).Add(&t4, &t3) // 4t1+t3 + t5.Double(&t0).Double(&t5).Add(&t5, &t2) // 4t0+t2 + t6.Add(&t3, &t5) // t3+t4 + t7.Add(&t2, &t4) // t2+t4 + s[4*i].Set(&t6) + s[4*i+1].Set(&t5) + s[4*i+2].Set(&t7) + s[4*i+3].Set(&t4) + } +} + +// when Width = 0 mod 4, the buffer is multiplied by circ(2M4,M4,..,M4) +// see https://eprint.iacr.org/2023/323.pdf +func (h *Permutation) matMulExternalInPlace(input []fr.Element) { + // 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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[3]) + } +} + +// when Width = 0 mod 4 the matrix is filled with ones except on the diagonal +func (h *Permutation) matMulInternalInPlace(input []fr.Element) { + switch h.params.Width { + case 8: + // 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.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.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]) + input[4*i+3].Add(&input[4*i], &tmp[3]) + } + default: + panic("only Width=8 is supported") + } +} + +// addRoundKeyInPlace adds the round-th key to the buffer +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 *Permutation) BlockSize() int { + return fr.Bytes +} + +// Permutation applies the permutation on input, and stores the result in input. +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.NbFullRounds / 2 + for i := 0; i < rf; i++ { + // one round = matMulExternal(sBox_Full(addRoundKey)) + h.addRoundKeyInPlace(i, input) + for j := 0; j < h.params.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + 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.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.Width; j++ { + h.sBox(j, input) + } + h.matMulExternalInPlace(input) + } + + return nil +} + +// Compress applies the permutation on left and right and returns the right lane +// 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 + + if err := x[0].SetBytesCanonical(left); err != nil { + return nil, err + } + if err := x[1].SetBytesCanonical(right); 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/field/goldilocks/poseidon2/poseidon2_test.go b/field/goldilocks/poseidon2/poseidon2_test.go new file mode 100644 index 0000000000..82ce9d4a72 --- /dev/null +++ b/field/goldilocks/poseidon2/poseidon2_test.go @@ -0,0 +1,52 @@ +// Copyright 2020-2025 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package poseidon2 + +import ( + "testing" + + fr "github.com/consensys/gnark-crypto/field/goldilocks" +) + +func TestPoseidon2Width8(t *testing.T) { + var input, expected [8]fr.Element + input[0].SetUint64(5116996373749832116) + input[1].SetUint64(8931548647907683339) + input[2].SetUint64(17132360229780760684) + input[3].SetUint64(11280040044015983889) + input[4].SetUint64(11957737519043010992) + input[5].SetUint64(15695650327991256125) + input[6].SetUint64(17604752143022812942) + input[7].SetUint64(543194415197607509) + + expected[0].SetUint64(13660962709356710016) + expected[1].SetUint64(14157778087516188574) + expected[2].SetUint64(16152999951555461431) + expected[3].SetUint64(8607075105484324444) + expected[4].SetUint64(1743726080892756454) + expected[5].SetUint64(2240541459052235012) + expected[6].SetUint64(4235763323091507869) + expected[7].SetUint64(15136582546434955203) + + h := NewPermutation(8, 6, 17) + h.Permutation(input[:]) + for i := 0; i < h.params.Width; i++ { + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + +// bench +func BenchmarkPoseidon2Width8(b *testing.B) { + h := NewPermutation(8, 6, 17) + var tmp [8]fr.Element + for i := 0; i < 8; i++ { + tmp[i].SetRandom() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.Permutation(tmp[:]) + } +} diff --git a/field/internal/main.go b/field/internal/main.go index 46412585eb..077f21fea0 100644 --- a/field/internal/main.go +++ b/field/internal/main.go @@ -35,6 +35,7 @@ func main() { generator.WithASM(&config.Assembly{BuildDir: asmDirIncludePath, IncludeDir: asmDirIncludePath}), generator.WithFFT(&config.FFT{}), // TODO @gbotrel generator.WithSIS(), + generator.WithPoseidon2(), ); err != nil { panic(err) } diff --git a/field/koalabear/element_purego.go b/field/koalabear/element_purego.go index 3f40f28b71..cfcaaade6a 100644 --- a/field/koalabear/element_purego.go +++ b/field/koalabear/element_purego.go @@ -31,6 +31,8 @@ func MulBy13(x *Element) { // Since the Montgomery constant is 2^32, the Montgomery form of 1/2^n is // 2^{32-n}. Montgomery reduction works provided the input is < 2^32 so this // works for 0 <= n <= 32. +// +// N.B. n must be < 33. func (z *Element) Mul2ExpNegN(x *Element, n uint32) *Element { v := uint64(x[0]) << (32 - n) z[0] = montReduce(v) diff --git a/field/koalabear/poseidon2/doc.go b/field/koalabear/poseidon2/doc.go index 07d7d09033..c2f3d4688e 100644 --- a/field/koalabear/poseidon2/doc.go +++ b/field/koalabear/poseidon2/doc.go @@ -1,6 +1,8 @@ // 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. diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index 612d515d75..1ce0d4d402 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -1,6 +1,8 @@ // 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 import ( @@ -66,7 +68,7 @@ func NewParametersWithSeed(width, nbFullRounds, nbPartialRounds int, seed 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-KOALABEAR[t=%d,rF=%d,rP=%d,d=%d]", p.Width, p.NbFullRounds, p.NbPartialRounds, d) + return fmt.Sprintf("Poseidon2-koalabear[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 @@ -141,7 +143,6 @@ func NewPermutationWithSeed(t, rf, rp int, seed string) *Permutation { func (h *Permutation) sBox(index int, input []fr.Element) { var tmp fr.Element tmp.Set(&input[index]) - // sbox degree is 3 input[index].Square(&input[index]). Mul(&input[index], &tmp) diff --git a/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go index b6b8182a7c..645f5503c9 100644 --- a/field/koalabear/poseidon2/poseidon2_test.go +++ b/field/koalabear/poseidon2/poseidon2_test.go @@ -28,22 +28,22 @@ func TestPoseidon2Width16(t *testing.T) { input[14].SetUint64(227047920) input[15].SetUint64(1783754913) - expected[0].SetUint64(1068619505) - expected[1].SetUint64(351695634) - expected[2].SetUint64(60433440) - expected[3].SetUint64(528072817) - expected[4].SetUint64(590262369) - expected[5].SetUint64(2004044931) - expected[6].SetUint64(1712782737) - expected[7].SetUint64(49715681) - expected[8].SetUint64(580916736) - expected[9].SetUint64(1994699298) - expected[10].SetUint64(1703437104) - expected[11].SetUint64(40370048) - expected[12].SetUint64(2025966092) - expected[13].SetUint64(1309042221) - expected[14].SetUint64(1017780027) - expected[15].SetUint64(1485419404) + expected[0].SetUint64(91519247) + expected[1].SetUint64(575841157) + expected[2].SetUint64(1298206260) + expected[3].SetUint64(521836678) + expected[4].SetUint64(1576077625) + expected[5].SetUint64(2060399535) + expected[6].SetUint64(652058205) + expected[7].SetUint64(2006395056) + expected[8].SetUint64(2020046681) + expected[9].SetUint64(373662158) + expected[10].SetUint64(1096027261) + expected[11].SetUint64(319657679) + expected[12].SetUint64(612453750) + expected[13].SetUint64(1096775660) + expected[14].SetUint64(1819140763) + expected[15].SetUint64(1042771181) h := NewPermutation(16, 6, 21) h.Permutation(input[:]) @@ -81,30 +81,30 @@ func TestPoseidon2Width24(t *testing.T) { input[22].SetUint64(171947831) input[23].SetUint64(12089055) - expected[0].SetUint64(1694135297) - expected[1].SetUint64(2025543907) - expected[2].SetUint64(964978921) - expected[3].SetUint64(1324041389) - expected[4].SetUint64(179393796) - expected[5].SetUint64(510802406) - expected[6].SetUint64(1580943853) - expected[7].SetUint64(1940006321) - expected[8].SetUint64(1206484229) - expected[9].SetUint64(1537892839) - expected[10].SetUint64(477327853) - expected[11].SetUint64(836390321) - expected[12].SetUint64(25634930) - expected[13].SetUint64(357043540) - expected[14].SetUint64(1427184987) - expected[15].SetUint64(1786247455) - expected[16].SetUint64(883104312) - expected[17].SetUint64(1214512922) - expected[18].SetUint64(153947936) - expected[19].SetUint64(513010404) - expected[20].SetUint64(1944813172) - expected[21].SetUint64(145515349) - expected[22].SetUint64(1215656796) - expected[23].SetUint64(1574719264) + expected[0].SetUint64(1906604688) + expected[1].SetUint64(2099480816) + expected[2].SetUint64(1200543037) + expected[3].SetUint64(1062689237) + expected[4].SetUint64(1528807699) + expected[5].SetUint64(1721683827) + expected[6].SetUint64(822746048) + expected[7].SetUint64(684892248) + expected[8].SetUint64(1750534626) + expected[9].SetUint64(1943410754) + expected[10].SetUint64(1044472975) + expected[11].SetUint64(906619175) + expected[12].SetUint64(2084344196) + expected[13].SetUint64(146513891) + expected[14].SetUint64(1378282545) + expected[15].SetUint64(1240428745) + expected[16].SetUint64(545439326) + expected[17].SetUint64(738315454) + expected[18].SetUint64(1970084108) + expected[19].SetUint64(1832230308) + expected[20].SetUint64(1416539654) + expected[21].SetUint64(1609415782) + expected[22].SetUint64(710478003) + expected[23].SetUint64(572624203) h := NewPermutation(24, 6, 21) h.Permutation(input[:]) From 776ab57f51ef8ecaa4c894f413c813242f6fa22a Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 14 Feb 2025 18:00:42 -0500 Subject: [PATCH 14/20] chore: generify poseidon2 for curves/fr --- ecc/bls12-377/fr/poseidon2/hash.go | 7 ++- ecc/bls12-377/fr/poseidon2/poseidon2.go | 28 +-------- ecc/bls12-381/fr/poseidon2/hash.go | 37 +++++++++++ ecc/bls12-381/fr/poseidon2/poseidon2.go | 28 +-------- ecc/bls24-315/fr/poseidon2/hash.go | 37 +++++++++++ ecc/bls24-315/fr/poseidon2/poseidon2.go | 28 +-------- ecc/bls24-317/fr/poseidon2/hash.go | 37 +++++++++++ ecc/bls24-317/fr/poseidon2/poseidon2.go | 28 +-------- ecc/bn254/fr/poseidon2/hash.go | 37 +++++++++++ ecc/bn254/fr/poseidon2/poseidon2.go | 28 +-------- ecc/bw6-633/fr/poseidon2/hash.go | 37 +++++++++++ ecc/bw6-633/fr/poseidon2/poseidon2.go | 28 +-------- ecc/bw6-761/fr/poseidon2/hash.go | 37 +++++++++++ ecc/bw6-761/fr/poseidon2/poseidon2.go | 28 +-------- ecc/grumpkin/fr/poseidon2/hash.go | 37 +++++++++++ ecc/grumpkin/fr/poseidon2/poseidon2.go | 28 +-------- hash/hashes.go | 61 ++++++++++++++++--- .../crypto/hash/poseidon2/generate.go | 1 + .../hash/poseidon2/template/hash.go.tmpl | 43 +++++++++++++ .../hash/poseidon2/template/poseidon2.go.tmpl | 28 +-------- 20 files changed, 380 insertions(+), 243 deletions(-) create mode 100644 ecc/bls12-381/fr/poseidon2/hash.go create mode 100644 ecc/bls24-315/fr/poseidon2/hash.go create mode 100644 ecc/bls24-317/fr/poseidon2/hash.go create mode 100644 ecc/bn254/fr/poseidon2/hash.go create mode 100644 ecc/bw6-633/fr/poseidon2/hash.go create mode 100644 ecc/bw6-761/fr/poseidon2/hash.go create mode 100644 ecc/grumpkin/fr/poseidon2/hash.go create mode 100644 internal/generator/crypto/hash/poseidon2/template/hash.go.tmpl diff --git a/ecc/bls12-377/fr/poseidon2/hash.go b/ecc/bls12-377/fr/poseidon2/hash.go index 2af91c8d10..a77db35bde 100644 --- a/ecc/bls12-377/fr/poseidon2/hash.go +++ b/ecc/bls12-377/fr/poseidon2/hash.go @@ -1,3 +1,8 @@ +// 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 import ( @@ -11,13 +16,13 @@ import ( // 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( &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) } // GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. // The default parameters are: + // - width: 2 // - nbFullRounds: 6 // - nbPartialRounds: 26 diff --git a/ecc/bls12-377/fr/poseidon2/poseidon2.go b/ecc/bls12-377/fr/poseidon2/poseidon2.go index 485d9d96e1..959a931b18 100644 --- a/ecc/bls12-377/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-377/fr/poseidon2/poseidon2.go @@ -206,27 +206,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -241,15 +226,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/hash.go b/ecc/bls12-381/fr/poseidon2/hash.go new file mode 100644 index 0000000000..b14eb12b7d --- /dev/null +++ b/ecc/bls12-381/fr/poseidon2/hash.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: + +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 50 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 50) +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS12_381, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/ecc/bls12-381/fr/poseidon2/poseidon2.go b/ecc/bls12-381/fr/poseidon2/poseidon2.go index 49f121dff8..d16874a7ce 100644 --- a/ecc/bls12-381/fr/poseidon2/poseidon2.go +++ b/ecc/bls12-381/fr/poseidon2/poseidon2.go @@ -204,27 +204,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -239,15 +224,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/hash.go b/ecc/bls24-315/fr/poseidon2/hash.go new file mode 100644 index 0000000000..845661cf32 --- /dev/null +++ b/ecc/bls24-315/fr/poseidon2/hash.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: + +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 50 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 50) +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS24_315, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/ecc/bls24-315/fr/poseidon2/poseidon2.go b/ecc/bls24-315/fr/poseidon2/poseidon2.go index b2b368b1d6..afcaf7f69f 100644 --- a/ecc/bls24-315/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-315/fr/poseidon2/poseidon2.go @@ -204,27 +204,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -239,15 +224,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/hash.go b/ecc/bls24-317/fr/poseidon2/hash.go new file mode 100644 index 0000000000..5e7d0061fa --- /dev/null +++ b/ecc/bls24-317/fr/poseidon2/hash.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: + +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 26 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 40) +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS24_317, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/ecc/bls24-317/fr/poseidon2/poseidon2.go b/ecc/bls24-317/fr/poseidon2/poseidon2.go index 1d7231b97e..176817a964 100644 --- a/ecc/bls24-317/fr/poseidon2/poseidon2.go +++ b/ecc/bls24-317/fr/poseidon2/poseidon2.go @@ -205,27 +205,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -240,15 +225,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/hash.go b/ecc/bn254/fr/poseidon2/hash.go new file mode 100644 index 0000000000..5f83550756 --- /dev/null +++ b/ecc/bn254/fr/poseidon2/hash.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: + +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 50 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 50) +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BN254, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/ecc/bn254/fr/poseidon2/poseidon2.go b/ecc/bn254/fr/poseidon2/poseidon2.go index ea89aca78a..479daf3de9 100644 --- a/ecc/bn254/fr/poseidon2/poseidon2.go +++ b/ecc/bn254/fr/poseidon2/poseidon2.go @@ -204,27 +204,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -239,15 +224,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/hash.go b/ecc/bw6-633/fr/poseidon2/hash.go new file mode 100644 index 0000000000..c373f39ef0 --- /dev/null +++ b/ecc/bw6-633/fr/poseidon2/hash.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: + +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 50 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 50) +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BW6_633, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/ecc/bw6-633/fr/poseidon2/poseidon2.go b/ecc/bw6-633/fr/poseidon2/poseidon2.go index c887e5c5fa..4868cdd4ae 100644 --- a/ecc/bw6-633/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-633/fr/poseidon2/poseidon2.go @@ -204,27 +204,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -239,15 +224,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/hash.go b/ecc/bw6-761/fr/poseidon2/hash.go new file mode 100644 index 0000000000..b6aecf58a0 --- /dev/null +++ b/ecc/bw6-761/fr/poseidon2/hash.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: + +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 50 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 50) +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BW6_761, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/ecc/bw6-761/fr/poseidon2/poseidon2.go b/ecc/bw6-761/fr/poseidon2/poseidon2.go index 58cde40b6c..706dcdc353 100644 --- a/ecc/bw6-761/fr/poseidon2/poseidon2.go +++ b/ecc/bw6-761/fr/poseidon2/poseidon2.go @@ -204,27 +204,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -239,15 +224,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/grumpkin/fr/poseidon2/hash.go b/ecc/grumpkin/fr/poseidon2/hash.go new file mode 100644 index 0000000000..d69acc510d --- /dev/null +++ b/ecc/grumpkin/fr/poseidon2/hash.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/grumpkin/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: + +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 50 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 50) +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_GRUMPKIN, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/ecc/grumpkin/fr/poseidon2/poseidon2.go b/ecc/grumpkin/fr/poseidon2/poseidon2.go index 451b9a26dc..a08dfdaa76 100644 --- a/ecc/grumpkin/fr/poseidon2/poseidon2.go +++ b/ecc/grumpkin/fr/poseidon2/poseidon2.go @@ -204,27 +204,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -239,15 +224,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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/hash/hashes.go b/hash/hashes.go index 82a2992700..bb57f1a1dd 100644 --- a/hash/hashes.go +++ b/hash/hashes.go @@ -23,6 +23,13 @@ func RegisterHash(h Hash, new func() hash.Hash) { // Hash defines an unique identifier for a hash function. type Hash uint +/* +./ecc/bls24-317/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS24_317, func() hash.Hash { + ./ecc/bw6-761/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BW6_761, func() hash.Hash { + ./ecc/bls24-315/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS24_315, func() hash.Hash { + ./ecc/bw6-633/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BW6_633, func() hash.Hash {}) +*/ + const ( // MIMC_BN254 is the MiMC hash function for the BN254 curve. MIMC_BN254 Hash = iota @@ -40,23 +47,46 @@ const ( MIMC_BW6_633 // MIMC_GRUMPKIN is the MiMC hash function for the Grumpkin curve. MIMC_GRUMPKIN + // POSEIDON2_BLS12_377 is the Poseidon2 hash function for the BLS12-377 curve. POSEIDON2_BLS12_377 + // POSEIDON2_BLS12_381 is the Poseidon2 hash function for the BLS12-381 curve. + POSEIDON2_BLS12_381 + // POSEIDON2_BN254 is the Poseidon2 hash function for the BN254 curve. + POSEIDON2_BN254 + // POSEIDON2_GRUMPKIN is the Poseidon2 hash function for the Grumpkin curve. + POSEIDON2_GRUMPKIN + // POSEIDON2_BW6_761 is the Poseidon2 hash function for the BW6-761 curve. + POSEIDON2_BW6_761 + // POSEIDON2_BW6_633 is the Poseidon2 hash function for the BW6-633 curve. + POSEIDON2_BW6_633 + // POSEIDON2_BLS24_315 is the Poseidon2 hash function for the BLS21-315 curve. + POSEIDON2_BLS24_315 + // POSEIDON2_BLS24_317 is the Poseidon2 hash function for the BLS21-317 curve. + POSEIDON2_BLS24_317 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_GRUMPKIN: 32, + 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_GRUMPKIN: 32, + + POSEIDON2_BN254: 32, + POSEIDON2_BLS12_381: 48, POSEIDON2_BLS12_377: 48, + POSEIDON2_BW6_761: 96, + POSEIDON2_BLS24_315: 48, + POSEIDON2_BLS24_317: 48, + POSEIDON2_BW6_633: 80, + POSEIDON2_GRUMPKIN: 32, } // New initializes the hash function. This is a convenience function which does @@ -95,8 +125,23 @@ func (m Hash) String() string { return "MIMC_BW6_633" case MIMC_GRUMPKIN: return "MIMC_GRUMPKIN" + + case POSEIDON2_BN254: + return "POSEIDON2_BN254" + case POSEIDON2_BLS12_381: + return "POSEIDON2_BLS12_381" case POSEIDON2_BLS12_377: return "POSEIDON2_BLS12_377" + case POSEIDON2_BW6_761: + return "POSEIDON2_BW6_761" + case POSEIDON2_BLS24_315: + return "POSEIDON2_BLS24_315" + case POSEIDON2_BLS24_317: + return "POSEIDON2_BLS24_317" + case POSEIDON2_BW6_633: + return "POSEIDON2_BW6_633" + case POSEIDON2_GRUMPKIN: + return "POSEIDON2_GRUMPKIN" default: return "unknown hash function" } diff --git a/internal/generator/crypto/hash/poseidon2/generate.go b/internal/generator/crypto/hash/poseidon2/generate.go index 542c4ed0cc..17da47bc96 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, "hash.go"), Templates: []string{"hash.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/hash.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/hash.go.tmpl new file mode 100644 index 0000000000..9bcde9a0a6 --- /dev/null +++ b/internal/generator/crypto/hash/poseidon2/template/hash.go.tmpl @@ -0,0 +1,43 @@ +import ( + "hash" + "sync" + + "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr" + gnarkHash "github.com/consensys/gnark-crypto/hash" +) + +// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// construction with the default parameters. +func NewMerkleDamgardHasher() gnarkHash.StateStorer { + return gnarkHash.NewMerkleDamgardHasher( + &Permutation{GetDefaultParameters()}, make([]byte, fr.Bytes)) +} + +// GetDefaultParameters returns a set of parameters for the Poseidon2 permutation. +// The default parameters are: +{{ if or (eq .Name "bn254") (eq .Name "grumpkin") (eq .Name "bls12-381") (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bls24-315")}} +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 50 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 50) +{{- else if eq .Name "bls12-377"}} +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 26 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 26) +{{- else if eq .Name "bls24-317"}} +// - width: 2 +// - nbFullRounds: 6 +// - nbPartialRounds: 26 +var GetDefaultParameters = sync.OnceValue(func() *Parameters { + return NewParameters(2, 6, 40) +{{- end}} +}) + +func init() { + gnarkHash.RegisterHash(gnarkHash.POSEIDON2_{{ .EnumID }}, func() hash.Hash { + return NewMerkleDamgardHasher() + }) +} diff --git a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl index fc11ff3ecb..40adef5624 100644 --- a/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl +++ b/internal/generator/crypto/hash/poseidon2/template/poseidon2.go.tmpl @@ -216,27 +216,12 @@ func (h *Permutation) matMulExternalInPlace(input []fr.Element) { } 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.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.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]) - input[4*i+3].Add(&input[4*i], &tmp[3]) - } + panic("only Width=2,3 are supported") } } // 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, +// otherwise the matrix is filled with ones except on the diagonal. func (h *Permutation) matMulInternalInPlace(input []fr.Element) { switch h.params.Width { case 2: @@ -251,15 +236,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[1].Add(&input[1], &sum) input[2].Double(&input[2]).Add(&input[2], &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 498ce87bb8f3676dd54445864fd51b2d0575ffc8 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 17 Feb 2025 15:42:58 -0500 Subject: [PATCH 15/20] fix: remove Merkle-Damgard t=2 --- field/babybear/poseidon2/hash.go | 14 ------------ field/babybear/poseidon2/poseidon2.go | 22 ------------------- .../templates/poseidon2/poseidon2.go.tmpl | 22 ------------------- field/goldilocks/poseidon2/hash.go | 14 ------------ field/goldilocks/poseidon2/poseidon2.go | 22 ------------------- field/koalabear/poseidon2/hash.go | 14 ------------ field/koalabear/poseidon2/poseidon2.go | 22 ------------------- 7 files changed, 130 deletions(-) diff --git a/field/babybear/poseidon2/hash.go b/field/babybear/poseidon2/hash.go index 50d61c7ba5..1fadfb74a7 100644 --- a/field/babybear/poseidon2/hash.go +++ b/field/babybear/poseidon2/hash.go @@ -1,19 +1,9 @@ package poseidon2 import ( - "hash" - fr "github.com/consensys/gnark-crypto/field/babybear" - gnarkHash "github.com/consensys/gnark-crypto/hash" ) -// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard -// construction with the default parameters. -func NewMerkleDamgardHasher() gnarkHash.StateStorer { - return gnarkHash.NewMerkleDamgardHasher( - &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) -} - // NewParameters returns a new set of parameters for the Poseidon2 permutation. // The default parameters are, // @@ -35,10 +25,6 @@ var diag16 [16]fr.Element var diag24 [24]fr.Element func init() { - gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BABYBEAR, func() hash.Hash { - return NewMerkleDamgardHasher() - }) - // diagonal of internal matrix when Width=16 diag16[0].SetUint64(2013265919) diag16[1].SetUint64(1) diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index d311937cf3..f52b27c719 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -328,25 +328,3 @@ func (h *Permutation) Permutation(input []fr.Element) error { return nil } - -// Compress applies the permutation on left and right and returns the right lane -// 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 - - if err := x[0].SetBytesCanonical(left); err != nil { - return nil, err - } - if err := x[1].SetBytesCanonical(right); 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/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl b/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl index 9c052f2aca..d4eafd3d97 100644 --- a/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl +++ b/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl @@ -473,25 +473,3 @@ func (h *Permutation) Permutation(input []fr.Element) error { return nil } - -// Compress applies the permutation on left and right and returns the right lane -// 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 - - if err := x[0].SetBytesCanonical(left); err != nil { - return nil, err - } - if err := x[1].SetBytesCanonical(right); 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/field/goldilocks/poseidon2/hash.go b/field/goldilocks/poseidon2/hash.go index 9301032b8c..fce3c00616 100644 --- a/field/goldilocks/poseidon2/hash.go +++ b/field/goldilocks/poseidon2/hash.go @@ -1,19 +1,9 @@ package poseidon2 import ( - "hash" - fr "github.com/consensys/gnark-crypto/field/goldilocks" - gnarkHash "github.com/consensys/gnark-crypto/hash" ) -// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard -// construction with the default parameters. -func NewMerkleDamgardHasher() gnarkHash.StateStorer { - return gnarkHash.NewMerkleDamgardHasher( - &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) -} - // NewParameters returns a new set of parameters for the Poseidon2 permutation. // The default parameters are, // @@ -27,10 +17,6 @@ func NewDefaultParameters() *Parameters { var diag8 [8]fr.Element func init() { - gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BABYBEAR, func() hash.Hash { - return NewMerkleDamgardHasher() - }) - // diagonal of internal matrix when Width=8 // same as https://github.com/Plonky3/Plonky3/blob/f91c76545cf5c4ae9182897bcc557715817bcbdc/goldilocks/src/poseidon2.rs#L54 diag8[0].SetUint64(0xa98811a1fed4e3a5) diff --git a/field/goldilocks/poseidon2/poseidon2.go b/field/goldilocks/poseidon2/poseidon2.go index 1c32820e92..69a73e1021 100644 --- a/field/goldilocks/poseidon2/poseidon2.go +++ b/field/goldilocks/poseidon2/poseidon2.go @@ -272,25 +272,3 @@ func (h *Permutation) Permutation(input []fr.Element) error { return nil } - -// Compress applies the permutation on left and right and returns the right lane -// 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 - - if err := x[0].SetBytesCanonical(left); err != nil { - return nil, err - } - if err := x[1].SetBytesCanonical(right); 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/field/koalabear/poseidon2/hash.go b/field/koalabear/poseidon2/hash.go index 13cd84c639..e113e6c427 100644 --- a/field/koalabear/poseidon2/hash.go +++ b/field/koalabear/poseidon2/hash.go @@ -1,19 +1,9 @@ package poseidon2 import ( - "hash" - fr "github.com/consensys/gnark-crypto/field/koalabear" - gnarkHash "github.com/consensys/gnark-crypto/hash" ) -// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard -// construction with the default parameters. -func NewMerkleDamgardHasher() gnarkHash.StateStorer { - return gnarkHash.NewMerkleDamgardHasher( - &Permutation{params: NewDefaultParameters()}, make([]byte, fr.Bytes)) -} - // NewParameters returns a new set of parameters for the Poseidon2 permutation. // The default parameters are, // @@ -34,10 +24,6 @@ var diag16 [16]fr.Element var diag24 [24]fr.Element func init() { - gnarkHash.RegisterHash(gnarkHash.POSEIDON2_KOALABEAR, func() hash.Hash { - return NewMerkleDamgardHasher() - }) - // diagonal of internal matrix when Width=16 diag16[0].SetString("2130706431") diag16[1].SetString("1") diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index 1ce0d4d402..af4dbbfbd9 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -326,25 +326,3 @@ func (h *Permutation) Permutation(input []fr.Element) error { return nil } - -// Compress applies the permutation on left and right and returns the right lane -// 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 - - if err := x[0].SetBytesCanonical(left); err != nil { - return nil, err - } - if err := x[1].SetBytesCanonical(right); err != nil { - return nil, err - } - if err := h.Permutation(x[:]); err != nil { - return nil, err - } - res := x[1].Bytes() - return res[:], nil -} From 3615acde0ac3597ae19c77e0de8619a5d123554d Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 17 Feb 2025 16:33:22 -0500 Subject: [PATCH 16/20] test: add some unit tests --- field/babybear/element_test.go | 35 +++++ field/babybear/poseidon2/poseidon2_test.go | 50 ++++++ .../internal/templates/element/tests.go | 144 +++++++++++------- field/koalabear/element_test.go | 35 +++++ field/koalabear/poseidon2/hash.go | 80 +++++----- field/koalabear/poseidon2/poseidon2_test.go | 50 ++++++ 6 files changed, 301 insertions(+), 93 deletions(-) diff --git a/field/babybear/element_test.go b/field/babybear/element_test.go index 712978bdbc..1e9474f526 100644 --- a/field/babybear/element_test.go +++ b/field/babybear/element_test.go @@ -2143,6 +2143,41 @@ func TestElementJSON(t *testing.T) { assert.Equal(s, decodedS, " json with strings -> element failed") } +func TestElementMul2ExpNegN(t *testing.T) { + t.Parallel() + + parameters := gopter.DefaultTestParameters() + if testing.Short() { + parameters.MinSuccessfulTests = nbFuzzShort + } else { + parameters.MinSuccessfulTests = nbFuzz + } + + properties := gopter.NewProperties(parameters) + + genA := gen() + + properties.Property("x * 2⁻ᵏ == Mul2ExpNegN(x, k) for 0 <= k <= 32", prop.ForAll( + func(a testPairElement) bool { + + var b, e, two Element + var c [33]Element + two.SetUint64(2) + for n := 0; n < 33; n++ { + e.Exp(two, big.NewInt(int64(n))).Inverse(&e) + b.Mul(&a.element, &e) + c[n].Mul2ExpNegN(&a.element, uint32(n)) + if !c[n].Equal(&b) { + return false + } + } + return true + }, + genA, + )) + + properties.TestingRun(t, gopter.ConsoleReporter(false)) +} type testPairElement struct { element Element diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go index e70944e04f..8802bb8a41 100644 --- a/field/babybear/poseidon2/poseidon2_test.go +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -9,6 +9,56 @@ import ( fr "github.com/consensys/gnark-crypto/field/babybear" ) +func TestMulMulInternalInPlaceWidth16(t *testing.T) { + var input, expected [16]fr.Element + for i := 0; i < 16; i++ { + input[i].SetRandom() + } + + expected = input + + h := NewPermutation(16, 6, 12) + h.matMulInternalInPlace(expected[:]) + + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag16[i]). + Add(&input[i], &sum) + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + +func TestMulMulInternalInPlaceWidth24(t *testing.T) { + var input, expected [24]fr.Element + for i := 0; i < 24; i++ { + input[i].SetRandom() + } + + expected = input + + h := NewPermutation(24, 6, 19) + h.matMulInternalInPlace(expected[:]) + + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag24[i]). + Add(&input[i], &sum) + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + func TestPoseidon2Width16(t *testing.T) { var input, expected [16]fr.Element input[0].SetUint64(894848333) diff --git a/field/generator/internal/templates/element/tests.go b/field/generator/internal/templates/element/tests.go index bdcc83956c..6da4bc11ee 100644 --- a/field/generator/internal/templates/element/tests.go +++ b/field/generator/internal/templates/element/tests.go @@ -9,11 +9,11 @@ import ( "math/big" "math/bits" "fmt" - {{if .UsingP20Inverse}} - mrand "math/rand" + {{if .UsingP20Inverse}} + mrand "math/rand" {{end}} "testing" - + "github.com/leanovate/gopter" "github.com/leanovate/gopter/prop" ggen "github.com/leanovate/gopter/gen" @@ -213,7 +213,7 @@ func Benchmark{{toTitle .ElementName}}Cmp(b *testing.B) { {{- range $i := .RSquare}} {{$i}},{{end}} } - benchRes{{.ElementName}} = x + benchRes{{.ElementName}} = x benchRes{{.ElementName}}[0] = 0 b.ResetTimer() for i := 0; i < b.N; i++ { @@ -223,7 +223,7 @@ func Benchmark{{toTitle .ElementName}}Cmp(b *testing.B) { func Test{{toTitle .ElementName}}Cmp(t *testing.T) { var x, y {{.ElementName}} - + if x.Cmp(&y) != 0 { t.Fatal("x == y") } @@ -238,7 +238,7 @@ func Test{{toTitle .ElementName}}Cmp(t *testing.T) { t.Fatal("x < y") } - x = y + x = y if x.Cmp(&y) != 0 { t.Fatal("x == y") } @@ -335,7 +335,7 @@ func init() { e.Sub(&qElement, &one) staticTestValues = append(staticTestValues, e) // q - 1 e.Double(&one) - staticTestValues = append(staticTestValues, e) // 2 + staticTestValues = append(staticTestValues, e) // 2 { @@ -416,7 +416,7 @@ func Test{{toTitle .ElementName}}Reduce(t *testing.T) { properties.TestingRun(t, gopter.ConsoleReporter(false)) - + } func Test{{toTitle .ElementName}}Equal(t *testing.T) { @@ -540,8 +540,8 @@ func Test{{toTitle .ElementName}}MulByConstants(t *testing.T) { for _, c := range implemented { var constant {{.ElementName}} constant.SetUint64(uint64(c)) - - b := a.element + + b := a.element b.Mul(&b, &constant) aa := a.element @@ -551,19 +551,19 @@ func Test{{toTitle .ElementName}}MulByConstants(t *testing.T) { return false } } - + return true }, genA, )) - + properties.Property("MulBy3(x) == Mul(x, 3)", prop.ForAll( func(a testPair{{.ElementName}}) bool { var constant {{.ElementName}} constant.SetUint64(3) - b := a.element + b := a.element b.Mul(&b, &constant) MulBy3(&a.element) @@ -578,7 +578,7 @@ func Test{{toTitle .ElementName}}MulByConstants(t *testing.T) { var constant {{.ElementName}} constant.SetUint64(5) - b := a.element + b := a.element b.Mul(&b, &constant) MulBy5(&a.element) @@ -593,7 +593,7 @@ func Test{{toTitle .ElementName}}MulByConstants(t *testing.T) { var constant {{.ElementName}} constant.SetUint64(13) - b := a.element + b := a.element b.Mul(&b, &constant) MulBy13(&a.element) @@ -605,7 +605,7 @@ func Test{{toTitle .ElementName}}MulByConstants(t *testing.T) { properties.TestingRun(t, gopter.ConsoleReporter(false)) - + } func Test{{toTitle .ElementName}}Legendre(t *testing.T) { @@ -623,14 +623,14 @@ func Test{{toTitle .ElementName}}Legendre(t *testing.T) { properties.Property("legendre should output same result than big.Int.Jacobi", prop.ForAll( func(a testPair{{.ElementName}}) bool { - return a.element.Legendre() == big.Jacobi(&a.bigint, Modulus()) + return a.element.Legendre() == big.Jacobi(&a.bigint, Modulus()) }, genA, )) properties.TestingRun(t, gopter.ConsoleReporter(false)) - + } func Test{{toTitle .ElementName}}BitLen(t *testing.T) { @@ -674,8 +674,8 @@ func Test{{toTitle .ElementName}}Butterflies(t *testing.T) { properties.Property("butterfly0 == a -b; a +b", prop.ForAll( func(a,b testPair{{.ElementName}}) bool { - a0, b0 := a.element, b.element - + a0, b0 := a.element, b.element + _butterflyGeneric(&a.element, &b.element) Butterfly(&a0, &b0) @@ -712,7 +712,7 @@ func Test{{toTitle .ElementName}}LexicographicallyLargest(t *testing.T) { lResult := a.element.LexicographicallyLargest() if lResult && cmpResult == 1 { - return true + return true } if !lResult && cmpResult !=1 { return true @@ -724,7 +724,7 @@ func Test{{toTitle .ElementName}}LexicographicallyLargest(t *testing.T) { properties.TestingRun(t, gopter.ConsoleReporter(false)) - + } @@ -754,7 +754,7 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { } else { parameters.MinSuccessfulTests = nbFuzz } - + properties := gopter.NewProperties(parameters) @@ -789,7 +789,7 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { {{else}} c.{{.Op}}(&a.element, &b.element) {{end}} - var d, e big.Int + var d, e big.Int {{- if eq .Op "Div"}} d.ModInverse(&b.bigint, Modulus()) d.Mul(&d, &a.bigint).Mod(&d, Modulus()) @@ -802,7 +802,7 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { if c.BigInt(&e).Cmp(&d) != 0 { return false - } + } } // fixed elements @@ -813,8 +813,8 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { for i := range testValues { r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) + var d, e, rb big.Int + r.BigInt(&rb) var c {{.all.ElementName}} {{- if eq .Op "Div"}} @@ -841,9 +841,9 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { if c.BigInt(&e).Cmp(&d) != 0 { return false - } + } } - return true + return true }, genA, genB, @@ -882,18 +882,18 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { // test special values against special values testValues := make([]{{.all.ElementName}}, len(staticTestValues)) copy(testValues, staticTestValues) - + for i := range testValues { a := testValues[i] var aBig big.Int a.BigInt(&aBig) for j := range testValues { b := testValues[j] - var bBig, d, e big.Int + var bBig, d, e big.Int b.BigInt(&bBig) var c {{.all.ElementName}} - + {{- if eq .Op "Div"}} @@ -907,7 +907,7 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { c.{{.Op}}(&a, &b) d.{{.Op}}(&aBig, &bBig).Mod(&d, Modulus()) {{- end }} - + {{if .GenericOp}} // checking asm against generic impl var cGeneric {{.all.ElementName}} @@ -916,11 +916,11 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { t.Fatal("{{.Op}} failed special test values: asm and generic impl don't match") } {{end}} - + if c.BigInt(&e).Cmp(&d) != 0 { t.Fatal("{{.Op}} failed special test values") - } + } } } } @@ -968,7 +968,7 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { var c {{.all.ElementName}} c.{{.Op}}(&a.element) - var d, e big.Int + var d, e big.Int {{- if eq .Op "Square"}} d.Mul(&a.bigint, &a.bigint).Mod(&d, Modulus()) {{- else if eq .Op "Inverse"}} @@ -1014,7 +1014,7 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { // test special values testValues := make([]{{.all.ElementName}}, len(staticTestValues)) copy(testValues, staticTestValues) - + for i := range testValues { a := testValues[i] var aBig big.Int @@ -1022,7 +1022,7 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { var c {{.all.ElementName}} c.{{.Op}}(&a) - var d, e big.Int + var d, e big.Int {{- if eq .Op "Square"}} d.Mul(&aBig, &aBig).Mod(&d, Modulus()) {{- else if eq .Op "Inverse"}} @@ -1043,11 +1043,11 @@ func Test{{toTitle .all.ElementName}}{{.Op}}(t *testing.T) { t.Fatal("{{.Op}} failed special test values: asm and generic impl don't match") } {{end}} - + if c.BigInt(&e).Cmp(&d) != 0 { t.Fatal("{{.Op}} failed special test values") - } + } } } @@ -1182,7 +1182,7 @@ func Test{{toTitle .ElementName}}Select(t *testing.T) { var c {{.ElementName}} c.Select(condC, &a, &b) - + if condC == 0 { return c.Equal(&a) } @@ -1197,7 +1197,7 @@ func Test{{toTitle .ElementName}}Select(t *testing.T) { properties.Property("Select: having the receiver as operand should output the same result", prop.ForAll( func(a, b {{.ElementName}}, cond int64, z int8) bool { condC := combineSelectionArguments(cond, z) - + var c, d {{.ElementName}} d.Set(&a) c.Select(condC, &a, &b) @@ -1342,11 +1342,11 @@ func Test{{toTitle .ElementName}}NegativeExp(t *testing.T) { properties := gopter.NewProperties(parameters) genA := gen() - + properties.Property("x⁻ᵏ == 1/xᵏ", prop.ForAll( func(a,b testPair{{.ElementName}}) bool { - var nb, d, e big.Int + var nb, d, e big.Int nb.Neg(&b.bigint) var c {{.ElementName}} @@ -1448,15 +1448,15 @@ func Test{{toTitle .ElementName}}BatchInvert(t *testing.T) { for i:=1; i element failed") } +func TestElementMul2ExpNegN(t *testing.T) { + t.Parallel() + + parameters := gopter.DefaultTestParameters() + if testing.Short() { + parameters.MinSuccessfulTests = nbFuzzShort + } else { + parameters.MinSuccessfulTests = nbFuzz + } + + properties := gopter.NewProperties(parameters) + + genA := gen() + + properties.Property("x * 2⁻ᵏ == Mul2ExpNegN(x, k) for 0 <= k <= 32", prop.ForAll( + func(a testPairElement) bool { + + var b, e, two Element + var c [33]Element + two.SetUint64(2) + for n := 0; n < 33; n++ { + e.Exp(two, big.NewInt(int64(n))).Inverse(&e) + b.Mul(&a.element, &e) + c[n].Mul2ExpNegN(&a.element, uint32(n)) + if !c[n].Equal(&b) { + return false + } + } + return true + }, + genA, + )) + + properties.TestingRun(t, gopter.ConsoleReporter(false)) +} type testPairElement struct { element Element diff --git a/field/koalabear/poseidon2/hash.go b/field/koalabear/poseidon2/hash.go index e113e6c427..eafcaf587d 100644 --- a/field/koalabear/poseidon2/hash.go +++ b/field/koalabear/poseidon2/hash.go @@ -25,46 +25,46 @@ var diag24 [24]fr.Element func init() { // diagonal of internal matrix when Width=16 - diag16[0].SetString("2130706431") - diag16[1].SetString("1") - diag16[2].SetString("2") - diag16[3].SetString("1065353217") - diag16[4].SetString("3") - diag16[5].SetString("4") - diag16[6].SetString("1065353216") - diag16[7].SetString("2130706430") - diag16[8].SetString("2130706429") - diag16[9].SetString("2122383361") - diag16[10].SetString("1864368129") - diag16[11].SetString("2130706306") - diag16[12].SetString("8323072") - diag16[13].SetString("266338304") - diag16[14].SetString("133169152") - diag16[15].SetString("127") + diag16[0].SetUint64(2130706431) + diag16[1].SetUint64(1) + diag16[2].SetUint64(2) + diag16[3].SetUint64(1065353217) + diag16[4].SetUint64(3) + diag16[5].SetUint64(4) + diag16[6].SetUint64(1065353216) + diag16[7].SetUint64(2130706430) + diag16[8].SetUint64(2130706429) + diag16[9].SetUint64(2122383361) + diag16[10].SetUint64(1864368129) + diag16[11].SetUint64(2130706306) + diag16[12].SetUint64(8323072) + diag16[13].SetUint64(266338304) + diag16[14].SetUint64(133169152) + diag16[15].SetUint64(127) // diagonal of internal matrix when Width=24 - diag24[0].SetString("2130706431") - diag24[1].SetString("1") - diag24[2].SetString("2") - diag24[3].SetString("1065353217") - diag24[4].SetString("3") - diag24[5].SetString("4") - diag24[6].SetString("1065353216") - diag24[7].SetString("2130706430") - diag24[8].SetString("2130706429") - diag24[9].SetString("2122383361") - diag24[10].SetString("1598029825") - diag24[11].SetString("1864368129") - diag24[12].SetString("1997537281") - diag24[13].SetString("2064121857") - diag24[14].SetString("2097414145") - diag24[15].SetString("2130706306") - diag24[16].SetString("8323072") - diag24[17].SetString("266338304") - diag24[18].SetString("133169152") - diag24[19].SetString("66584576") - diag24[20].SetString("33292288") - diag24[21].SetString("16646144") - diag24[22].SetString("4161536") - diag24[23].SetString("127") + diag24[0].SetUint64(2130706431) + diag24[1].SetUint64(1) + diag24[2].SetUint64(2) + diag24[3].SetUint64(1065353217) + diag24[4].SetUint64(3) + diag24[5].SetUint64(4) + diag24[6].SetUint64(1065353216) + diag24[7].SetUint64(2130706430) + diag24[8].SetUint64(2130706429) + diag24[9].SetUint64(2122383361) + diag24[10].SetUint64(1598029825) + diag24[11].SetUint64(1864368129) + diag24[12].SetUint64(1997537281) + diag24[13].SetUint64(2064121857) + diag24[14].SetUint64(2097414145) + diag24[15].SetUint64(2130706306) + diag24[16].SetUint64(8323072) + diag24[17].SetUint64(266338304) + diag24[18].SetUint64(133169152) + diag24[19].SetUint64(66584576) + diag24[20].SetUint64(33292288) + diag24[21].SetUint64(16646144) + diag24[22].SetUint64(4161536) + diag24[23].SetUint64(127) } diff --git a/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go index 645f5503c9..c637aafa2b 100644 --- a/field/koalabear/poseidon2/poseidon2_test.go +++ b/field/koalabear/poseidon2/poseidon2_test.go @@ -9,6 +9,56 @@ import ( fr "github.com/consensys/gnark-crypto/field/koalabear" ) +func TestMulMulInternalInPlaceWidth16(t *testing.T) { + var input, expected [16]fr.Element + for i := 0; i < 16; i++ { + input[i].SetRandom() + } + + expected = input + + h := NewPermutation(16, 6, 12) + h.matMulInternalInPlace(expected[:]) + + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag16[i]). + Add(&input[i], &sum) + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + +func TestMulMulInternalInPlaceWidth24(t *testing.T) { + var input, expected [24]fr.Element + for i := 0; i < 24; i++ { + input[i].SetRandom() + } + + expected = input + + h := NewPermutation(24, 6, 19) + h.matMulInternalInPlace(expected[:]) + + var sum fr.Element + sum.Set(&input[0]) + for i := 1; i < h.params.Width; i++ { + sum.Add(&sum, &input[i]) + } + for i := 0; i < h.params.Width; i++ { + input[i].Mul(&input[i], &diag24[i]). + Add(&input[i], &sum) + if !input[i].Equal(&expected[i]) { + t.Fatal("mismatch error") + } + } +} + func TestPoseidon2Width16(t *testing.T) { var input, expected [16]fr.Element input[0].SetUint64(894848333) From 6134a361ac1795591396d851515498068d4d315f Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 17 Feb 2025 16:52:59 -0500 Subject: [PATCH 17/20] test: generate random value with SetRandom --- field/babybear/poseidon2/poseidon2_test.go | 166 ++++++++++--------- field/goldilocks/poseidon2/poseidon2_test.go | 33 ++-- field/koalabear/poseidon2/poseidon2_test.go | 166 ++++++++++--------- 3 files changed, 185 insertions(+), 180 deletions(-) diff --git a/field/babybear/poseidon2/poseidon2_test.go b/field/babybear/poseidon2/poseidon2_test.go index 8802bb8a41..c07df3651a 100644 --- a/field/babybear/poseidon2/poseidon2_test.go +++ b/field/babybear/poseidon2/poseidon2_test.go @@ -61,39 +61,40 @@ func TestMulMulInternalInPlaceWidth24(t *testing.T) { func TestPoseidon2Width16(t *testing.T) { var input, expected [16]fr.Element - input[0].SetUint64(894848333) - input[1].SetUint64(1437655012) - input[2].SetUint64(1200606629) - input[3].SetUint64(1690012884) - input[4].SetUint64(71131202) - input[5].SetUint64(1749206695) - input[6].SetUint64(1717947831) - input[7].SetUint64(120589055) - input[8].SetUint64(19776022) - input[9].SetUint64(42382981) - input[10].SetUint64(1831865506) - input[11].SetUint64(724844064) - input[12].SetUint64(171220207) - input[13].SetUint64(1299207443) - input[14].SetUint64(227047920) - input[15].SetUint64(1783754913) - - expected[0].SetUint64(844700463) - expected[1].SetUint64(487744727) - expected[2].SetUint64(1398916173) - expected[3].SetUint64(26581195) - expected[4].SetUint64(20225485) - expected[5].SetUint64(1676535670) - expected[6].SetUint64(574441195) - expected[7].SetUint64(1215372138) - expected[8].SetUint64(222335125) - expected[9].SetUint64(1878645310) - expected[10].SetUint64(776550835) - expected[11].SetUint64(1417481778) - expected[12].SetUint64(1849058630) - expected[13].SetUint64(1492102894) - expected[14].SetUint64(390008419) - expected[15].SetUint64(1030939362) + // these are random values generated by SetRandom() + input[0].SetUint64(926848709) + input[1].SetUint64(772257670) + input[2].SetUint64(775357184) + input[3].SetUint64(1501166730) + input[4].SetUint64(865948535) + input[5].SetUint64(1208358603) + input[6].SetUint64(1755902432) + input[7].SetUint64(392259314) + input[8].SetUint64(630678817) + input[9].SetUint64(1665029989) + input[10].SetUint64(1776916052) + input[11].SetUint64(36754593) + input[12].SetUint64(1920998735) + input[13].SetUint64(842665326) + input[14].SetUint64(1674852701) + input[15].SetUint64(310605518) + + expected[0].SetUint64(1725293588) + expected[1].SetUint64(301603521) + expected[2].SetUint64(45294163) + expected[3].SetUint64(1976713254) + expected[4].SetUint64(982313340) + expected[5].SetUint64(1571889194) + expected[6].SetUint64(1315579836) + expected[7].SetUint64(1233733006) + expected[8].SetUint64(1646960303) + expected[9].SetUint64(223270236) + expected[10].SetUint64(1980226799) + expected[11].SetUint64(1898379969) + expected[12].SetUint64(1638842885) + expected[13].SetUint64(215152818) + expected[14].SetUint64(1972109381) + expected[15].SetUint64(1890262551) h := NewPermutation(16, 6, 12) h.Permutation(input[:]) @@ -106,55 +107,56 @@ func TestPoseidon2Width16(t *testing.T) { func TestPoseidon2Width24(t *testing.T) { var input, expected [24]fr.Element - input[0].SetUint64(89488333) - input[1].SetUint64(143755012) - input[2].SetUint64(120006629) - input[3].SetUint64(169012884) - input[4].SetUint64(7113202) - input[5].SetUint64(174906695) - input[6].SetUint64(171747831) - input[7].SetUint64(12059055) - input[8].SetUint64(1977022) - input[9].SetUint64(4238981) - input[10].SetUint64(183865506) - input[11].SetUint64(72444064) - input[12].SetUint64(17120207) - input[13].SetUint64(129207443) - input[14].SetUint64(22747920) - input[15].SetUint64(178754913) - input[16].SetUint64(89448333) - input[17].SetUint64(143655012) - input[18].SetUint64(120606629) - input[19].SetUint64(169012884) - input[20].SetUint64(7111202) - input[21].SetUint64(174206695) - input[22].SetUint64(171947831) - input[23].SetUint64(12089055) - - expected[0].SetUint64(1305447350) - expected[1].SetUint64(675392180) - expected[2].SetUint64(675835241) - expected[3].SetUint64(1994335439) - expected[4].SetUint64(759610375) - expected[5].SetUint64(129555205) - expected[6].SetUint64(129998266) - expected[7].SetUint64(1448498464) - expected[8].SetUint64(1541233419) - expected[9].SetUint64(911178249) - expected[10].SetUint64(911621310) - expected[11].SetUint64(216855587) - expected[12].SetUint64(1806802708) - expected[13].SetUint64(1176747538) - expected[14].SetUint64(1177190599) - expected[15].SetUint64(482424876) - expected[16].SetUint64(1797470401) - expected[17].SetUint64(1167415231) - expected[18].SetUint64(1167858292) - expected[19].SetUint64(473092569) - expected[20].SetUint64(805435295) - expected[21].SetUint64(175380125) - expected[22].SetUint64(175823186) - expected[23].SetUint64(1494323384) + // these are random values generated by SetRandom() + input[0].SetUint64(60806399) + input[1].SetUint64(523046893) + input[2].SetUint64(770765907) + input[3].SetUint64(316416977) + input[4].SetUint64(214364663) + input[5].SetUint64(1341870810) + input[6].SetUint64(1556213068) + input[7].SetUint64(175271367) + input[8].SetUint64(1651721560) + input[9].SetUint64(1496696610) + input[10].SetUint64(1823989412) + input[11].SetUint64(1045720388) + input[12].SetUint64(1480044199) + input[13].SetUint64(698921269) + input[14].SetUint64(163319479) + input[15].SetUint64(1553935046) + input[16].SetUint64(1332517615) + input[17].SetUint64(1026652696) + input[18].SetUint64(1770706686) + input[19].SetUint64(1656168728) + input[20].SetUint64(1447871165) + input[21].SetUint64(1397927099) + input[22].SetUint64(641149593) + input[23].SetUint64(1002972123) + + expected[0].SetUint64(1709821405) + expected[1].SetUint64(937022526) + expected[2].SetUint64(1800355590) + expected[3].SetUint64(717229570) + expected[4].SetUint64(192445051) + expected[5].SetUint64(1432912093) + expected[6].SetUint64(282979236) + expected[7].SetUint64(1213119137) + expected[8].SetUint64(1385296506) + expected[9].SetUint64(612497627) + expected[10].SetUint64(1475830691) + expected[11].SetUint64(392704671) + expected[12].SetUint64(674871393) + expected[13].SetUint64(1915338435) + expected[14].SetUint64(765405578) + expected[15].SetUint64(1695545479) + expected[16].SetUint64(1574064346) + expected[17].SetUint64(801265467) + expected[18].SetUint64(1664598531) + expected[19].SetUint64(581472511) + expected[20].SetUint64(1662458996) + expected[21].SetUint64(889660117) + expected[22].SetUint64(1752993181) + expected[23].SetUint64(669867161) h := NewPermutation(24, 6, 19) h.Permutation(input[:]) diff --git a/field/goldilocks/poseidon2/poseidon2_test.go b/field/goldilocks/poseidon2/poseidon2_test.go index 82ce9d4a72..15aad653fd 100644 --- a/field/goldilocks/poseidon2/poseidon2_test.go +++ b/field/goldilocks/poseidon2/poseidon2_test.go @@ -11,23 +11,24 @@ import ( func TestPoseidon2Width8(t *testing.T) { var input, expected [8]fr.Element - input[0].SetUint64(5116996373749832116) - input[1].SetUint64(8931548647907683339) - input[2].SetUint64(17132360229780760684) - input[3].SetUint64(11280040044015983889) - input[4].SetUint64(11957737519043010992) - input[5].SetUint64(15695650327991256125) - input[6].SetUint64(17604752143022812942) - input[7].SetUint64(543194415197607509) + // these are random values generated by SetRandom() + input[0].SetUint64(16906858123866173649) + input[1].SetUint64(15166437626912738600) + input[2].SetUint64(5043155767520437527) + input[3].SetUint64(4803372521910203894) + input[4].SetUint64(1363381407771951133) + input[5].SetUint64(14358392110422722767) + input[6].SetUint64(16147940662011238603) + input[7].SetUint64(17042226261559028170) - expected[0].SetUint64(13660962709356710016) - expected[1].SetUint64(14157778087516188574) - expected[2].SetUint64(16152999951555461431) - expected[3].SetUint64(8607075105484324444) - expected[4].SetUint64(1743726080892756454) - expected[5].SetUint64(2240541459052235012) - expected[6].SetUint64(4235763323091507869) - expected[7].SetUint64(15136582546434955203) + expected[0].SetUint64(4831120570930915263) + expected[1].SetUint64(7858209692001460708) + expected[2].SetUint64(1117691555051945953) + expected[3].SetUint64(11282480842472032166) + expected[4].SetUint64(7526220450308569653) + expected[5].SetUint64(10553309571379115098) + expected[6].SetUint64(3812791434429600343) + expected[7].SetUint64(13977580721849686556) h := NewPermutation(8, 6, 17) h.Permutation(input[:]) diff --git a/field/koalabear/poseidon2/poseidon2_test.go b/field/koalabear/poseidon2/poseidon2_test.go index c637aafa2b..6d294c373c 100644 --- a/field/koalabear/poseidon2/poseidon2_test.go +++ b/field/koalabear/poseidon2/poseidon2_test.go @@ -61,39 +61,40 @@ func TestMulMulInternalInPlaceWidth24(t *testing.T) { func TestPoseidon2Width16(t *testing.T) { var input, expected [16]fr.Element - input[0].SetUint64(894848333) - input[1].SetUint64(1437655012) - input[2].SetUint64(1200606629) - input[3].SetUint64(1690012884) - input[4].SetUint64(71131202) - input[5].SetUint64(1749206695) - input[6].SetUint64(1717947831) - input[7].SetUint64(120589055) - input[8].SetUint64(19776022) - input[9].SetUint64(42382981) - input[10].SetUint64(1831865506) - input[11].SetUint64(724844064) - input[12].SetUint64(171220207) - input[13].SetUint64(1299207443) - input[14].SetUint64(227047920) - input[15].SetUint64(1783754913) - - expected[0].SetUint64(91519247) - expected[1].SetUint64(575841157) - expected[2].SetUint64(1298206260) - expected[3].SetUint64(521836678) - expected[4].SetUint64(1576077625) - expected[5].SetUint64(2060399535) - expected[6].SetUint64(652058205) - expected[7].SetUint64(2006395056) - expected[8].SetUint64(2020046681) - expected[9].SetUint64(373662158) - expected[10].SetUint64(1096027261) - expected[11].SetUint64(319657679) - expected[12].SetUint64(612453750) - expected[13].SetUint64(1096775660) - expected[14].SetUint64(1819140763) - expected[15].SetUint64(1042771181) + // these are random values generated by SetRandom() + input[0].SetUint64(595602690) + input[1].SetUint64(847709907) + input[2].SetUint64(543464918) + input[3].SetUint64(2007411168) + input[4].SetUint64(388763785) + input[5].SetUint64(1476043928) + input[6].SetUint64(1217186791) + input[7].SetUint64(1009172579) + input[8].SetUint64(1702185369) + input[9].SetUint64(831063788) + input[10].SetUint64(1937176007) + input[11].SetUint64(1631695539) + input[12].SetUint64(1955714534) + input[13].SetUint64(1387220004) + input[14].SetUint64(567062513) + input[15].SetUint64(331325971) + + expected[0].SetUint64(1673374760) + expected[1].SetUint64(1218464009) + expected[2].SetUint64(1826800163) + expected[3].SetUint64(216885713) + expected[4].SetUint64(583230872) + expected[5].SetUint64(128320121) + expected[6].SetUint64(736656275) + expected[7].SetUint64(1257448258) + expected[8].SetUint64(1173101920) + expected[9].SetUint64(718191169) + expected[10].SetUint64(1326527323) + expected[11].SetUint64(1847319306) + expected[12].SetUint64(934026019) + expected[13].SetUint64(479115268) + expected[14].SetUint64(1087451422) + expected[15].SetUint64(1608243405) h := NewPermutation(16, 6, 21) h.Permutation(input[:]) @@ -106,55 +107,56 @@ func TestPoseidon2Width16(t *testing.T) { func TestPoseidon2Width24(t *testing.T) { var input, expected [24]fr.Element - input[0].SetUint64(89488333) - input[1].SetUint64(143755012) - input[2].SetUint64(120006629) - input[3].SetUint64(169012884) - input[4].SetUint64(7113202) - input[5].SetUint64(174906695) - input[6].SetUint64(171747831) - input[7].SetUint64(12059055) - input[8].SetUint64(1977022) - input[9].SetUint64(4238981) - input[10].SetUint64(183865506) - input[11].SetUint64(72444064) - input[12].SetUint64(17120207) - input[13].SetUint64(129207443) - input[14].SetUint64(22747920) - input[15].SetUint64(178754913) - input[16].SetUint64(89448333) - input[17].SetUint64(143655012) - input[18].SetUint64(120606629) - input[19].SetUint64(169012884) - input[20].SetUint64(7111202) - input[21].SetUint64(174206695) - input[22].SetUint64(171947831) - input[23].SetUint64(12089055) - - expected[0].SetUint64(1906604688) - expected[1].SetUint64(2099480816) - expected[2].SetUint64(1200543037) - expected[3].SetUint64(1062689237) - expected[4].SetUint64(1528807699) - expected[5].SetUint64(1721683827) - expected[6].SetUint64(822746048) - expected[7].SetUint64(684892248) - expected[8].SetUint64(1750534626) - expected[9].SetUint64(1943410754) - expected[10].SetUint64(1044472975) - expected[11].SetUint64(906619175) - expected[12].SetUint64(2084344196) - expected[13].SetUint64(146513891) - expected[14].SetUint64(1378282545) - expected[15].SetUint64(1240428745) - expected[16].SetUint64(545439326) - expected[17].SetUint64(738315454) - expected[18].SetUint64(1970084108) - expected[19].SetUint64(1832230308) - expected[20].SetUint64(1416539654) - expected[21].SetUint64(1609415782) - expected[22].SetUint64(710478003) - expected[23].SetUint64(572624203) + // these are random values generated by SetRandom() + input[0].SetUint64(568554527) + input[1].SetUint64(1037389773) + input[2].SetUint64(974985042) + input[3].SetUint64(693745454) + input[4].SetUint64(445115978) + input[5].SetUint64(247489969) + input[6].SetUint64(1800921402) + input[7].SetUint64(380223487) + input[8].SetUint64(1663707776) + input[9].SetUint64(542110938) + input[10].SetUint64(1156833323) + input[11].SetUint64(2007942824) + input[12].SetUint64(2068171589) + input[13].SetUint64(386387355) + input[14].SetUint64(407453015) + input[15].SetUint64(806215973) + input[16].SetUint64(141351644) + input[17].SetUint64(129559919) + input[18].SetUint64(1565876180) + input[19].SetUint64(257799181) + input[20].SetUint64(1038008269) + input[21].SetUint64(1353553525) + input[22].SetUint64(410540253) + input[23].SetUint64(1602372302) + + expected[0].SetUint64(1069593679) + expected[1].SetUint64(669336983) + expected[2].SetUint64(267668141) + expected[3].SetUint64(852893120) + expected[4].SetUint64(429472123) + expected[5].SetUint64(29215427) + expected[6].SetUint64(1758253018) + expected[7].SetUint64(212771564) + expected[8].SetUint64(47248052) + expected[9].SetUint64(1777697789) + expected[10].SetUint64(1376028947) + expected[11].SetUint64(1961253926) + expected[12].SetUint64(380504530) + expected[13].SetUint64(2110954267) + expected[14].SetUint64(1709285425) + expected[15].SetUint64(163803971) + expected[16].SetUint64(1331817096) + expected[17].SetUint64(931560400) + expected[18].SetUint64(529891558) + expected[19].SetUint64(1115116537) + expected[20].SetUint64(1195889899) + expected[21].SetUint64(795633203) + expected[22].SetUint64(393964361) + expected[23].SetUint64(979189340) h := NewPermutation(24, 6, 21) h.Permutation(input[:]) From 717b7bca41d288d4d0f894ee14e6492d6db10f48 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 17 Feb 2025 17:20:37 -0500 Subject: [PATCH 18/20] style: clean nonsense --- hash/hashes.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hash/hashes.go b/hash/hashes.go index bb57f1a1dd..50536582e2 100644 --- a/hash/hashes.go +++ b/hash/hashes.go @@ -23,13 +23,6 @@ func RegisterHash(h Hash, new func() hash.Hash) { // Hash defines an unique identifier for a hash function. type Hash uint -/* -./ecc/bls24-317/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS24_317, func() hash.Hash { - ./ecc/bw6-761/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BW6_761, func() hash.Hash { - ./ecc/bls24-315/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BLS24_315, func() hash.Hash { - ./ecc/bw6-633/fr/poseidon2/hash.go: gnarkHash.RegisterHash(gnarkHash.POSEIDON2_BW6_633, func() hash.Hash {}) -*/ - const ( // MIMC_BN254 is the MiMC hash function for the BN254 curve. MIMC_BN254 Hash = iota From 24035b399d018f0017df24bcb01009d4b4b6dcd7 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 17 Feb 2025 17:37:22 -0500 Subject: [PATCH 19/20] fix: silence gosec --- field/babybear/poseidon2/poseidon2.go | 28 ++++------ .../templates/poseidon2/poseidon2.go.tmpl | 54 ++++++------------- field/koalabear/poseidon2/poseidon2.go | 26 +++------ 3 files changed, 34 insertions(+), 74 deletions(-) diff --git a/field/babybear/poseidon2/poseidon2.go b/field/babybear/poseidon2/poseidon2.go index f52b27c719..e5d5412935 100644 --- a/field/babybear/poseidon2/poseidon2.go +++ b/field/babybear/poseidon2/poseidon2.go @@ -231,11 +231,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 8)) input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 27)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag16[i]). - // Add(&input[i], &sum) - // } case 24: var sum fr.Element sum.Set(&input[0]) @@ -262,20 +257,15 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 4)) input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 7)) input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 9)) - input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 27)) - input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) - input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 2)) - input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 3)) - input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 4)) - input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 5)) - input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 6)) - input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 7)) - input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 27)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag24[i]). - // Add(&input[i], &sum) - // } + input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 27)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 2)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 3)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 4)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 5)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 6)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 7)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 27)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) default: panic("only Width=16,24 are supported") } diff --git a/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl b/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl index d4eafd3d97..be9e18b57c 100644 --- a/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl +++ b/field/generator/internal/templates/poseidon2/poseidon2.go.tmpl @@ -298,11 +298,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 3)) input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 24)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag16[i]). - // Add(&input[i], &sum) - // } case 24: var sum fr.Element sum.Set(&input[0]) @@ -330,19 +325,14 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 5)) input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 6)) input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 24)) - input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) - input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 3)) - input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 4)) - input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 5)) - input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 6)) - input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 7)) - input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 9)) - input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 24)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag24[i]). - // Add(&input[i], &sum) - // } + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 3)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 4)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 5)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 6)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 7)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 9)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 24)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) default: panic("only Width=16,24 are supported") } @@ -375,11 +365,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 8)) input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 27)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag16[i]). - // Add(&input[i], &sum) - // } case 24: var sum fr.Element sum.Set(&input[0]) @@ -406,20 +391,15 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[12].Add(&sum, temp.Mul2ExpNegN(&input[12], 4)) input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 7)) input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 9)) - input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 27)) - input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) - input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 2)) - input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 3)) - input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 4)) - input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 5)) - input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 6)) - input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 7)) - input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 27)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag24[i]). - // Add(&input[i], &sum) - // } + input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 27)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 2)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 3)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 4)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 5)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 6)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 7)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 27)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) default: panic("only Width=16,24 are supported") } diff --git a/field/koalabear/poseidon2/poseidon2.go b/field/koalabear/poseidon2/poseidon2.go index af4dbbfbd9..54222831c1 100644 --- a/field/koalabear/poseidon2/poseidon2.go +++ b/field/koalabear/poseidon2/poseidon2.go @@ -229,11 +229,6 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[13].Sub(&sum, temp.Mul2ExpNegN(&input[13], 3)) input[14].Sub(&sum, temp.Mul2ExpNegN(&input[14], 4)) input[15].Sub(&sum, temp.Mul2ExpNegN(&input[15], 24)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag16[i]). - // Add(&input[i], &sum) - // } case 24: var sum fr.Element sum.Set(&input[0]) @@ -261,19 +256,14 @@ func (h *Permutation) matMulInternalInPlace(input []fr.Element) { input[13].Add(&sum, temp.Mul2ExpNegN(&input[13], 5)) input[14].Add(&sum, temp.Mul2ExpNegN(&input[14], 6)) input[15].Add(&sum, temp.Mul2ExpNegN(&input[15], 24)) - input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) - input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 3)) - input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 4)) - input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 5)) - input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 6)) - input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 7)) - input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 9)) - input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 24)) - // naive version: - // for i := 0; i < h.params.Width; i++ { - // input[i].Mul(&input[i], &diag24[i]). - // Add(&input[i], &sum) - // } + input[16].Sub(&sum, temp.Mul2ExpNegN(&input[16], 8)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[17].Sub(&sum, temp.Mul2ExpNegN(&input[17], 3)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[18].Sub(&sum, temp.Mul2ExpNegN(&input[18], 4)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[19].Sub(&sum, temp.Mul2ExpNegN(&input[19], 5)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[20].Sub(&sum, temp.Mul2ExpNegN(&input[20], 6)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[21].Sub(&sum, temp.Mul2ExpNegN(&input[21], 7)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[22].Sub(&sum, temp.Mul2ExpNegN(&input[22], 9)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) + input[23].Sub(&sum, temp.Mul2ExpNegN(&input[23], 24)) //nolint: gosec // incorrectly flagged by gosec as out of bounds read (G602) default: panic("only Width=16,24 are supported") } From 6cb4a2bdcde7e4733b4a59bbe43c5293e4cd9c09 Mon Sep 17 00:00:00 2001 From: Arya Tabaie <15056835+Tabaie@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:14:24 -0600 Subject: [PATCH 20/20] style: gofmt --- hash/hashes.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hash/hashes.go b/hash/hashes.go index 1236e6efa6..140b674b6f 100644 --- a/hash/hashes.go +++ b/hash/hashes.go @@ -78,14 +78,14 @@ var digestSize = []uint8{ MIMC_BW6_633: 80, MIMC_GRUMPKIN: 32, - POSEIDON2_BN254: 32, - POSEIDON2_BLS12_381: 48, - POSEIDON2_BLS12_377: 48, - POSEIDON2_BW6_761: 96, - POSEIDON2_BLS24_315: 48, - POSEIDON2_BLS24_317: 48, - POSEIDON2_BW6_633: 80, - POSEIDON2_GRUMPKIN: 32, + POSEIDON2_BN254: 32, + POSEIDON2_BLS12_381: 48, + POSEIDON2_BLS12_377: 48, + POSEIDON2_BW6_761: 96, + POSEIDON2_BLS24_315: 48, + POSEIDON2_BLS24_317: 48, + POSEIDON2_BW6_633: 80, + POSEIDON2_GRUMPKIN: 32, POSEIDON2_KOALABEAR: 4, POSEIDON2_BABYBEAR: 4,