diff --git a/.github/.golangci.yml b/.github/.golangci.yml index 3b96dd1..4fd1862 100644 --- a/.github/.golangci.yml +++ b/.github/.golangci.yml @@ -19,7 +19,7 @@ linters: - errorlint - execinquery #- exhaustive - - exhaustruct + #- exhaustruct - exportloopref - forbidigo - forcetypeassert diff --git a/README.md b/README.md index 7eba719..846d30a 100644 --- a/README.md +++ b/README.md @@ -8,25 +8,24 @@ ``` This package exposes abstract operations over opaque prime-order elliptic curve groups and their scalars and elements, -and implements the latest [hash-to-curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve) specification -to date. +and support hash-to-curve as per [RFC 9380](https://datatracker.ietf.org/doc/rfc9380). -It is made so you can swap between primitives with no code change. The only changing parameter is the Group identifier. -The package serves as an interface to optimized and secured implementations that serve as backends, and to which you +It is made so you can swap between primitives with no code change and only the Group identifier. +The package serves as an interface to optimized and secure implementations that serve as backends, and to which you don't need to adapt. The following table indexes supported groups with hash-to-curve capability and links each one to the underlying implementations: -| ID | Name | Backend | -|-----|--------------|-------------------------------| -| 1 | Ristretto255 | github.com/gtank/ristretto255 | -| 2 | Decaf448 | not yet supported | -| 3 | P-256 | filippo.io/nistec | -| 4 | P-384 | filippo.io/nistec | -| 5 | P-521 | filippo.io/nistec | -| 6 | Edwards25519 | filippo.io/edwards25519 | -| 7 | Secp256k1 | github.com/bytemare/secp256k1 | -| 8 | Double-Odd | not yet supported | +| ID | Name | Backend | +|----|--------------|-------------------------------| +| 1 | Ristretto255 | github.com/gtank/ristretto255 | +| 2 | Decaf448 | not supported | +| 3 | P-256 | filippo.io/nistec | +| 4 | P-384 | filippo.io/nistec | +| 5 | P-521 | filippo.io/nistec | +| 6 | Edwards25519 | filippo.io/edwards25519 | +| 7 | Secp256k1 | github.com/bytemare/secp256k1 | +| 8 | Double-Odd | not yet supported | ## Prime-order group interface diff --git a/element.go b/element.go index f7d93f8..1e144d6 100644 --- a/element.go +++ b/element.go @@ -17,21 +17,22 @@ import ( // Element represents an element on the curve of the prime-order group. type Element struct { + _ disallowEqual internal.Element } func newPoint(p internal.Element) *Element { - return &Element{p} + return &Element{Element: p} } // Base sets the element to the group's base point a.k.a. canonical generator. func (e *Element) Base() *Element { - return &Element{e.Element.Base()} + return &Element{Element: e.Element.Base()} } // Identity sets the element to the point at infinity of the Group's underlying curve. func (e *Element) Identity() *Element { - return &Element{e.Element.Identity()} + return &Element{Element: e.Element.Identity()} } // Add sets the receiver to the sum of the input and the receiver, and returns the receiver. @@ -96,13 +97,20 @@ func (e *Element) IsIdentity() bool { // Set sets the receiver to the argument, and returns the receiver. func (e *Element) Set(element *Element) *Element { + if element == nil { + e.Element.Set(nil) + + return e + } + e.Element.Set(element.Element) + return e } // Copy returns a copy of the receiver. func (e *Element) Copy() *Element { - return &Element{e.Element.Copy()} + return &Element{Element: e.Element.Copy()} } // Encode returns the compressed byte encoding of the element. @@ -118,7 +126,7 @@ func (e *Element) XCoordinate() []byte { // Decode sets the receiver to a decoding of the input data, and returns an error on failure. func (e *Element) Decode(data []byte) error { if err := e.Element.Decode(data); err != nil { - return fmt.Errorf("element decoding: %w", err) + return fmt.Errorf("element Decode: %w", err) } return nil diff --git a/go.mod b/go.mod index 65bd40d..f57fac7 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,15 @@ module github.com/bytemare/crypto go 1.21 require ( - filippo.io/edwards25519 v1.0.0 + filippo.io/edwards25519 v1.1.0 filippo.io/nistec v0.0.3 - github.com/bytemare/hash2curve v0.2.2 - github.com/bytemare/secp256k1 v0.1.0 + github.com/bytemare/hash2curve v0.2.4 + github.com/bytemare/secp256k1 v0.1.1 github.com/gtank/ristretto255 v0.1.2 ) require ( - github.com/bytemare/hash v0.1.5 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/sys v0.14.0 // indirect + github.com/bytemare/hash v0.2.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect ) diff --git a/go.sum b/go.sum index 8e50203..1c9603c 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,16 @@ -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/nistec v0.0.3 h1:h336Je2jRDZdBCLy2fLDUd9E2unG32JLwcJi0JQE9Cw= filippo.io/nistec v0.0.3/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw= -github.com/bytemare/hash v0.1.5 h1:VW+X1YQ2b3chjRFHkRUnO42uclsQjXimdBCPOgIobR4= -github.com/bytemare/hash v0.1.5/go.mod h1:+QmWXTky/2b63ngqM5IYezGydn9UTFDhpX7mLYwYxCA= -github.com/bytemare/hash2curve v0.2.2 h1:zaGx6Z4/N4Pl9B7aGNtpbZ09vu1NNJGoJRRtHHl8oTw= -github.com/bytemare/hash2curve v0.2.2/go.mod h1:Wma3DmJdn8kqiK9j120hkWvC3tQVKS1PyA8ZzyG23BI= -github.com/bytemare/secp256k1 v0.1.0 h1:kjVJ06GAHSa+EJ7Rz1LdVgE0DQWdvUT77tmcGf7epXQ= -github.com/bytemare/secp256k1 v0.1.0/go.mod h1:hzquMsr3GXhVcqL9qFX7GGjmcT5dlQldKrArd7tcXHE= +github.com/bytemare/hash v0.2.0 h1:BVWJOz1IIaLmxSybx8WiMMAS6OlB23JLU9vkCLgrt+0= +github.com/bytemare/hash v0.2.0/go.mod h1:aAUXRjcoavq+IrTSZHPY9nEy8wHmWZk8y4Sbol4XkWU= +github.com/bytemare/hash2curve v0.2.4 h1:os6/FM43D7W/K0FkUbrGoGemp+nX4x/Sytv9N5tF+hU= +github.com/bytemare/hash2curve v0.2.4/go.mod h1:P9v9uVR5wOGlSwlPqvRYzbj28+pmw/Lxpn2FgRQ7hCE= +github.com/bytemare/secp256k1 v0.1.1 h1:gy594u/BhqXt77EA+c2nBuJbGl/XAFhsc+kXm+5O6oA= +github.com/bytemare/secp256k1 v0.1.1/go.mod h1:VGxliu7lu4ZKheaBvGwAATmbzdELTrQqiDusFkodOCM= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/groups.go b/groups.go index a2de0b2..889788c 100644 --- a/groups.go +++ b/groups.go @@ -175,3 +175,8 @@ func (g Group) init() { panic("group not recognized") } } + +// disallowEqual is an incomparable type. +// If you place it first in your struct, you prevent == from +// working on your struct without growing its size. +type disallowEqual [0]func() diff --git a/internal/edwards25519/element.go b/internal/edwards25519/element.go index 0ae8b29..f279aab 100644 --- a/internal/edwards25519/element.go +++ b/internal/edwards25519/element.go @@ -106,7 +106,7 @@ func (e *Element) set(element *Element) *Element { // Set sets the receiver to the value of the argument, and returns the receiver. func (e *Element) Set(element internal.Element) internal.Element { if element == nil { - return e.set(nil) + return e.Identity() } ec, ok := element.(*Element) @@ -127,7 +127,8 @@ func (e *Element) Encode() []byte { return e.element.Bytes() } -// XCoordinate returns the encoded x coordinate of the element, which is the same as Encode(). +// XCoordinate returns the encoded u coordinate of the element. Note that there's no inverse function for this, and +// that decoding this output might result in another point. func (e *Element) XCoordinate() []byte { return e.element.BytesMontgomery() } @@ -139,7 +140,7 @@ func decodeElement(element []byte) (*ed.Point, error) { e := ed.NewIdentityPoint() if _, err := e.SetBytes(element); err != nil { - return nil, fmt.Errorf("edwards25519 element Decode: %w", err) + return nil, fmt.Errorf("%w", err) } return e, nil diff --git a/internal/edwards25519/scalar.go b/internal/edwards25519/scalar.go index 0ea984a..8a40af2 100644 --- a/internal/edwards25519/scalar.go +++ b/internal/edwards25519/scalar.go @@ -63,9 +63,8 @@ func assert(scalar internal.Scalar) *Scalar { return &Scalar{*ed.NewScalar().Set(&sc.scalar)} } -func (s *Scalar) set(scalar *ed.Scalar) *Scalar { +func (s *Scalar) set(scalar *ed.Scalar) { s.scalar = *scalar - return s } // Zero sets the scalar to 0, and returns it. @@ -260,7 +259,7 @@ func (s *Scalar) IsZero() bool { // Set sets the receiver to the value of the argument scalar, and returns the receiver. func (s *Scalar) Set(scalar internal.Scalar) internal.Scalar { if scalar == nil { - return s.set(nil) + return s.Zero() } ec := assert(scalar) @@ -308,7 +307,7 @@ func decodeScalar(scalar []byte) (*ed.Scalar, error) { s := ed.NewScalar() if _, err := s.SetCanonicalBytes(scalar); err != nil { - return nil, fmt.Errorf("ristretto scalar Decode: %w", err) + return nil, fmt.Errorf("%w", err) } return s, nil diff --git a/internal/field/field.go b/internal/field/field.go index 056fe38..2219940 100644 --- a/internal/field/field.go +++ b/internal/field/field.go @@ -109,22 +109,11 @@ func (f Field) Inv(res, x *big.Int) { f.Exponent(res, x, f.pMinus2) } -// LegendreSymbol applies the Legendre symbole on (a/p) and returns either {-1, 0, 1} mod field order. -func (f Field) LegendreSymbol(a *big.Int) *big.Int { - var res big.Int - return f.Exponent(&res, a, f.pMinus1div2) -} - // Exponent returns x^n mod field order. func (f Field) Exponent(res, x, n *big.Int) *big.Int { return res.Exp(x, n, f.order) } -// IsSquare returns whether e is a quadratic square. -func (f Field) IsSquare(e *big.Int) bool { - return f.AreEqual(f.LegendreSymbol(e), f.One()) -} - // IsEqual returns whether the two fields have the same order. func (f Field) IsEqual(f2 *Field) bool { return f.order.Cmp(f2.order) == 0 @@ -135,24 +124,6 @@ func (f Field) Mod(x *big.Int) *big.Int { return x.Mod(x, f.order) } -// Neg sets res to the -x modulo the field order. -func (f Field) Neg(res, x *big.Int) *big.Int { - return f.Mod(res.Neg(x)) -} - -// CondNeg sets res to -x if cond == 1. -func (f Field) CondNeg(res, x *big.Int, cond int) { - var neg, cpy big.Int - cpy.Set(x) - f.Neg(&neg, x) - - if cond == 1 { - res.Set(&neg) - } else { - res.Set(&cpy) - } -} - // Add sets res to x + y modulo the field order. func (f Field) Add(res, x, y *big.Int) { f.Mod(res.Add(x, y)) @@ -163,55 +134,7 @@ func (f Field) Sub(res, x, y *big.Int) *big.Int { return f.Mod(res.Sub(x, y)) } -// Lsh sets res to the left shift of n bits on x modulo the field order. -func (f Field) Lsh(res, x *big.Int, n uint) { - f.Mod(res.Lsh(x, n)) -} - // Mul sets res to the multiplication of x and y modulo the field order. func (f Field) Mul(res, x, y *big.Int) { f.Mod(res.Mul(x, y)) } - -// Square sets res to the square of x modulo the field order. -func (f Field) Square(res, x *big.Int) { - f.Mod(res.Mul(x, x)) -} - -// CondMov sets res to y if b true, and to x otherwise. -func (f Field) CondMov(res, x, y *big.Int, b bool) { - if b { - res.Set(y) - } else { - res.Set(x) - } -} - -// Sgn0 returns the first bit in the big-endian representation. -func (f Field) Sgn0(x *big.Int) int { - return int(x.Bit(0)) -} - -func (f Field) sqrt3mod4(res, e *big.Int) *big.Int { - return f.Exponent(res, e, f.exp) -} - -// SquareRoot sets res to a square root of e mod the field's order, if such a square root exists. -func (f Field) SquareRoot(res, e *big.Int) *big.Int { - return f.sqrt3mod4(res, e) -} - -// SqrtRatio res result to the square root of (e/v), and indicates whether (e/v) is a square. -func (f Field) SqrtRatio(res, zMapConstant, e, v *big.Int) bool { - f.Inv(res, v) - f.Mul(res, res, e) - - square := f.IsSquare(res) - if !square { - f.Mul(res, res, zMapConstant) - } - - f.SquareRoot(res, res) - - return square -} diff --git a/internal/misc.go b/internal/misc.go index ab8d179..670fca9 100644 --- a/internal/misc.go +++ b/internal/misc.go @@ -47,6 +47,9 @@ var ( // ErrParamScalarTooBig reports an error when the input scalar is too big. ErrParamScalarTooBig = errors.New("scalar too big") + + // ErrParamScalarInvalidEncoding indicates an invalid scalar encoding has been provided, or that it's too big. + ErrParamScalarInvalidEncoding = errors.New("invalid scalar encoding") ) // RandomBytes returns random bytes of length len (wrapper for crypto/rand). diff --git a/internal/nist/element.go b/internal/nist/element.go index cac430c..c282513 100644 --- a/internal/nist/element.go +++ b/internal/nist/element.go @@ -145,15 +145,10 @@ func (e *Element[P]) IsIdentity() bool { return subtle.ConstantTimeCompare(b, i) == 1 } -func (e *Element[P]) set(element *Element[P]) *Element[P] { - *e = *element - return e -} - // Set sets the receiver to the value of the argument, and returns the receiver. func (e *Element[P]) Set(element internal.Element) internal.Element { if element == nil { - return e.set(nil) + return e.Identity() } ec, ok := element.(*Element[P]) diff --git a/internal/nist/scalar.go b/internal/nist/scalar.go index 5c00b1c..5f42498 100644 --- a/internal/nist/scalar.go +++ b/internal/nist/scalar.go @@ -169,15 +169,10 @@ func (s *Scalar) IsZero() bool { return s.field.AreEqual(&s.scalar, s.field.Zero()) } -func (s *Scalar) set(scalar *Scalar) *Scalar { - *s = *scalar - return s -} - // Set sets the receiver to the value of the argument scalar, and returns the receiver. func (s *Scalar) Set(scalar internal.Scalar) internal.Scalar { if scalar == nil { - return s.set(nil) + return s.Zero() } ec := s.assert(scalar) @@ -212,18 +207,25 @@ func (s *Scalar) Encode() []byte { // Decode sets the receiver to a decoding of the input data, and returns an error on failure. func (s *Scalar) Decode(in []byte) error { - if len(in) == 0 { + expectedLength := (s.field.BitLen() + 7) / 8 + + switch len(in) { + case 0: return internal.ErrParamNilScalar + case expectedLength: + break + default: + return internal.ErrParamScalarLength } // warning - SetBytes interprets the input as a non-signed integer, so this will always be false + // if tmp.Sign() < 0 { + // return internal.ErrParamNegScalar + // } tmp := new(big.Int).SetBytes(in) - if tmp.Sign() < 0 { - return internal.ErrParamNegScalar - } if s.field.Order().Cmp(tmp) <= 0 { - return internal.ErrParamScalarTooBig + return internal.ErrParamScalarInvalidEncoding } s.scalar.Set(tmp) diff --git a/internal/ristretto/element.go b/internal/ristretto/element.go index 065c7fb..ab8895d 100644 --- a/internal/ristretto/element.go +++ b/internal/ristretto/element.go @@ -108,7 +108,7 @@ func (e *Element) set(element *Element) *Element { // Set sets the receiver to the value of the argument, and returns the receiver. func (e *Element) Set(element internal.Element) internal.Element { if element == nil { - return e.set(nil) + return e.Identity() } ec, ok := element.(*Element) @@ -146,7 +146,7 @@ func decodeElement(element []byte) (*ristretto255.Element, error) { e := ristretto255.NewElement() if err := e.Decode(element); err != nil { - return nil, fmt.Errorf("ristretto element Decode: %w", err) + return nil, fmt.Errorf("%w", err) } return e, nil diff --git a/internal/ristretto/scalar.go b/internal/ristretto/scalar.go index c84add2..c677542 100644 --- a/internal/ristretto/scalar.go +++ b/internal/ristretto/scalar.go @@ -248,7 +248,7 @@ func (s *Scalar) IsZero() bool { // Set sets the receiver to the value of the argument scalar, and returns the receiver. func (s *Scalar) Set(scalar internal.Scalar) internal.Scalar { if scalar == nil { - s.set(nil) + s.Zero() return s } @@ -297,7 +297,7 @@ func decodeScalar(scalar []byte) (*ristretto255.Scalar, error) { s := ristretto255.NewScalar() if err := s.Decode(scalar); err != nil { - return nil, fmt.Errorf("ristretto scalar Decode: %w", err) + return nil, fmt.Errorf("%w", err) } return s, nil diff --git a/internal/secp256k1/element.go b/internal/secp256k1/element.go index 42e3a43..bea9e95 100644 --- a/internal/secp256k1/element.go +++ b/internal/secp256k1/element.go @@ -100,6 +100,10 @@ func (e *Element) IsIdentity() bool { // Set sets the receiver to the value of the argument, and returns the receiver. func (e *Element) Set(element internal.Element) internal.Element { + if element == nil { + return e.Identity() + } + q := assertElement(element) e.element.Set(q.element) @@ -124,7 +128,7 @@ func (e *Element) XCoordinate() []byte { // Decode sets the receiver to a decoding of the input data, and returns an error on failure. func (e *Element) Decode(data []byte) error { if err := e.element.Decode(data); err != nil { - return fmt.Errorf("secp256k1 decode element: %w", err) + return fmt.Errorf("%w", err) } return nil diff --git a/internal/secp256k1/scalar.go b/internal/secp256k1/scalar.go index 1d67271..1db134c 100644 --- a/internal/secp256k1/scalar.go +++ b/internal/secp256k1/scalar.go @@ -134,15 +134,10 @@ func (s *Scalar) IsZero() bool { return s.scalar.IsZero() } -func (s *Scalar) set(scalar *Scalar) *Scalar { - *s = *scalar - return s -} - // Set sets the receiver to the value of the argument scalar, and returns the receiver. func (s *Scalar) Set(scalar internal.Scalar) internal.Scalar { if scalar == nil { - return s.set(nil) + return s.Zero() } sc := assert(scalar) @@ -154,7 +149,7 @@ func (s *Scalar) Set(scalar internal.Scalar) internal.Scalar { // SetInt sets s to i modulo the field order, and returns an error if one occurs. func (s *Scalar) SetInt(i *big.Int) error { if err := s.scalar.SetInt(i); err != nil { - return fmt.Errorf("secp256k1 SetInt: %w", err) + return fmt.Errorf("%w", err) } return nil @@ -173,7 +168,11 @@ func (s *Scalar) Encode() []byte { // Decode sets the receiver to a decoding of the input data, and returns an error on failure. func (s *Scalar) Decode(in []byte) error { if err := s.scalar.Decode(in); err != nil { - return fmt.Errorf("secp256k1 decode scalar: %w", err) + if err.Error() == "scalar too big" { + return internal.ErrParamScalarInvalidEncoding + } + + return fmt.Errorf("%w", err) } return nil diff --git a/scalar.go b/scalar.go index 8fc4af1..b228e28 100644 --- a/scalar.go +++ b/scalar.go @@ -18,11 +18,12 @@ import ( // Scalar represents a scalar in the prime-order group. type Scalar struct { + _ disallowEqual internal.Scalar } func newScalar(s internal.Scalar) *Scalar { - return &Scalar{s} + return &Scalar{Scalar: s} } // Zero sets the scalar to 0, and returns it. @@ -119,7 +120,12 @@ func (s *Scalar) IsZero() bool { // Set sets the receiver to the value of the argument scalar, and returns the receiver. func (s *Scalar) Set(scalar *Scalar) *Scalar { + if scalar == nil { + return s.Zero() + } + s.Scalar.Set(scalar.Scalar) + return s } @@ -134,7 +140,7 @@ func (s *Scalar) SetInt(i *big.Int) error { // Copy returns a copy of the receiver. func (s *Scalar) Copy() *Scalar { - return &Scalar{s.Scalar.Copy()} + return &Scalar{Scalar: s.Scalar.Copy()} } // Encode returns the compressed byte encoding of the scalar. diff --git a/tests/element_test.go b/tests/element_test.go index 1a4690f..b0dc51b 100644 --- a/tests/element_test.go +++ b/tests/element_test.go @@ -10,9 +10,13 @@ package group_test import ( "encoding/hex" + "errors" "log" + "math/big" "testing" + "github.com/bytemare/secp256k1" + "github.com/bytemare/crypto" "github.com/bytemare/crypto/internal" ) @@ -44,10 +48,15 @@ func testElementCopySet(t *testing.T, element, other *crypto.Element) { if element.Equal(other) == 1 { t.Fatalf("Unexpected equality") } + + // Verify setting to nil sets to identity + if element.Set(nil).Equal(other.Identity()) != 1 { + t.Error(errExpectedEquality) + } } func TestElementCopy(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { base := group.group.Base() cpy := base.Copy() testElementCopySet(t, base, cpy) @@ -55,7 +64,7 @@ func TestElementCopy(t *testing.T) { } func TestElementSet(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { base := group.group.Base() other := group.group.NewElement() other.Set(base) @@ -82,7 +91,7 @@ func TestElement_WrongInput(t *testing.T) { } } - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { element := group.group.NewElement() var alternativeGroup crypto.Group @@ -125,7 +134,7 @@ func TestElement_WrongInput(t *testing.T) { } func TestElement_EncodedLength(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { id := group.group.NewElement().Identity().Encode() if len(id) != group.elementLength { t.Fatalf( @@ -155,8 +164,125 @@ func TestElement_EncodedLength(t *testing.T) { }) } +func TestElement_Decode_OutOfBounds(t *testing.T) { + testAll(t, func(group *testGroup) { + expected := errors.New("invalid point encoding") + encoded := make([]byte, group.group.ElementLength()) + + y := big.NewInt(0) + + x, ok := new(big.Int).SetString(group.fieldOrder, 0) + if !ok { + t.Fatalf("setting int in base %d failed: %v", 0, group.fieldOrder) + } + + // x exceeds the order + x.Add(x, big.NewInt(1)) + + switch group.group { + case crypto.Ristretto255Sha512, crypto.Edwards25519Sha512: + x.FillBytes(encoded) + case crypto.P256Sha256, crypto.P384Sha384, crypto.P521Sha512, crypto.Secp256k1: + encoded[0] = byte(2 | y.Bit(0)&1) + x.FillBytes(encoded[1:]) + default: + t.Fatalf("non registered group %s", group.group) + } + + if err := secp256k1.NewElement().Decode(encoded[:]); err == nil || err.Error() != expected.Error() { + t.Errorf("expected error %q, got %v", expected, err) + } + }) +} + +func TestElement_XCoordinate(t *testing.T) { + testAll(t, func(group *testGroup) { + baseX := hex.EncodeToString(group.group.Base().XCoordinate()) + refLen := len(baseX) / 2 // hexadecimal length is 2 times byt length + + if baseX != group.basePointX { + t.Error(errExpectedEquality) + } + + zero := hex.EncodeToString(make([]byte, refLen)) + id := hex.EncodeToString(group.group.NewElement().XCoordinate()) + + if zero != id { + t.Error(errExpectedEquality) + } + }) +} + +func TestElement_Vectors_Add(t *testing.T) { + testAll(t, func(group *testGroup) { + base := group.group.Base() + acc := group.group.Base() + + for _, mult := range group.multBase { + e := decodeElement(t, group.group, mult) + if e.Equal(acc) != 1 { + t.Fatal("expected equality") + } + + acc.Add(base) + } + + base.Add(group.group.NewElement()) + if base.Equal(group.group.Base()) != 1 { + t.Fatal(errExpectedEquality) + } + + if group.group.NewElement().Add(base).Equal(base) != 1 { + t.Fatal(errExpectedEquality) + } + }) +} + +func TestElement_Vectors_Double(t *testing.T) { + testAll(t, func(group *testGroup) { + tables := [][]int{ + {1, 2, 4, 8}, + {3, 6, 12}, + {5, 10}, + {7, 14}, + } + + for _, table := range tables { + e := decodeElement(t, group.group, group.multBase[table[0]-1]) + for _, multiple := range table[1:] { + e.Double() + + v := decodeElement(t, group.group, group.multBase[multiple-1]) + if v.Equal(e) != 1 { + t.Fatalf("expected equality for %d", multiple) + } + } + } + }) +} + +func TestElement_Vectors_Mult(t *testing.T) { + testAll(t, func(group *testGroup) { + s := group.group.NewScalar() + base := group.group.Base() + + for i, mult := range group.multBase { + e := decodeElement(t, group.group, mult) + if e.Equal(base) != 1 { + t.Fatalf("expected equality for %d", i) + } + + if err := s.SetInt(big.NewInt(int64(i + 2))); err != nil { + t.Fatal(err) + } + + base.Base().Multiply(s) + } + }) +} + func TestElement_Arithmetic(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { elementTestEqual(t, group.group) elementTestAdd(t, group.group) elementTestDouble(t, group.group) diff --git a/tests/groups_test.go b/tests/groups_test.go index 1091813..98bfece 100644 --- a/tests/groups_test.go +++ b/tests/groups_test.go @@ -11,6 +11,7 @@ package group_test import ( "encoding/hex" "errors" + "fmt" "testing" "github.com/bytemare/crypto" @@ -19,7 +20,7 @@ import ( const consideredAvailableFmt = "%v is considered available when it must not" func TestAvailability(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { if !group.group.Available() { t.Errorf("'%s' is not available, but should be", group.group.String()) } @@ -62,7 +63,7 @@ func TestNonAvailability(t *testing.T) { } func TestGroup_Base(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { if hex.EncodeToString(group.group.Base().Encode()) != group.basePoint { t.Fatalf("Got wrong base element\n\tgot : %s\n\twant: %s", hex.EncodeToString(group.group.Base().Encode()), @@ -83,7 +84,7 @@ func TestDST(t *testing.T) { crypto.Secp256k1: app + "-V01-CS07-", } - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { res := string(group.group.MakeDST(app, version)) test := tests[group.group] + group.h2c if res != test { @@ -93,7 +94,7 @@ func TestDST(t *testing.T) { } func TestGroup_String(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { res := group.group.String() ref := group.h2c if res != ref { @@ -103,7 +104,7 @@ func TestGroup_String(t *testing.T) { } func TestGroup_NewScalar(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { s := group.group.NewScalar().Encode() for _, b := range s { if b != 0 { @@ -114,7 +115,7 @@ func TestGroup_NewScalar(t *testing.T) { } func TestGroup_NewElement(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { e := hex.EncodeToString(group.group.NewElement().Encode()) ref := group.identity @@ -125,7 +126,7 @@ func TestGroup_NewElement(t *testing.T) { } func TestGroup_ScalarLength(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { if int(group.group.ScalarLength()) != group.scalarLength { t.Fatalf("expected encoded scalar length %d, but got %d", group.scalarLength, group.group.ScalarLength()) } @@ -133,9 +134,71 @@ func TestGroup_ScalarLength(t *testing.T) { } func TestGroup_ElementLength(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { - if int(group.group.ElementLength()) != group.elementLength { + testAll(t, func(group *testGroup) { + if group.group.ElementLength() != group.elementLength { t.Fatalf("expected encoded element length %d, but got %d", group.elementLength, group.group.ElementLength()) } }) } + +func TestHashToScalar(t *testing.T) { + testAll(t, func(group *testGroup) { + sv := decodeScalar(t, group.group, group.hashToCurve.hashToScalar) + + s := group.group.HashToScalar(group.hashToCurve.input, group.hashToCurve.dst) + if s.Equal(sv) != 1 { + t.Error(errExpectedEquality) + } + }) +} + +func TestHashToScalar_NoDST(t *testing.T) { + testAll(t, func(group *testGroup) { + data := []byte("input data") + + // Nil DST + if err := testPanic("nil dst", errZeroLenDST, func() { + _ = group.group.HashToScalar(data, nil) + }); err != nil { + t.Error(fmt.Errorf(errWrapGroup, errNoPanic, err)) + } + + // Zero length DST + if err := testPanic("zero-length dst", errZeroLenDST, func() { + _ = group.group.HashToScalar(data, []byte{}) + }); err != nil { + t.Error(fmt.Errorf(errWrapGroup, errNoPanic, err)) + } + }) +} + +func TestHashToGroup(t *testing.T) { + testAll(t, func(group *testGroup) { + ev := decodeElement(t, group.group, group.hashToCurve.hashToGroup) + + e := group.group.HashToGroup(group.hashToCurve.input, group.hashToCurve.dst) + if e.Equal(ev) != 1 { + t.Error(errExpectedEquality) + } + }) +} + +func TestHashToGroup_NoDST(t *testing.T) { + testAll(t, func(group *testGroup) { + data := []byte("input data") + + // Nil DST + if err := testPanic("nil dst", errZeroLenDST, func() { + _ = group.group.HashToGroup(data, nil) + }); err != nil { + t.Error(fmt.Errorf(errWrapGroup, errNoPanic, err)) + } + + // Zero length DST + if err := testPanic("zero-length dst", errZeroLenDST, func() { + _ = group.group.HashToGroup(data, []byte{}) + }); err != nil { + t.Error(fmt.Errorf(errWrapGroup, errNoPanic, err)) + } + }) +} diff --git a/tests/scalar_test.go b/tests/scalar_test.go index b2a85c8..2c1f609 100644 --- a/tests/scalar_test.go +++ b/tests/scalar_test.go @@ -10,6 +10,7 @@ package group_test import ( "encoding/hex" + "errors" "math/big" "testing" @@ -30,7 +31,7 @@ func TestScalar_WrongInput(t *testing.T) { } } - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { scalar := group.group.NewScalar() methods := []func(arg *crypto.Scalar) *crypto.Scalar{ scalar.Add, scalar.Subtract, scalar.Multiply, scalar.Set, @@ -87,10 +88,15 @@ func testScalarCopySet(t *testing.T, scalar, other *crypto.Scalar) { if scalar.Equal(other) == 1 { t.Fatalf("Unexpected equality") } + + // Verify setting to nil sets to 0 + if scalar.Set(nil).Equal(other.Zero()) != 1 { + t.Error(errExpectedEquality) + } } func TestScalarCopy(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { random := group.group.NewScalar().Random() cpy := random.Copy() testScalarCopySet(t, random, cpy) @@ -98,7 +104,7 @@ func TestScalarCopy(t *testing.T) { } func TestScalarSet(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { random := group.group.NewScalar().Random() other := group.group.NewScalar() other.Set(random) @@ -107,7 +113,7 @@ func TestScalarSet(t *testing.T) { } func TestScalarSetInt(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { i := big.NewInt(0) s := group.group.NewScalar() @@ -144,7 +150,7 @@ func TestScalarSetInt(t *testing.T) { } func TestScalar_EncodedLength(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { encodedScalar := group.group.NewScalar().Random().Encode() if len(encodedScalar) != group.scalarLength { t.Fatalf( @@ -156,8 +162,37 @@ func TestScalar_EncodedLength(t *testing.T) { }) } +func TestScalar_Decode_OutOfBounds(t *testing.T) { + testAll(t, func(group *testGroup) { + // Decode invalid length + encoded := make([]byte, 2) + big.NewInt(1).FillBytes(encoded) + + expected := errors.New("scalar Decode: invalid scalar length") + if err := group.group.NewScalar().Decode(encoded); err == nil || err.Error() != expected.Error() { + t.Errorf("expected error %q, got %v", expected, err) + } + + // Decode a scalar higher than order + encoded = make([]byte, group.group.ScalarLength()) + + order, ok := new(big.Int).SetString(group.group.Order(), 0) + if !ok { + t.Errorf("setting int in base %d failed: %v", 0, group.group.Order()) + } + + order.Add(order, big.NewInt(1)) + order.FillBytes(encoded) + + expected = errors.New("scalar Decode: invalid scalar encoding") + if err := group.group.NewScalar().Decode(encoded); err == nil || err.Error() != expected.Error() { + t.Errorf("expected error %q, got %v", expected, err) + } + }) +} + func TestScalar_Arithmetic(t *testing.T) { - testAll(t, func(t2 *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { scalarTestZero(t, group.group) scalarTestOne(t, group.group) scalarTestEqual(t, group.group) diff --git a/tests/table_test.go b/tests/table_test.go index 96fca72..7834696 100644 --- a/tests/table_test.go +++ b/tests/table_test.go @@ -14,21 +14,37 @@ import ( "github.com/bytemare/crypto" ) -func testAll(t *testing.T, f func(*testing.T, *testGroup)) { +func testAll(t *testing.T, f func(*testGroup)) { for _, test := range testTable { t.Run(test.name, func(t *testing.T) { - f(t, test) + f(test) }) } } +var ( + testHashToGroupInput = []byte("input data") + testHashToGroupDST = []byte("domain separation tag") +) + +type testHashToCurve struct { + hashToScalar string + hashToGroup string + input []byte + dst []byte +} + // a testGroup references some parameters of a Group. type testGroup struct { + multBase [15]string name string h2c string e2c string basePoint string + basePointX string identity string + fieldOrder string + hashToCurve testHashToCurve elementLength int scalarLength int group crypto.Group @@ -36,61 +52,211 @@ type testGroup struct { var testTable = []*testGroup{ { + [15]string{ + "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", + "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", + "94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259", + "da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57", + "e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e", + "f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403", + "44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d", + "903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c", + "02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031", + "20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f", + "bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42", + "e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460", + "aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f", + "46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e", + "e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e", + }, "Ristretto255", "ristretto255_XMD:SHA-512_R255MAP_RO_", "ristretto255_XMD:SHA-512_R255MAP_RO_", ristrettoBasePoint, + ristrettoBasePoint, "0000000000000000000000000000000000000000000000000000000000000000", + "57896044618658097711785492504343953926634992332820282019728792003956564819949", + testHashToCurve{ + input: testHashToGroupInput, + dst: testHashToGroupDST, + hashToScalar: "7cf9410111022202c71f9d317d6fcd711a84fee5a406063f8376379bbe8a3f03", + hashToGroup: "d0f15a907366d66998784ff0148356bb0de24088680fb29d5fbe1a629d743b10", + }, 32, 32, 1, }, { + [15]string{ + "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978", + "025ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c", + "02e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852", + "0251590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed", + "02b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a9", + "028e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3", + "0262d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393", + "02ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0", + "03cef66d6b2a3a993e591214d1ea223fb545ca6c471c48306e4c36069404c5723f", + "023ed113b7883b4c590638379db0c21cda16742ed0255048bf433391d374bc21d1", + "03741dd5bda817d95e4626537320e5d55179983028b2f82c99d500c5ee8624e3c4", + "02177c837ae0ac495a61805df2d85ee2fc792e284b65ead58a98e15d9d46072c01", + "0354e77a001c3862b97a76647f4336df3cf126acbe7a069c5e5709277324d2920b", + "02f0454dc6971abae7adfb378999888265ae03af92de3a0ef163668c63e59b9d5f", + }, "P256", "P256_XMD:SHA-256_SSWU_RO_", "P256_XMD:SHA-256_SSWU_NU_", "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", "000000000000000000000000000000000000000000000000000000000000000000", + "115792089210356248762697446949407573530086143415290314195533631308867097853951", + testHashToCurve{ + input: testHashToGroupInput, + dst: testHashToGroupDST, + hashToScalar: "4b51fd1148439c3a30539e87a2a75c63d72f71b74d108184beeb933d259456b9", + hashToGroup: "03536d17bf54e34ebc3926d425e76502b54bc2c393369fc6df0c729a18df667f4c", + }, 33, 32, 3, }, { + [15]string{ + "03aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", + "0208d999057ba3d2d969260045c55b97f089025959a6f434d651d207d19fb96e9e4fe0e86ebe0e64f85b96a9c75295df61", + "03077a41d4606ffa1464793c7e5fdc7d98cb9d3910202dcd06bea4f240d3566da6b408bbae5026580d02d7e5c70500c831", + "03138251cd52ac9298c1c8aad977321deb97e709bd0b4ca0aca55dc8ad51dcfc9d1589a1597e3a5120e1efd631c63e1835", + "0211de24a2c251c777573cac5ea025e467f208e51dbff98fc54f6661cbe56583b037882f4a1ca297e60abcdbc3836d84bc", + "02627be1acd064d2b2226fe0d26f2d15d3c33ebcbb7f0f5da51cbd41f26257383021317d7202ff30e50937f0854e35c5df", + "02283c1d7365ce4788f29f8ebf234edffead6fe997fbea5ffa2d58cc9dfa7b1c508b05526f55b9ebb2040f05b48fb6d0e1", + "021692778ea596e0be75114297a6fa383445bf227fbe58190a900c3c73256f11fb5a3258d6f403d5ece6e9b269d822c87d", + "028f0a39a4049bcb3ef1bf29b8b025b78f2216f7291e6fd3bac6cb1ee285fb6e21c388528bfee2b9535c55e4461079118b", + "03a669c5563bd67eec678d29d6ef4fde864f372d90b79b9e88931d5c29291238cced8e85ab507bf91aa9cb2d13186658fb", + "03099056e27da7b998da1eeec2904816c57fe935ed5837c37456c9fd14892d3f8c4749b66e3afb81d626356f3b55b4ddd8", + "02952a7a349bd49289ab3ac421dcf683d08c2ed5e41f6d0e21648af2691a481406da4a5e22da817cb466da2ea77d2a7022", + "02a567ba97b67aea5bafdaf5002ffcc6ab9632bff9f01f873f6267bcd1f0f11c139ee5f441abd99f1baaf1ca1e3b5cbce7", + "02e8c8f94d44fbc2396bbeac481b89d2b0877b1dffd23e7dc95de541eb651cca2c41aba24dbc02de6637209accf0f59ea0", + "02b3d13fc8b32b01058cc15c11d813525522a94156fff01c205b21f9f7da7c4e9ca849557a10b6383b4b88701a9606860b", + }, "P384", "P384_XMD:SHA-384_SSWU_RO_", "P384_XMD:SHA-384_SSWU_NU_", "03aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", + "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", + testHashToCurve{ + input: testHashToGroupInput, + dst: testHashToGroupDST, + hashToScalar: "d22b5352caa675f8a2f385236b95cbc1f84b9e34540b3587d6d55bd5032bf51aeb54ccab701c6f05a489b82ec301012d", + hashToGroup: "02777ff137e17b48ab4984de510461af79cf34609ac27f98eb2a4a553f94dbf31bf97b5cf7bac08f60bb8c7ee474a26202", + }, 49, 48, 4, }, { + [15]string{ + "0200c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", + "0200433c219024277e7e682fcb288148c282747403279b1ccc06352c6e5505d769be97b3b204da6ef55507aa104a3a35c5af41cf2fa364d60fd967f43e3933ba6d783d", + "0301a73d352443de29195dd91d6a64b5959479b52a6e5b123d9ab9e5ad7a112d7a8dd1ad3f164a3a4832051da6bd16b59fe21baeb490862c32ea05a5919d2ede37ad7d", + "030035b5df64ae2ac204c354b483487c9070cdc61c891c5ff39afc06c5d55541d3ceac8659e24afe3d0750e8b88e9f078af066a1d5025b08e5a5e2fbc87412871902f3", + "0300652bf3c52927a432c73dbc3391c04eb0bf7a596efdb53f0d24cf03dab8f177ace4383c0c6d5e3014237112feaf137e79a329d7e1e6d8931738d5ab5096ec8f3078", + "0301ee4569d6cdb59219532eff34f94480d195623d30977fd71cf3981506ade4ab01525fbcca16153f7394e0727a239531be8c2f66e95657f380ae23731bedf79206b9", + "030056d5d1d99d5b7f6346eeb65fda0b073a0c5f22e0e8f5483228f018d2c2f7114c5d8c308d0abfc698d8c9a6df30dce3bbc46f953f50fdc2619a01cead882816ecd4", + "02000822c40fb6301f7262a8348396b010e25bd4e29d8a9b003e0a8b8a3b05f826298f5bfea5b8579f49f08b598c1bc8d79e1ab56289b5a6f4040586f9ea54aa78ce68", + "0201585389e359e1e21826a2f5bf157156d488ed34541b988746992c4ab145b8c6b6657429e1396134da35f3c556df725a318f4f50babd85cd28661f45627967cbe207", + "030190eb8f22bda61f281dfcfe7bb6721ec4cd901d879ac09ac7c34a9246b11ada8910a2c7c178fcc263299daa4da9842093f37c2e411f1a8e819a87ff09a04f2f3320", + "02008a75841259fdedff546f1a39573b4315cfed5dc7ed7c17849543ef2c54f2991652f3dbc5332663da1bd19b1aebe3191085015c024fa4c9a902ecc0e02dda0cdb9a", + "0201c0d9dcec93f8221c5de4fae9749c7fde1e81874157958457b6107cf7a5967713a644e90b7c3fb81b31477fee9a60e938013774c75c530928b17be69571bf842d8c", + "03007e3e98f984c396ad9cd7865d2b4924861a93f736cde1b4c2384eedd2beaf5b866132c45908e03c996a3550a5e79ab88ee94bec3b00ab38eff81887848d32fbcda7", + "0201875bc7dc551b1b65a9e1b8ccfaaf84ded1958b401494116a2fd4fb0babe0b3199974fc06c8b897222d79df3e4b7bc744aa6767f6b812efbf5d2c9e682dd3432d74", + "03006b6ad89abcb92465f041558fc546d4300fb8fbcc30b40a0852d697b532df128e11b91cce27dbd00ffe7875bd1c8fc0331d9b8d96981e3f92bde9afe337bcb8db55", + }, "P521", "P521_XMD:SHA-512_SSWU_RO_", "P521_XMD:SHA-512_SSWU_NU_", "0200c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", + "00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", + testHashToCurve{ + input: testHashToGroupInput, + dst: testHashToGroupDST, + hashToScalar: "01f4e5806586dbebd01e85b17da1eb2df4ac678bc8683b9baa5dd5fba6a0f9d1ff5621ed342a90273150fd095c7abc07f97d202183ec804d063b9fcc0b95daec0614", + hashToGroup: "0300d24ae26cefe28681d4cf35cf7bea7de3acd15f38ba0b835303c9cdc641d1912566041cb5f6939ad43f0b21e506cecc4a8124a0517dce94f2f1affa47f052f25bf0", + }, 67, 66, 5, }, { + [15]string{ + "5866666666666666666666666666666666666666666666666666666666666666", + "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", + "d4b4f5784868c3020403246717ec169ff79e26608ea126a1ab69ee77d1b16712", + "2f1132ca61ab38dff00f2fea3228f24c6c71d58085b80e47e19515cb27e8d047", + "edc876d6831fd2105d0b4389ca2e283166469289146e2ce06faefe98b22548df", + "f47e49f9d07ad2c1606b4d94067c41f9777d4ffda709b71da1d88628fce34d85", + "b862409fb5c4c4123df2abf7462b88f041ad36dd6864ce872fd5472be363c5b1", + "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", + "c0f1225584444ec730446e231390781ffdd2f256e9fcbeb2f40dddc2c2233d7f", + "2c7be86ab07488ba43e8e03d85a67625cfbf98c8544de4c877241b7aaafc7fe3", + "1337036ac32d8f30d4589c3c1c595812ce0fff40e37c6f5a97ab213f318290ad", + "f9e42d2edc81d23367967352b47e4856b82578634e6c1de72280ce8b60ce70c0", + "801f40eaaee1ef8723279a28b2cf4037b889dad222604678748b53ed0db0db92", + "39289c8998fd69835c26b619e89848a7bf02b7cb7ad1ba1581cbc4506f2550ce", + "df5c2eadc44c6d94a19a9aa118afe5ac3193d26401f76251f522ff042dfbcb92", + }, "Edwards25519", "edwards25519_XMD:SHA-512_ELL2_RO_", "edwards25519_XMD:SHA-512_ELL2_NU_", "5866666666666666666666666666666666666666666666666666666666666666", + "0900000000000000000000000000000000000000000000000000000000000000", "0100000000000000000000000000000000000000000000000000000000000000", + "57896044618658097711785492504343953926634992332820282019728792003956564819949", + testHashToCurve{ + input: testHashToGroupInput, + dst: testHashToGroupDST, + hashToScalar: "90249f56fa61b29fc09b8787d9954a6beba6ca49e25c80f78560ca5458e5b807", + hashToGroup: "a2ca6693cdda5b8d204a506fe873ce1d3e58d5b14d04635e13c10ba9d5637f8f", + }, 32, 32, 6, }, { + [15]string{ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", + "02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13", + "022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4", + "03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556", + "025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc", + "022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01", + "03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe", + "03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7", + "03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb", + "03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a", + "03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8", + "03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4", + "02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e", + }, "Secp256k1", "secp256k1_XMD:SHA-256_SSWU_RO_", "secp256k1_XMD:SHA-256_SSWU_NU_", "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", "000000000000000000000000000000000000000000000000000000000000000000", + "115792089237316195423570985008687907853269984665640564039457584007908834671663", + testHashToCurve{ + input: testHashToGroupInput, + dst: testHashToGroupDST, + hashToScalar: "782a63d48eace435ac06468208d9a62e3680e4ddc3977c4345b2c6de08258b69", + hashToGroup: "0210dca4244e263298000ff1e9f0dfbf1c28333e1f0a252024e8b20b9921cdf3b2", + }, 33, 32, 7, diff --git a/tests/utils_test.go b/tests/utils_test.go index 218b74a..c885c42 100644 --- a/tests/utils_test.go +++ b/tests/utils_test.go @@ -15,11 +15,15 @@ import ( "errors" "fmt" "testing" + + "github.com/bytemare/crypto" ) var ( errNoPanic = errors.New("no panic") errNoPanicMessage = errors.New("panic but no message") + errZeroLenDST = errors.New("zero-length DST") + errWrapGroup = "%s: %w" ) func hasPanic(f func()) (has bool, err error) { @@ -64,6 +68,34 @@ func testPanic(s string, expectedError error, f func()) error { return nil } +func decodeScalar(t *testing.T, g crypto.Group, input string) *crypto.Scalar { + b, err := hex.DecodeString(input) + if err != nil { + t.Error(err) + } + + s := g.NewScalar() + if err := s.Decode(b); err != nil { + t.Error(err) + } + + return s +} + +func decodeElement(t *testing.T, g crypto.Group, input string) *crypto.Element { + b, err := hex.DecodeString(input) + if err != nil { + t.Error(err) + } + + e := g.NewElement() + if err := e.Decode(b); err != nil { + t.Error(err) + } + + return e +} + type serde interface { Encode() []byte Decode(data []byte) error @@ -94,7 +126,7 @@ func testEncoding(t *testing.T, thing1, thing2 serde) { } func TestEncoding(t *testing.T) { - testAll(t, func(t *testing.T, group *testGroup) { + testAll(t, func(group *testGroup) { g := group.group scalar := g.NewScalar().Random() testEncoding(t, scalar, g.NewScalar())