Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update some dependencies and use scalarbasemult for nist curves when posssible #43

Merged
merged 5 commits into from
Nov 27, 2023
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
2 changes: 1 addition & 1 deletion .github/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ linters:
- contextcheck
- cyclop
- decorder
- depguard
#- depguard
- dogsled
- dupl
- durationcheck
Expand Down
3 changes: 2 additions & 1 deletion .github/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ update:
.PHONY: update-linters
update-linters:
@echo "Updating linters..."
@go install golang.org/x/tools/cmd/goimports@latest
@go install mvdan.cc/gofumpt@latest
@go install github.com/daixiang0/gci@latest
@go install github.com/segmentio/golines@latest
Expand All @@ -20,7 +21,7 @@ fmt:
@echo "Formatting ..."
@go mod tidy
@go fmt ../...
#@golines -m 120 -t 4 -w ../
@golines -m 120 -t 4 -w ../
@gofumpt -w -extra ../
@gci write -s Standard -s Default -s "Prefix($(shell go list -m))" ../
@fieldalignment -fix ../...
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
strategy:
fail-fast: false
matrix:
go: [ '1.20', '1.19' ]
go: [ '1.21', '1.20' ]
steps:
- name: Checkout repo
uses: actions/checkout@27135e314dd1818f797af1db9dae03a9f045786b # pin@master
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/snyk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089 # pin@master
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@806182742461562b67788a64410098c9d9b96adb # pin@master
uses: snyk/actions/golang@3e2680e8df93a24b52d119b1305fb7cedc60ceae # pin@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
module github.com/bytemare/crypto

go 1.20
go 1.21

require (
filippo.io/edwards25519 v1.0.0
filippo.io/nistec v0.0.2
filippo.io/nistec v0.0.3
github.com/bytemare/hash2curve v0.2.2
github.com/bytemare/secp256k1 v0.1.0
github.com/gtank/ristretto255 v0.1.2
)

require (
github.com/bytemare/hash v0.1.5 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/sys v0.14.0 // indirect
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/nistec v0.0.2 h1:/NIXTUimcHIh0E2DsYucHlICvUisgj28/XEnKSEptUs=
filippo.io/nistec v0.0.2/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw=
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=
Expand All @@ -10,7 +10,7 @@ github.com/bytemare/secp256k1 v0.1.0 h1:kjVJ06GAHSa+EJ7Rz1LdVgE0DQWdvUT77tmcGf7e
github.com/bytemare/secp256k1 v0.1.0/go.mod h1:hzquMsr3GXhVcqL9qFX7GGjmcT5dlQldKrArd7tcXHE=
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.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
4 changes: 3 additions & 1 deletion groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
case Ristretto255Sha512:
g.initGroup(ristretto.New)
case decaf448Shake256:
panic("Decaf is not yet supported")
panic("decaf is not yet supported")

Check warning on line 161 in groups.go

View check run for this annotation

Codecov / codecov/patch

groups.go#L161

Added line #L161 was not covered by tests
case P256Sha256:
g.initGroup(nist.P256)
case P384Sha384:
Expand All @@ -169,6 +169,8 @@
g.initGroup(edwards25519.New)
case Secp256k1:
g.initGroup(secp256k1.New)
case maxID:
panic("group not recognized")

Check warning on line 173 in groups.go

View check run for this annotation

Codecov / codecov/patch

groups.go#L173

Added line #L173 was not covered by tests
default:
panic("group not recognized")
}
Expand Down
17 changes: 15 additions & 2 deletions internal/nist/element.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,21 @@
return e
}

func (e *Element[P]) isGenerator() bool {
b := e.new().SetGenerator().BytesCompressed()
return subtle.ConstantTimeCompare(b, e.Encode()) == 1
}

