Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ jobs:
for test in $(go test -list='Fuzz.*' github.com/elliottech/poseidon_crypto/int | grep ^Fuzz); do
go test -fuzz="^${test}$" -fuzztime=30s github.com/elliottech/poseidon_crypto/int
done
- name: Run fuzzy ecgfp5 tests
run: |
for test in $(go test -list='Fuzz.*' github.com/elliottech/poseidon_crypto/curve/ecgfp5 | grep ^Fuzz); do
go test -fuzz="^${test}$" -fuzztime=30s github.com/elliottech/poseidon_crypto/curve/ecgfp5
done

lint:
name: Lint
Expand Down
23 changes: 19 additions & 4 deletions curve/ecgfp5/scalar_field.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ func ScalarElementFromLittleEndianBytes(data []byte) ECgFp5Scalar {
value[i] = binary.LittleEndian.Uint64(data[i*8:])
}

if !value.IsCanonical() {
panic("trying to deserialize non-canonical bytes")
bigValue := ToNonCanonicalBigInt(value)
if bigValue.Cmp(ORDER) < 0 {
return value
}

return value
return FromNonCanonicalBigInt(bigValue)
}

func (s ECgFp5Scalar) SplitTo4BitLimbs() [80]uint8 {
Expand Down Expand Up @@ -163,12 +164,26 @@ func Select(c uint64, a0, a1 ECgFp5Scalar) ECgFp5Scalar {
}

func (s ECgFp5Scalar) Add(rhs ECgFp5Scalar) ECgFp5Scalar {
if !s.IsCanonical() {
panic("Add: first operand 's' must be canonical (< n)")
}
if !rhs.IsCanonical() {
panic("Add: second operand 'rhs' must be canonical (< n)")
}

r0 := s.AddInner(rhs)
r1, c := r0.SubInner(N) // one reduce is enough if s < n and rhs < n
return Select(c, r1, r0)
}

func (s *ECgFp5Scalar) Sub(rhs ECgFp5Scalar) ECgFp5Scalar {
if !s.IsCanonical() {
panic("Sub: first operand 's' must be canonical (< n)")
}
if !rhs.IsCanonical() {
panic("Sub: second operand 'rhs' must be canonical (< n)")
}

r0, c := s.SubInner(rhs)
r1 := r0.AddInner(N) // one add is enough if s < n and rhs < n
return Select(c, r0, r1)
Expand Down Expand Up @@ -242,7 +257,7 @@ func FromGfp5(fp5 gFp5.Element) ECgFp5Scalar {
result := new(big.Int)
for i := 4; i >= 0; i-- {
result.Lsh(result, 64)
result.Or(result, new(big.Int).SetUint64(fp5[i].ToCanonicalUint64())) // it always fit to
result.Or(result, new(big.Int).SetUint64(fp5[i].ToCanonicalUint64()))
}

return FromNonCanonicalBigInt(result)
Expand Down
98 changes: 95 additions & 3 deletions curve/ecgfp5/scalar_field_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ecgfp5

import (
"math/big"
"testing"

g "github.com/elliottech/poseidon_crypto/field/goldilocks"
Expand All @@ -24,6 +25,40 @@ func TestSerdes(t *testing.T) {
}
}

func TestScalarElementFromLittleEndianBytesReduces(t *testing.T) {
// Create a byte array that represents a scalar larger than the order
bigScalar := new(big.Int).Add(ORDER, big.NewInt(1234567890))
leBytes := bigScalar.Bytes()
s := ScalarElementFromLittleEndianBytes(leBytes)

if ToNonCanonicalBigInt(s).Cmp(ORDER) != -1 {
t.Fatalf("Expected scalar to be reduced modulo order, but got %v", ToNonCanonicalBigInt(s))
}
}

func FuzzSerdes(f *testing.F) {
f.Add([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40})
f.Add(ORDER.Bytes())

f.Fuzz(func(t *testing.T, a []byte) {
// take 40 bytes only
if len(a) > 40 {
a = a[:40]
} else if len(a) < 40 {
// pad with zeros
a = append(a, make([]byte, 40-len(a))...)
}
s := ScalarElementFromLittleEndianBytes(a)

b := s.ToLittleEndianBytes()
ss := ScalarElementFromLittleEndianBytes(b)

if !s.Equals(ss) {
t.Fatalf("Serdes mismatch: %v != %v", s, ss)
}
})
}

func TestSplitTo4LimbBits(t *testing.T) {
scalar := ECgFp5Scalar{
6950590877883398434,
Expand Down Expand Up @@ -120,12 +155,31 @@ func TestAddScalar(t *testing.T) {
}
}

func FuzzAddScalar(f *testing.F) {
f.Add([]byte{1, 2, 3, 4}, []byte{5, 6, 7, 8})
f.Add(ORDER.Bytes(), ORDER.Bytes())

f.Fuzz(func(t *testing.T, a []byte, b []byte) {
aBig := new(big.Int).SetBytes(a)
scalar1 := FromNonCanonicalBigInt(aBig)
bBig := new(big.Int).SetBytes(b)
scalar2 := FromNonCanonicalBigInt(bBig)

result := scalar1.Add(scalar2)
resultBig := FromNonCanonicalBigInt(new(big.Int).Add(aBig, bBig))

if !result.Equals(resultBig) {
t.Fatalf("Addition mismatch: %v + %v != %v", scalar1, scalar2, result)
}
})
}

func TestSub(t *testing.T) {
scalar1 := ECgFp5Scalar{1, 2, 0, 0, 0}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x0FFFFFFFFFFFFFFF}

result := scalar1.Sub(scalar2)
expectedValues := ECgFp5Scalar{0xe80fd996948bffe3, 0xe8885c39d724a09e, 0x7fffffe6cfb80639, 0x7ffffff100000016, 0x7ffffffd80000007}
expectedValues := ECgFp5Scalar{0xe80fd996948bffe3, 0xe8885c39d724a09e, 0x7fffffe6cfb80639, 0x7ffffff100000016, 8070450521510510599}

for i := 0; i < 5; i++ {
if result[i] != expectedValues[i] {
Expand All @@ -134,6 +188,25 @@ func TestSub(t *testing.T) {
}
}

func FuzzSubScalar(f *testing.F) {
f.Add([]byte{1, 2, 3, 4}, []byte{5, 6, 7, 8})
f.Add(ORDER.Bytes(), ORDER.Bytes())

f.Fuzz(func(t *testing.T, a []byte, b []byte) {
aBig := new(big.Int).SetBytes(a)
scalar1 := FromNonCanonicalBigInt(aBig)
bBig := new(big.Int).SetBytes(b)
scalar2 := FromNonCanonicalBigInt(bBig)

result := scalar1.Sub(scalar2)
resultBig := FromNonCanonicalBigInt(new(big.Int).Sub(aBig, bBig))

if !result.Equals(resultBig) {
t.Fatalf("Subtraction mismatch: %v - %v != %v", scalar1, scalar2, result)
}
})
}

func TestSelect(t *testing.T) {
a0 := ECgFp5Scalar{1, 2, 3, 4, 5}
a1 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFB}
Expand All @@ -155,7 +228,7 @@ func TestSelect(t *testing.T) {

func TestMontyMul(t *testing.T) {
scalar1 := ECgFp5Scalar{1, 2, 3, 4, 5}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF} // montymul can work with non-canonical inputs

result := scalar1.MontyMul(scalar2)
expectedValues := ECgFp5Scalar{10974894505036100890, 7458803775930281466, 744239893213209819, 3396127080529349464, 5979369289905897562}
Expand Down Expand Up @@ -192,6 +265,25 @@ func TestMul(t *testing.T) {
}
}

func FuzzMulScalar(f *testing.F) {
f.Add([]byte{1, 2, 3, 4}, []byte{5, 6, 7, 8})
f.Add(ORDER.Bytes(), ORDER.Bytes())

f.Fuzz(func(t *testing.T, a []byte, b []byte) {
aBig := new(big.Int).SetBytes(a)
scalar1 := FromNonCanonicalBigInt(aBig)
bBig := new(big.Int).SetBytes(b)
scalar2 := FromNonCanonicalBigInt(bBig)

result := scalar1.Mul(scalar2)
resultBig := FromNonCanonicalBigInt(new(big.Int).Mul(aBig, bBig))

if !result.Equals(resultBig) {
t.Fatalf("Multiplication mismatch: %v * %v != %v", scalar1, scalar2, result)
}
})
}

func TestRecodeSigned(t *testing.T) {
var ss [50]int32
scalar := ECgFp5Scalar{
Expand Down
8 changes: 8 additions & 0 deletions signature/schnorr/schnorr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ func TestBytes(t *testing.T) {
if err := Validate(pk.ToLittleEndianBytes(), hashedMsg.ToLittleEndianBytes(), sig2.ToBytes()); err != nil {
t.Fatalf("Signature is invalid")
}

// Works with non-canonical inputs
sig3 := sig2
sig3.S = sig3.S.AddInner(curve.N)
sig3.E = sig3.E.AddInner(curve.N)
if err := Validate(pk.ToLittleEndianBytes(), hashedMsg.ToLittleEndianBytes(), sig3.ToBytes()); err != nil {
t.Fatalf("Signature is invalid")
}
}

func BenchmarkSignatureVerify(b *testing.B) {
Expand Down