From d26b007526e3c294dd8150ff8aadae3b55782fa5 Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:49:35 +0100 Subject: [PATCH 1/5] update some dependencies and use scalarbasemult for nist curves when posssible Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- .github/Makefile | 3 ++- .github/workflows/snyk.yml | 2 +- go.mod | 8 ++++---- go.sum | 12 ++++++------ groups.go | 2 ++ internal/nist/element.go | 15 +++++++++++++-- internal/nist/group.go | 10 +++++----- internal/nist/point.go | 1 + tests/element_test.go | 18 +++++++++++++++--- tests/h2c_test.go | 12 ++++++++++-- tests/scalar_test.go | 10 ++++++++-- 11 files changed, 67 insertions(+), 26 deletions(-) diff --git a/.github/Makefile b/.github/Makefile index 77e5c57..0fe46ed 100644 --- a/.github/Makefile +++ b/.github/Makefile @@ -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 @@ -20,7 +21,7 @@ fmt: @echo "Formatting ..." @go mod tidy @go fmt ../... - #@golines -m 120 -t 4 -w ../ + @golines -d -m 120 -t 4 -w ../ @gofumpt -w -extra ../ @gci write -s Standard -s Default -s "Prefix($(shell go list -m))" ../ @fieldalignment -fix ../... diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 1dd259d..98ff9ce 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -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: diff --git a/go.mod b/go.mod index f201c5b..65bd40d 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ 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 @@ -12,6 +12,6 @@ require ( 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 ) diff --git a/go.sum b/go.sum index af79dc6..8e50203 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= diff --git a/groups.go b/groups.go index 72da99a..69c7524 100644 --- a/groups.go +++ b/groups.go @@ -169,6 +169,8 @@ func (g Group) init() { g.initGroup(edwards25519.New) case Secp256k1: g.initGroup(secp256k1.New) + case maxID: + panic("group not recognized") default: panic("group not recognized") } diff --git a/internal/nist/element.go b/internal/nist/element.go index 489d521..5e3b5e3 100644 --- a/internal/nist/element.go +++ b/internal/nist/element.go @@ -110,10 +110,21 @@ func (e *Element[P]) Subtract(element internal.Element) internal.Element { 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) + } + } else { + if _, err := e.p.ScalarMult(e.p, scalar.Encode()); err != nil { + panic(err) + } } return e diff --git a/internal/nist/group.go b/internal/nist/group.go index 9c52076..f9955f4 100644 --- a/internal/nist/group.go +++ b/internal/nist/group.go @@ -172,7 +172,7 @@ func initP256() { nistec.NewP256Point, ) p256.curve.setMapping(crypto.SHA256, "-10", 48) - p256.setScalarField("0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551") + setScalarField(&p256, "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551") } func initP384() { @@ -185,7 +185,7 @@ func initP384() { nistec.NewP384Point, ) p384.curve.setMapping(crypto.SHA384, "-12", 72) - p384.setScalarField( + setScalarField(&p384, "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973", ) } @@ -202,13 +202,13 @@ func initP521() { 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) } diff --git a/internal/nist/point.go b/internal/nist/point.go index f6dca8c..19721d7 100644 --- a/internal/nist/point.go +++ b/internal/nist/point.go @@ -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 diff --git a/tests/element_test.go b/tests/element_test.go index 0628e99..6a8a4c2 100644 --- a/tests/element_test.go +++ b/tests/element_test.go @@ -123,17 +123,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, + ) } }) } diff --git a/tests/h2c_test.go b/tests/h2c_test.go index 51b925a..b56004e 100644 --- a/tests/h2c_test.go +++ b/tests/h2c_test.go @@ -137,13 +137,21 @@ func (v *h2cVector) run(t *testing.T) { 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())) + t.Fatalf( + "Unexpected HashToGroup output.\n\tExpected %q\n\tgot \t%q", + expected, + hex.EncodeToString(p.Encode()), + ) } 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())) + t.Fatalf( + "Unexpected EncodeToGroup output.\n\tExpected %q\n\tgot %q", + expected, + hex.EncodeToString(p.Encode()), + ) } default: t.Fatal("ciphersuite not recognized") diff --git a/tests/scalar_test.go b/tests/scalar_test.go index 3bdb9f1..428c121 100644 --- a/tests/scalar_test.go +++ b/tests/scalar_test.go @@ -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, + ) } }) } @@ -368,7 +372,9 @@ 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 From 35acb60279a18cb89eebe1dceed4005216283afb Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:50:39 +0100 Subject: [PATCH 2/5] update to go 1.21 in workflow Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6962cf8..e23587d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 From 4813f1afe92c505c3defe1e20b98920f64d70f56 Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:00:15 +0100 Subject: [PATCH 3/5] some linting Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- internal/nist/element.go | 2 ++ tests/element_test.go | 15 ++++++++++----- tests/h2c_test.go | 31 +++++++++++++++++-------------- tests/scalar_test.go | 3 ++- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/internal/nist/element.go b/internal/nist/element.go index 5e3b5e3..cac430c 100644 --- a/internal/nist/element.go +++ b/internal/nist/element.go @@ -194,6 +194,8 @@ func encodeInfinity[Point nistECPoint[Point]](element *Element[Point]) []byte { encodedLength = p384CompressedEncodingLength case "P521": encodedLength = p521CompressedEncodingLength + default: + panic("could not infer nist curve") } return make([]byte, encodedLength) diff --git a/tests/element_test.go b/tests/element_test.go index 6a8a4c2..1a4690f 100644 --- a/tests/element_test.go +++ b/tests/element_test.go @@ -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) } } diff --git a/tests/h2c_test.go b/tests/h2c_test.go index b56004e..2796a92 100644 --- a/tests/h2c_test.go +++ b/tests/h2c_test.go @@ -12,6 +12,7 @@ import ( "crypto/elliptic" "encoding/hex" "encoding/json" + "fmt" "io" "math/big" "os" @@ -135,29 +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 diff --git a/tests/scalar_test.go b/tests/scalar_test.go index 428c121..b2a85c8 100644 --- a/tests/scalar_test.go +++ b/tests/scalar_test.go @@ -373,7 +373,8 @@ func scalarTestPow(t *testing.T, g crypto.Group) { res = s.Pow(exp) if res.Equal(result) != 1 { t.Fatal( - "expected 3**255 = 11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6AB", + "expected 3**255 = " + + "11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6AB", ) } From d6c389d41b88f48623c57992cd9b63ccb4ab5d44 Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:05:03 +0100 Subject: [PATCH 4/5] some linting Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- .github/.golangci.yml | 2 +- .github/Makefile | 2 +- groups.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/.golangci.yml b/.github/.golangci.yml index fc3549c..3b96dd1 100644 --- a/.github/.golangci.yml +++ b/.github/.golangci.yml @@ -9,7 +9,7 @@ linters: - contextcheck - cyclop - decorder - - depguard + #- depguard - dogsled - dupl - durationcheck diff --git a/.github/Makefile b/.github/Makefile index 0fe46ed..6498af2 100644 --- a/.github/Makefile +++ b/.github/Makefile @@ -21,7 +21,7 @@ fmt: @echo "Formatting ..." @go mod tidy @go fmt ../... - @golines -d -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 ../... diff --git a/groups.go b/groups.go index 69c7524..ad14ec7 100644 --- a/groups.go +++ b/groups.go @@ -170,7 +170,7 @@ func (g Group) init() { case Secp256k1: g.initGroup(secp256k1.New) case maxID: - panic("group not recognized") + fallthrough default: panic("group not recognized") } From 55b926d6c7d65f6a74d2f5458221cad7820142f7 Mon Sep 17 00:00:00 2001 From: bytemare <3641580+bytemare@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:31:41 +0100 Subject: [PATCH 5/5] add tests Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- groups.go | 4 ++-- tests/groups_test.go | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/groups.go b/groups.go index ad14ec7..a2de0b2 100644 --- a/groups.go +++ b/groups.go @@ -158,7 +158,7 @@ func (g Group) init() { case Ristretto255Sha512: g.initGroup(ristretto.New) case decaf448Shake256: - panic("Decaf is not yet supported") + panic("decaf is not yet supported") case P256Sha256: g.initGroup(nist.P256) case P384Sha384: @@ -170,7 +170,7 @@ func (g Group) init() { case Secp256k1: g.initGroup(secp256k1.New) case maxID: - fallthrough + panic("group not recognized") default: panic("group not recognized") } diff --git a/tests/groups_test.go b/tests/groups_test.go index 0322d48..1091813 100644 --- a/tests/groups_test.go +++ b/tests/groups_test.go @@ -10,6 +10,7 @@ package group_test import ( "encoding/hex" + "errors" "testing" "github.com/bytemare/crypto" @@ -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) @@ -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) {