// Multiply sets the receiver to the scalar multiplication of the receiver with the given Scalar, and returns it.
func (e *Element[P]) Multiply(scalar internal.Scalar) internal.Element {
if _, err := e.p.ScalarMult(e.p, scalar.Encode()); err != nil {
panic(err)
if e.isGenerator() {
if _, err := e.p.ScalarBaseMult(scalar.Encode()); err != nil {
panic(err)

Check warning on line 122 in internal/nist/element.go

View check run for this annotation

Codecov / codecov/patch

internal/nist/element.go#L122

Added line #L122 was not covered by tests
}
} else {
if _, err := e.p.ScalarMult(e.p, scalar.Encode()); err != nil {
panic(err)

Check warning on line 126 in internal/nist/element.go

View check run for this annotation

Codecov / codecov/patch

internal/nist/element.go#L126

Added line #L126 was not covered by tests
}
}

return e
Expand Down Expand Up @@ -183,6 +194,8 @@
encodedLength = p384CompressedEncodingLength
case "P521":
encodedLength = p521CompressedEncodingLength
default:
panic("could not infer nist curve")

Check warning on line 198 in internal/nist/element.go

View check run for this annotation

Codecov / codecov/patch

internal/nist/element.go#L197-L198

Added lines #L197 - L198 were not covered by tests
}

return make([]byte, encodedLength)
Expand All @@ -191,8 +204,8 @@
// XCoordinate returns the encoded x coordinate of the element.
func (e *Element[P]) XCoordinate() []byte {
if e.IsIdentity() {
inf := encodeInfinity(e)
return inf[:len(inf)-1]

Check warning on line 208 in internal/nist/element.go

View check run for this annotation

Codecov / codecov/patch

internal/nist/element.go#L207-L208

Added lines #L207 - L208 were not covered by tests
}

b, err := e.p.BytesX()
Expand Down
10 changes: 5 additions & 5 deletions internal/nist/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@
// HashToScalar returns a safe mapping of the arbitrary input to a Scalar.
// The DST must not be empty or nil, and is recommended to be longer than 16 bytes.
func (g Group[P]) HashToScalar(input, dst []byte) internal.Scalar {
s := hash2curve.HashToFieldXMD(g.curve.hash, input, dst, 1, 1, g.curve.secLength, g.scalarField.Order())[0]

Check warning on line 101 in internal/nist/group.go

View check run for this annotation

Codecov / codecov/patch

internal/nist/group.go#L101

Added line #L101 was not covered by tests

// If necessary, build a buffer of right size, so it gets correctly interpreted.
bytes := s.Bytes()

length := g.ScalarLength()

Check warning on line 106 in internal/nist/group.go

View check run for this annotation

Codecov / codecov/patch

internal/nist/group.go#L106

Added line #L106 was not covered by tests
if l := length - len(bytes); l > 0 {
buf := make([]byte, l, length)
buf = append(buf, bytes...)
Expand All @@ -111,7 +111,7 @@
}

res := newScalar(&g.scalarField)
res.scalar.SetBytes(bytes)

Check warning on line 114 in internal/nist/group.go

View check run for this annotation

Codecov / codecov/patch

internal/nist/group.go#L114

Added line #L114 was not covered by tests

return res
}
Expand Down Expand Up @@ -172,7 +172,7 @@
nistec.NewP256Point,
)
p256.curve.setMapping(crypto.SHA256, "-10", 48)
p256.setScalarField("0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551")
setScalarField(&p256, "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551")
}

func initP384() {
Expand All @@ -185,7 +185,7 @@
nistec.NewP384Point,
)
p384.curve.setMapping(crypto.SHA384, "-12", 72)
p384.setScalarField(
setScalarField(&p384,
"0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973",
)
}
Expand All @@ -202,13 +202,13 @@
nistec.NewP521Point,
)
p521.curve.setMapping(crypto.SHA512, "-4", 98)
p521.setScalarField(
"0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" +
setScalarField(&p521,
"0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+
"a51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
)
}

