Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 8 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ jobs:

- name: Run fuzzy field tests
run: |
go test -tags=fuzz -fuzz=Fuzz -fuzztime=30s github.com/elliottech/poseidon_crypto/field
for test in $(go test -list='Fuzz.*' github.com/elliottech/poseidon_crypto/field | grep ^Fuzz); do
go test -fuzz="^${test}$" -fuzztime=30s github.com/elliottech/poseidon_crypto/field
done
- name: Run fuzzy signature tests
run: |
go test -tags=fuzz -fuzz=Fuzz -fuzztime=30s github.com/elliottech/poseidon_crypto/signature/schnorr
Expand All @@ -41,6 +43,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
24 changes: 23 additions & 1 deletion field/field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ var inputs = []uint64{
9223372036854775798, 9223372036854775799, 9223372036854775800, 9223372036854775801, 9223372036854775802, 9223372036854775803, 9223372036854775804, 9223372036854775805,
9223372036854775806, 9223372036854775807, 9223372036854775808, 9223372036854775809, 9223372036854775810, 9223372036854775811, 9223372036854775812, 9223372036854775813,
9223372036854775814, 9223372036854775815, 9223372036854775816, 9223372036854775817, 18446744069414584311, 18446744069414584312, 18446744069414584313, 18446744069414584314,
18446744069414584315, 18446744069414584316, 18446744069414584317, 18446744069414584318, 18446744069414584319, 18446744069414584320,
18446744069414584315, 18446744069414584316, 18446744069414584317, 18446744069414584318, 18446744069414584319, 18446744069414584320, 18446744069414584321, 18446744069414584323,
math.MaxUint64,
}

func NewBigInt(x uint64) *big.Int {
Expand Down Expand Up @@ -427,6 +428,27 @@ func FuzzTestF(f *testing.F) {
})
}

func FuzzEquivalenceF(f *testing.F) {
f.Add(uint64(0))
f.Add(uint64(1))
f.Add(g.ORDER - 1)
f.Add(g.ORDER + 1)
f.Add(uint64(math.MaxUint64))

f.Fuzz(func(t *testing.T, val uint64) {
fVal := g.GoldilocksField(val)
gVal := g.FromUint64(val)

if fVal.ToCanonicalUint64() != gVal.Uint64() {
t.Fatalf("FromUint64: Expected %d to be equal, but got %d and %d", val, fVal.ToCanonicalUint64(), gVal.Uint64())
}

if g.SquareF(fVal).ToCanonicalUint64() != new(g.Element).Square(&gVal).Uint64() {
t.Fatalf("Square: Expected square of %d to be equal, but got %d and %d", val, g.SquareF(fVal).ToCanonicalUint64(), new(g.Element).Square(&gVal).Uint64())
}
})
}

// Quintic extension tests

func TestQuinticExtensionAddSubMulSquare(t *testing.T) {
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