From fc4995518efe8f616f57b715bd74168b01e3d65e Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Wed, 28 Dec 2022 19:12:15 +0100 Subject: [PATCH 1/4] Add Scalar.LessOrEqual() Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- internal/nist/scalar.go | 29 +++++++++++++++++++++++++---- internal/ristretto/scalar.go | 25 +++++++++++++++++++++++++ scalar.go | 9 +++++++++ tests/scalar_test.go | 27 +++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/internal/nist/scalar.go b/internal/nist/scalar.go index 5462ac3..2a9e847 100644 --- a/internal/nist/scalar.go +++ b/internal/nist/scalar.go @@ -9,6 +9,7 @@ package nist import ( + "crypto/subtle" "errors" "fmt" "math/big" @@ -124,12 +125,32 @@ func (s *Scalar) Equal(scalar internal.Scalar) int { sc := s.assert(scalar) - switch s.s.Cmp(&sc.s) { - case 0: - return 1 - default: + return subtle.ConstantTimeCompare(s.s.Bytes(), sc.s.Bytes()) +} + +// LessOrEqual returns 1 if s <= scalar, and 0 otherwise. +func (s *Scalar) LessOrEqual(scalar internal.Scalar) int { + sc := s.assert(scalar) + + ienc := s.Encode() + jenc := sc.Encode() + + leni := len(ienc) + if leni != len(jenc) { + panic(internal.ErrParamScalarLength) + } + + var res bool + + for i := 0; i < leni; i++ { + res = res || (ienc[i] > jenc[i]) + } + + if res { return 0 } + + return 1 } // IsZero returns whether the scalar is 0. diff --git a/internal/ristretto/scalar.go b/internal/ristretto/scalar.go index b5d11ad..8827559 100644 --- a/internal/ristretto/scalar.go +++ b/internal/ristretto/scalar.go @@ -123,6 +123,31 @@ func (s *Scalar) Equal(scalar internal.Scalar) int { return s.scalar.Equal(&sc.scalar) } +// LessOrEqual returns 1 if s <= scalar and 0 otherwise. +func (s *Scalar) LessOrEqual(scalar internal.Scalar) int { + sc := assert(scalar) + + ienc := s.Encode() + jenc := sc.Encode() + + i := len(ienc) + if i != len(jenc) { + panic(internal.ErrParamScalarLength) + } + + var res bool + + for i--; i >= 0; i-- { + res = res || (ienc[i] > jenc[i]) + } + + if res { + return 0 + } + + return 1 +} + // IsZero returns whether the scalar is 0. func (s *Scalar) IsZero() bool { return s.scalar.Equal(ristretto255.NewScalar().Zero()) == 1 diff --git a/scalar.go b/scalar.go index bb862b5..9c1df9c 100644 --- a/scalar.go +++ b/scalar.go @@ -91,6 +91,15 @@ func (s *Scalar) Equal(scalar *Scalar) int { return s.Scalar.Equal(scalar.Scalar) } +// LessOrEqual returns 1 if s <= scalar, and 0 otherwise. +func (s *Scalar) LessOrEqual(scalar *Scalar) int { + if scalar == nil { + return 0 + } + + return s.Scalar.LessOrEqual(scalar.Scalar) +} + // IsZero returns whether the scalar is 0. func (s *Scalar) IsZero() bool { return s.Scalar.IsZero() diff --git a/tests/scalar_test.go b/tests/scalar_test.go index a5275d8..cb6cc20 100644 --- a/tests/scalar_test.go +++ b/tests/scalar_test.go @@ -118,6 +118,7 @@ func TestScalar_Arithmetic(t *testing.T) { scalarTestZero(t, group.id) scalarTestOne(t, group.id) scalarTestEqual(t, group.id) + scalarTestLessOrEqual(t, group.id) scalarTestRandom(t, group.id) scalarTestAdd(t, group.id) scalarTestSubtract(t, group.id) @@ -183,6 +184,32 @@ func scalarTestEqual(t *testing.T, g crypto.Group) { } } +func scalarTestLessOrEqual(t *testing.T, g crypto.Group) { + zero := g.NewScalar().Zero() + one := g.NewScalar().One() + two := g.NewScalar().One().Add(one) + + if zero.LessOrEqual(one) != 1 { + t.Fatal("expected 0 < 1") + } + + if one.LessOrEqual(two) != 1 { + t.Fatal("expected 1 < 2") + } + + if one.LessOrEqual(zero) == 1 { + t.Fatal("expected 1 > 0") + } + + if two.LessOrEqual(one) == 1 { + t.Fatal("expected 2 > 1") + } + + if two.LessOrEqual(two) != 1 { + t.Fatal("expected 2 == 2") + } +} + func scalarTestAdd(t *testing.T, g crypto.Group) { r := g.NewScalar().Random() cpy := r.Copy() From b2d87fe7c48d732c693320e364f3e23a122f104c Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Wed, 28 Dec 2022 19:22:22 +0100 Subject: [PATCH 2/4] Add Scalar.LessOrEqual() Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- internal/scalar.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/scalar.go b/internal/scalar.go index ccfc360..07a652b 100644 --- a/internal/scalar.go +++ b/internal/scalar.go @@ -38,6 +38,9 @@ type Scalar interface { // Equal returns 1 if the scalars are equal, and 0 otherwise. Equal(Scalar) int + // LessOrEqual returns 1 if s <= scalar, and 0 otherwise. + LessOrEqual(scalar Scalar) int + // IsZero returns whether the scalar is 0. IsZero() bool From ad882358e75dc0bed4fa45eb7fa3d8d2246635af Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:36:37 +0100 Subject: [PATCH 3/4] add pow() and fix some comments Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- internal/nist/scalar.go | 32 ++++++++++++++++++++++++-------- internal/ristretto/scalar.go | 21 +++++++++++++++++++++ internal/scalar.go | 3 +++ scalar.go | 11 +++++++++++ tests/scalar_test.go | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 8 deletions(-) diff --git a/internal/nist/scalar.go b/internal/nist/scalar.go index 2a9e847..f0d1208 100644 --- a/internal/nist/scalar.go +++ b/internal/nist/scalar.go @@ -51,19 +51,19 @@ func (s *Scalar) assert(scalar internal.Scalar) *Scalar { return _sc } -// Zero sets the scalar to 0, and returns it. +// Zero sets s to 0, and returns it. func (s *Scalar) Zero() internal.Scalar { s.s.Set(zero) return s } -// One sets the scalar to 1, and returns it. +// One sets s to 1, and returns it. func (s *Scalar) One() internal.Scalar { s.s.Set(one) return s } -// Random sets the current scalar to a new random scalar and returns it. +// Random sets s to a new random scalar and returns it. // The random source is crypto/rand, and this functions is guaranteed to return a non-zero scalar. func (s *Scalar) Random() internal.Scalar { for { @@ -75,7 +75,7 @@ func (s *Scalar) Random() internal.Scalar { } } -// Add returns the sum of the scalars, and does not change the receiver. +// Add returns s+scalar, and returns s. func (s *Scalar) Add(scalar internal.Scalar) internal.Scalar { if scalar == nil { return s @@ -87,7 +87,7 @@ func (s *Scalar) Add(scalar internal.Scalar) internal.Scalar { return s } -// Subtract returns the difference between the scalars, and does not change the receiver. +// Subtract returns s-scalar, and returns s. func (s *Scalar) Subtract(scalar internal.Scalar) internal.Scalar { if scalar == nil { return s @@ -99,7 +99,7 @@ func (s *Scalar) Subtract(scalar internal.Scalar) internal.Scalar { return s } -// Multiply returns the multiplication of the scalars, and does not change the receiver. +// Multiply sets s to s*scalar, and returns s. func (s *Scalar) Multiply(scalar internal.Scalar) internal.Scalar { if scalar == nil { return s.Zero() @@ -111,13 +111,29 @@ func (s *Scalar) Multiply(scalar internal.Scalar) internal.Scalar { return s } -// Invert returns the scalar's modular inverse ( 1 / scalar ), and does not change the receiver. +// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1. +func (s *Scalar) Pow(scalar internal.Scalar) internal.Scalar { + if scalar == nil || scalar.IsZero() { + return s.One() + } + + if scalar.Equal(scalar.Copy().One()) == 1 { + return s + } + + sc := s.assert(scalar) + s.field.exponent(&s.s, &s.s, &sc.s) + + return s +} + +// Invert sets s to its modular inverse ( 1 / s ). func (s *Scalar) Invert() internal.Scalar { s.field.inv(&s.s, &s.s) return s } -// Equal returns 1 if the scalars are equal, and 0 otherwise. +// Equal returns 1 if the s == scalar are equal, and 0 otherwise. func (s *Scalar) Equal(scalar internal.Scalar) int { if scalar == nil { return 0 diff --git a/internal/ristretto/scalar.go b/internal/ristretto/scalar.go index 8827559..3faeeb9 100644 --- a/internal/ristretto/scalar.go +++ b/internal/ristretto/scalar.go @@ -106,6 +106,27 @@ func (s *Scalar) Multiply(scalar internal.Scalar) internal.Scalar { return s } +// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1. +func (s *Scalar) Pow(scalar internal.Scalar) internal.Scalar { + if scalar == nil || scalar.IsZero() { + return s.One() + } + + if scalar.Equal(scalar.Copy().One()) == 1 { + return s + } + + sc := assert(scalar) + sc.Subtract(&scOne) + + for !sc.IsZero() { + s.Multiply(s) + sc.Subtract(&scOne) + } + + return s +} + // Invert sets the receiver to the scalar's modular inverse ( 1 / scalar ), and returns it. func (s *Scalar) Invert() internal.Scalar { s.scalar.Invert(&s.scalar) diff --git a/internal/scalar.go b/internal/scalar.go index 07a652b..3e1af7e 100644 --- a/internal/scalar.go +++ b/internal/scalar.go @@ -32,6 +32,9 @@ type Scalar interface { // Multiply multiplies the receiver with the input, and returns the receiver. Multiply(Scalar) Scalar + // Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1. + Pow(scalar Scalar) Scalar + // Invert sets the receiver to the scalar's modular inverse ( 1 / scalar ), and returns it. Invert() Scalar diff --git a/scalar.go b/scalar.go index 9c1df9c..97e2a01 100644 --- a/scalar.go +++ b/scalar.go @@ -76,6 +76,17 @@ func (s *Scalar) Multiply(scalar *Scalar) *Scalar { return s } +// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1. +func (s *Scalar) Pow(scalar *Scalar) *Scalar { + if scalar == nil { + return s.One() + } + + s.Scalar.Pow(scalar.Scalar) + + return s +} + // Invert sets the receiver to the scalar's modular inverse ( 1 / scalar ), and returns it. func (s *Scalar) Invert() *Scalar { s.Scalar.Invert() diff --git a/tests/scalar_test.go b/tests/scalar_test.go index cb6cc20..ea86c91 100644 --- a/tests/scalar_test.go +++ b/tests/scalar_test.go @@ -123,6 +123,7 @@ func TestScalar_Arithmetic(t *testing.T) { scalarTestAdd(t, group.id) scalarTestSubtract(t, group.id) scalarTestMultiply(t, group.id) + scalarTestPow(t, group.id) scalarTestInvert(t, group.id) }) } @@ -233,6 +234,37 @@ func scalarTestMultiply(t *testing.T, g crypto.Group) { } } +func scalarTestPow(t *testing.T, g crypto.Group) { + // s**nil = 1 + s := g.NewScalar().Random() + if s.Pow(nil).Equal(g.NewScalar().One()) != 1 { + t.Fatal("expected s**nil = 1") + } + + // s**0 = 1 + s = g.NewScalar().Random() + zero := g.NewScalar().Zero() + if s.Pow(zero).Equal(g.NewScalar().One()) != 1 { + t.Fatal("expected s**0 = 1") + } + + // s**1 = s + s = g.NewScalar().Random() + one := g.NewScalar().One() + if s.Copy().Pow(one).Equal(s) != 1 { + t.Fatal("expected s**1 = s") + } + + // s**2 = s*s + s = g.NewScalar().Random() + s2 := s.Copy().Multiply(s) + two := g.NewScalar().One().Add(g.NewScalar().One()) + + if s.Pow(two).Equal(s2) != 1 { + t.Fatal("expected s**2 = s*s") + } +} + func scalarTestInvert(t *testing.T, g crypto.Group) { s := g.NewScalar().Random() sqr := s.Copy().Multiply(s) From 9b7be407077f731477c305837825380c155fedb2 Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:42:56 +0100 Subject: [PATCH 4/4] re-enable Analyze job Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f224a7..5a41051 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,6 @@ jobs: analyze: name: Analyze - if: github.event_name == 'push' runs-on: ubuntu-latest steps: - name: Checkout repo