Skip to content
This repository has been archived by the owner on Oct 3, 2024. It is now read-only.

Commit

Permalink
group.Order() now returns bytes, scalar.MinusOne() sets to order-1 (#68)
Browse files Browse the repository at this point in the history
* group.Order() now returns bytes, scalar.MinusOne() sets to order-1

---------

Signed-off-by: bytemare <[email protected]>
  • Loading branch information
bytemare authored Oct 2, 2024
1 parent 5fb54db commit b2be839
Show file tree
Hide file tree
Showing 19 changed files with 133 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .github/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ linters-settings:
threshold: 100
errcheck:
check-type-assertions: true
check-blank: true
check-blank: false
exclude-functions:
- (*crypto/Element).MarshalBinary
- (*crypto/Scalar).MarshalBinary
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
filippo.io/edwards25519 v1.1.0
filippo.io/nistec v0.0.3
github.com/bytemare/hash2curve v0.3.0
github.com/bytemare/secp256k1 v0.1.4
github.com/bytemare/secp256k1 v0.1.6
github.com/gtank/ristretto255 v0.1.2
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ github.com/bytemare/hash v0.3.0 h1:RqFMt3mqpF7UxLdjBrsOZm/2cz0cQiAOnYc9gDLopWE=
github.com/bytemare/hash v0.3.0/go.mod h1:YKOBchL0l8hRLFinVCL8YUKokGNIMhrWEHPHo3EV7/M=
github.com/bytemare/hash2curve v0.3.0 h1:41Npcbc+u/E252A5aCMtxDcz7JPkkX1QzShneTFm4eg=
github.com/bytemare/hash2curve v0.3.0/go.mod h1:itj45U8uqvCtWC0eCswIHVHswXcEHkpFui7gfJdPSfQ=
github.com/bytemare/secp256k1 v0.1.4 h1:6F1yP6RiUiWwH7AsGHsHktmHm24QcetdDcc39roBd2M=
github.com/bytemare/secp256k1 v0.1.4/go.mod h1:Pxb9miDs8PTt5mOktvvXiRflvLxI1wdxbXrc6IYsaho=
github.com/bytemare/secp256k1 v0.1.6 h1:5pOA84UBBTPTUmCkjtH6jHrbvZSh2kyxG0mW/OjSih0=
github.com/bytemare/secp256k1 v0.1.6/go.mod h1:Zr7o3YCog5jKx5JwgYbj984gRIqVioTDZMSDo1y0zgE=
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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
Expand Down
2 changes: 1 addition & 1 deletion groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func (g Group) ElementLength() int {
}

// Order returns the order of the canonical group of scalars.
func (g Group) Order() string {
func (g Group) Order() []byte {
return g.get().Order()
}

Expand Down
5 changes: 3 additions & 2 deletions internal/edwards25519/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package edwards25519

import (
"crypto"
"slices"

ed "filippo.io/edwards25519"

Expand Down Expand Up @@ -87,6 +88,6 @@ func (g Group) ElementLength() int {
}

// Order returns the order of the canonical group of scalars.
func (g Group) Order() string {
return orderPrime
func (g Group) Order() []byte {
return slices.Clone(orderBytes)
}
47 changes: 25 additions & 22 deletions internal/edwards25519/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,17 @@ import (
const inputLength = 64

var (
scZero Scalar
scOne Scalar
order big.Int
scZero Scalar
scOne Scalar
order big.Int
scMinusOne = []byte{
236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
}
orderBytes = []byte{
237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
}
)

func init() {
Expand Down Expand Up @@ -84,6 +92,12 @@ func (s *Scalar) One() internal.Scalar {
return s
}

// MinusOne sets the scalar to order-1, and returns it.
func (s *Scalar) MinusOne() internal.Scalar {
_ = s.decodeScalar(scMinusOne)
return s
}

// Random sets the current scalar 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 {
Expand Down Expand Up @@ -278,14 +292,11 @@ func (s *Scalar) SetUInt64(i uint64) internal.Scalar {
encoded := make([]byte, canonicalEncodingLength)
binary.LittleEndian.PutUint64(encoded, i)

sc, err := decodeScalar(encoded)
if err != nil {
if err := s.decodeScalar(encoded); err != nil {
// This cannot happen, since any uint64 is smaller than the order.
panic(fmt.Sprintf("unexpected decoding of uint64 scalar: %s", err))
}

s.set(sc)

return s
}

Expand Down Expand Up @@ -320,33 +331,25 @@ func (s *Scalar) Encode() []byte {
return s.scalar.Bytes()
}

func decodeScalar(scalar []byte) (*ed.Scalar, error) {
func (s *Scalar) decodeScalar(scalar []byte) error {
if len(scalar) == 0 {
return nil, internal.ErrParamNilScalar
return internal.ErrParamNilScalar
}

if len(scalar) != canonicalEncodingLength {
return nil, internal.ErrParamScalarLength
return internal.ErrParamScalarLength
}

s := ed.NewScalar()
if _, err := s.SetCanonicalBytes(scalar); err != nil {
return nil, fmt.Errorf("%w", err)
if _, err := s.scalar.SetCanonicalBytes(scalar); err != nil {
return fmt.Errorf("%w", err)
}

return s, nil
return nil
}

// Decode sets the receiver to a decoding of the input data, and returns an error on failure.
func (s *Scalar) Decode(in []byte) error {
sc, err := decodeScalar(in)
if err != nil {
return err
}

s.scalar = *sc

return nil
return s.decodeScalar(in)
}

// Hex returns the fixed-sized hexadecimal encoding of s.
Expand Down
5 changes: 5 additions & 0 deletions internal/field/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ func (f Field) Order() *big.Int {
return f.order
}

// PMinusOne returns p-1, the greatest integer below the order.
func (f Field) PMinusOne() *big.Int {
return new(big.Int).Sub(f.order, big.NewInt(1))
}

// ByteLen returns the length of the field order in bytes.
func (f Field) ByteLen() int {
return f.byteLen
Expand Down
2 changes: 1 addition & 1 deletion internal/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@ type Group interface {
ElementLength() int

// Order returns the order of the canonical group of scalars.
Order() string
Order() []byte
}
5 changes: 3 additions & 2 deletions internal/nist/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ func (g Group[P]) ElementLength() int {
}

// Order returns the order of the canonical group of scalars.
func (g Group[P]) Order() string {
return g.scalarField.Order().String()
func (g Group[P]) Order() []byte {
out := make([]byte, g.scalarField.ByteLen())
return g.scalarField.Order().FillBytes(out)
}

var (
Expand Down
6 changes: 6 additions & 0 deletions internal/nist/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ func (s *Scalar) One() internal.Scalar {
return s
}

// MinusOne sets the scalar to order-1, and returns it.
func (s *Scalar) MinusOne() internal.Scalar {
s.scalar.Set(s.field.PMinusOne())
return s
}

// 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 {
Expand Down
11 changes: 3 additions & 8 deletions internal/ristretto/ristretto.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package ristretto

import (
"crypto"
"slices"

"github.com/bytemare/hash2curve"
"github.com/gtank/ristretto255"
Expand All @@ -26,12 +27,6 @@ const (

// H2C represents the hash-to-curve string identifier.
H2C = "ristretto255_XMD:SHA-512_R255MAP_RO_"

// orderPrime represents curve25519's subgroup prime-order
// = 2^252 + 27742317777372353535851937790883648493
// = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed
// cofactor h = 8.
orderPrime = "7237005577332262213973186563042994240857116359379907606001950938285454250989"
)

// Group represents the Ristretto255 group. It exposes a prime-order group API with hash-to-curve operations.
Expand Down Expand Up @@ -99,6 +94,6 @@ func (g Group) ElementLength() int {
}

// Order returns the order of the canonical group of scalars.
func (g Group) Order() string {
return orderPrime
func (g Group) Order() []byte {
return slices.Clone(orderBytes)
}
56 changes: 29 additions & 27 deletions internal/ristretto/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"math/big"

"github.com/gtank/ristretto255"

Expand All @@ -23,9 +22,21 @@ import (
const canonicalEncodingLength = 32

var (
scZero = &Scalar{*ristretto255.NewScalar()}
scOne Scalar
order big.Int
scZero = &Scalar{*ristretto255.NewScalar()}
scOne Scalar
scMinusOne = []byte{
236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
}
// orderBytes represents curve25519's subgroup prime-order
// = 2^252 + 27742317777372353535851937790883648493
// = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed
// = 7237005577332262213973186563042994240857116359379907606001950938285454250989
// cofactor h = 8.
orderBytes = []byte{
237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
}
)

func init() {
Expand All @@ -36,10 +47,6 @@ func init() {
}); err != nil {
panic(err)
}

if _, ok := order.SetString(orderPrime, 10); !ok {
panic(internal.ErrBigIntConversion)
}
}

// Scalar implements the Scalar interface for Ristretto255 group scalars.
Expand Down Expand Up @@ -77,6 +84,12 @@ func (s *Scalar) One() internal.Scalar {
return s
}

// MinusOne sets the scalar to order-1, and returns it.
func (s *Scalar) MinusOne() internal.Scalar {
_ = s.decodeScalar(scMinusOne)
return s
}

// Random sets the current scalar 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 {
Expand Down Expand Up @@ -270,14 +283,11 @@ func (s *Scalar) SetUInt64(i uint64) internal.Scalar {
encoded := make([]byte, canonicalEncodingLength)
binary.LittleEndian.PutUint64(encoded, i)

sc, err := decodeScalar(encoded)
if err != nil {
if err := s.decodeScalar(encoded); err != nil {
// This cannot happen, since any uint64 is smaller than the order.
panic(fmt.Sprintf("unexpected decoding of uint64 scalar: %s", err))
}

s.set(sc)

return s
}

Expand Down Expand Up @@ -312,33 +322,25 @@ func (s *Scalar) Encode() []byte {
return s.scalar.Encode(nil)
}

func decodeScalar(scalar []byte) (*ristretto255.Scalar, error) {
func (s *Scalar) decodeScalar(scalar []byte) error {
if len(scalar) == 0 {
return nil, internal.ErrParamNilScalar
return internal.ErrParamNilScalar
}

if len(scalar) != canonicalEncodingLength {
return nil, internal.ErrParamScalarLength
return internal.ErrParamScalarLength
}

s := ristretto255.NewScalar()
if err := s.Decode(scalar); err != nil {
return nil, fmt.Errorf("%w", err)
if err := s.scalar.Decode(scalar); err != nil {
return fmt.Errorf("%w", err)
}

return s, nil
return nil
}

// Decode sets the receiver to a decoding of the input data, and returns an error on failure.
func (s *Scalar) Decode(in []byte) error {
sc, err := decodeScalar(in)
if err != nil {
return err
}

s.scalar = *sc

return nil
return s.decodeScalar(in)
}

// Hex returns the fixed-sized hexadecimal encoding of s.
Expand Down
3 changes: 3 additions & 0 deletions internal/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type Scalar interface {
// One sets the scalar to 1, and returns it.
One() Scalar

// MinusOne sets the scalar to order-1, and returns it.
MinusOne() Scalar

// Random sets the current scalar 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.
Random() Scalar
Expand Down
16 changes: 7 additions & 9 deletions internal/secp256k1/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@ const (
// E2CSECP256K1 represents the encode-to-curve string identifier for Secp256k1.
E2CSECP256K1 = "secp256k1_XMD:SHA-256_SSWU_NU_"

groupOrder = "115792089237316195423570985008687907852837564279074904382605163141518161494337"
scalarLength = 32
elementLength = 33
scalarLength = 32
)

// Group represents the Secp256k1 group. It exposes a prime-order group API with hash-to-curve operations.
// Group represents the SECp256k1 group. It exposes a prime-order group API with hash-to-curve operations.
type Group struct{}

// New returns a new instantiation of the Secp256k1 Group.
// New returns a new instantiation of the SECp256k1 Group.
func New() internal.Group {
return Group{}
}
Expand Down Expand Up @@ -85,15 +83,15 @@ func (g Group) Ciphersuite() string {

// ScalarLength returns the byte size of an encoded scalar.
func (g Group) ScalarLength() int {
return scalarLength
return secp256k1.ScalarLength()
}

// ElementLength returns the byte size of an encoded element.
func (g Group) ElementLength() int {
return elementLength
return secp256k1.ElementLength()
}

// Order returns the order of the canonical group of scalars.
func (g Group) Order() string {
return groupOrder
func (g Group) Order() []byte {
return secp256k1.Order()
}
6 changes: 6 additions & 0 deletions internal/secp256k1/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ func (s *Scalar) One() internal.Scalar {
return s
}

// MinusOne sets the scalar to order-1, and returns it.
func (s *Scalar) MinusOne() internal.Scalar {
s.scalar.MinusOne()
return s
}

// Random sets the current scalar 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 {
Expand Down
Loading

0 comments on commit b2be839

Please sign in to comment.