Skip to content

Commit

Permalink
update dependencies and docs, add tests, minor fixes (#44)
Browse files Browse the repository at this point in the history
* update dependencies and docs, add tests, minor fixes

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>

---------

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>
  • Loading branch information
bytemare committed Dec 30, 2023
1 parent 88de307 commit 622e6c4
Show file tree
Hide file tree
Showing 22 changed files with 543 additions and 177 deletions.
2 changes: 1 addition & 1 deletion .github/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ linters:
- errorlint
- execinquery
#- exhaustive
- exhaustruct
#- exhaustruct
- exportloopref
- forbidigo
- forcetypeassert
Expand Down
27 changes: 13 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 13 additions & 5 deletions element.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
24 changes: 12 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
5 changes: 5 additions & 0 deletions groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
7 changes: 4 additions & 3 deletions internal/edwards25519/element.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
}
Expand All @@ -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
Expand Down
7 changes: 3 additions & 4 deletions internal/edwards25519/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
77 changes: 0 additions & 77 deletions internal/field/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand All @@ -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
}
3 changes: 3 additions & 0 deletions internal/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
7 changes: 1 addition & 6 deletions internal/nist/element.go
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down

0 comments on commit 622e6c4

Please sign in to comment.