func (g *Group[Point]) setScalarField(order string) {
func setScalarField[Point nistECPoint[Point]](g *Group[Point], order string) {
prime := field.String2Int(order)
g.scalarField = field.NewField(&prime)
}
1 change: 1 addition & 0 deletions internal/nist/point.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package nist

type nistECPoint[point any] interface {
Add(p1, p2 point) point
Negate(p point) point
BytesCompressed() []byte
BytesX() ([]byte, error)
Double(p point) point
Expand Down
33 changes: 25 additions & 8 deletions tests/element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,30 @@ func TestElement_WrongInput(t *testing.T) {
t.Fatalf("Invalid group id %d", group.group)
}

if err := testPanic(errWrongGroup, internal.ErrCastElement, exec(element.Add, alternativeGroup.NewElement())); err != nil {
if err := testPanic(errWrongGroup, internal.ErrCastElement,
exec(element.Add, alternativeGroup.NewElement())); err != nil {
t.Fatal(err)
}

if err := testPanic(errWrongGroup, internal.ErrCastElement, exec(element.Subtract, alternativeGroup.NewElement())); err != nil {
if err := testPanic(errWrongGroup, internal.ErrCastElement,
exec(element.Subtract, alternativeGroup.NewElement())); err != nil {
t.Fatal(err)
}

if err := testPanic(errWrongGroup, internal.ErrCastElement, exec(element.Set, alternativeGroup.NewElement())); err != nil {
if err := testPanic(errWrongGroup, internal.ErrCastElement,
exec(element.Set, alternativeGroup.NewElement())); err != nil {
t.Fatal(err)
}

if err := testPanic(errWrongGroup, internal.ErrCastElement, equal(element.Equal, alternativeGroup.NewElement())); err != nil {
if err := testPanic(errWrongGroup, internal.ErrCastElement,
equal(element.Equal, alternativeGroup.NewElement())); err != nil {
t.Fatal(err)
}
})

// Specifically test Ristretto
if err := testPanic(errWrongGroup, internal.ErrCastScalar, mult(crypto.Ristretto255Sha512.NewElement().Multiply, crypto.P384Sha384.NewScalar())); err != nil {
if err := testPanic(errWrongGroup, internal.ErrCastScalar,
mult(crypto.Ristretto255Sha512.NewElement().Multiply, crypto.P384Sha384.NewScalar())); err != nil {
t.Fatal(err)
}
}
Expand All @@ -123,17 +128,29 @@ func TestElement_EncodedLength(t *testing.T) {
testAll(t, func(t2 *testing.T, group *testGroup) {
id := group.group.NewElement().Identity().Encode()
if len(id) != group.elementLength {
t.Fatalf("Encode() of the identity element is expected to return %d bytes, but returned %d bytes", group.elementLength, len(id))
t.Fatalf(
"Encode() of the identity element is expected to return %d bytes, but returned %d bytes",
group.elementLength,
len(id),
)
}

encodedID := hex.EncodeToString(id)
if encodedID != group.identity {
t.Fatalf("Encode() of the identity element is unexpected.\n\twant: %v\n\tgot : %v", group.identity, encodedID)
t.Fatalf(
"Encode() of the identity element is unexpected.\n\twant: %v\n\tgot : %v",
group.identity,
encodedID,
)
}

encodedElement := group.group.NewElement().Base().Multiply(group.group.NewScalar().Random()).Encode()
if len(encodedElement) != group.elementLength {
t.Fatalf("Encode() is expected to return %d bytes, but returned %d bytes", group.elementLength, encodedElement)
t.Fatalf(
"Encode() is expected to return %d bytes, but returned %d bytes",
group.elementLength,
encodedElement,
)
}
})
}
Expand Down
19 changes: 19 additions & 0 deletions tests/groups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package group_test

import (
"encoding/hex"
"errors"
"testing"

"github.com/bytemare/crypto"
Expand All @@ -26,6 +27,8 @@ func TestAvailability(t *testing.T) {
}

func TestNonAvailability(t *testing.T) {
errInvalidID := errors.New("invalid group identifier")

oob := crypto.Group(0)
if oob.Available() {
t.Errorf(consideredAvailableFmt, oob)
Expand All @@ -36,10 +39,26 @@ func TestNonAvailability(t *testing.T) {
t.Errorf(consideredAvailableFmt, d)
}

if err := testPanic("decaf availability", errInvalidID,
func() { _ = d.String() }); err != nil {
t.Fatal(err)
}

oob = crypto.Secp256k1 + 1
if oob.Available() {
t.Errorf(consideredAvailableFmt, oob)
}

if err := testPanic("oob availability", errInvalidID,
func() { _ = oob.String() }); err != nil {
t.Fatal(err)
}

oob++
if err := testPanic("oob availability", errInvalidID,
func() { _ = oob.String() }); err != nil {
t.Fatal(err)
}
}

func TestGroup_Base(t *testing.T) {
Expand Down
23 changes: 17 additions & 6 deletions tests/h2c_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"crypto/elliptic"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/big"
"os"
Expand Down Expand Up @@ -135,21 +136,31 @@ func (v *h2cVector) run(t *testing.T) {
switch v.Ciphersuite[len(v.Ciphersuite)-3:] {
case "RO_":
p := v.group.HashToGroup([]byte(v.Msg), []byte(v.Dst))

if hex.EncodeToString(p.Encode()) != expected {
t.Fatalf("Unexpected HashToGroup output.\n\tExpected %q\n\tgot \t%q", expected, hex.EncodeToString(p.Encode()))
if err := verifyEncoding(p, "HashToGroup", expected); err != nil {
t.Fatal(err)
}
case "NU_":
p := v.group.EncodeToGroup([]byte(v.Msg), []byte(v.Dst))

if hex.EncodeToString(p.Encode()) != expected {
t.Fatalf("Unexpected EncodeToGroup output.\n\tExpected %q\n\tgot %q", expected, hex.EncodeToString(p.Encode()))
if err := verifyEncoding(p, "EncodeToGroup", expected); err != nil {
t.Fatal(err)
}
default:
t.Fatal("ciphersuite not recognized")
}
}

func verifyEncoding(p *crypto.Element, function, expected string) error {
if hex.EncodeToString(p.Encode()) != expected {
return fmt.Errorf("Unexpected %s output.\n\tExpected %q\n\tgot %q",
function,
expected,
hex.EncodeToString(p.Encode()),
)
}

return nil
}

func (v *h2cVectors) runCiphersuite(t *testing.T) {
for _, vector := range v.Vectors {
vector.h2cVectors = v
Expand Down
11 changes: 9 additions & 2 deletions tests/scalar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,11 @@ func TestScalar_EncodedLength(t *testing.T) {
testAll(t, func(t2 *testing.T, group *testGroup) {
encodedScalar := group.group.NewScalar().Random().Encode()
if len(encodedScalar) != group.scalarLength {
t.Fatalf("Encode() is expected to return %d bytes, but returned %d bytes", group.scalarLength, encodedScalar)
t.Fatalf(
"Encode() is expected to return %d bytes, but returned %d bytes",
group.scalarLength,
encodedScalar,
)
}
})
}
Expand Down Expand Up @@ -368,7 +372,10 @@ func scalarTestPow(t *testing.T, g crypto.Group) {
}
res = s.Pow(exp)
if res.Equal(result) != 1 {
t.Fatal("expected 3**255 = 11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6AB")
t.Fatal(
"expected 3**255 = " +
"11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6AB",
)
}

// 7945232487465**513
Expand Down
Loading