diff --git a/go.mod b/go.mod index 500267617..21550ec0c 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/Masterminds/sprig/v3 v3.3.0 github.com/Masterminds/squirrel v1.5.4 github.com/Masterminds/vcs v1.13.3 + github.com/ProtonMail/go-crypto v1.4.1 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/cyphar/filepath-securejoin v0.7.0 github.com/distribution/distribution/v3 v3.1.1 @@ -59,6 +60,7 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/cloudflare/circl v1.6.3 // indirect github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/go.sum b/go.sum index 4efd142be..f6f00bf75 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8 github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/vcs v1.13.3 h1:IIA2aBdXvfbIM+yl/eTnL4hb1XwdpvuQLglAix1gweE= github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8= +github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= +github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= @@ -49,6 +51,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go index 7f89ef3f5..31279336d 100644 --- a/pkg/provenance/sign.go +++ b/pkg/provenance/sign.go @@ -24,10 +24,10 @@ import ( "path/filepath" "strings" + "github.com/ProtonMail/go-crypto/openpgp" + "github.com/ProtonMail/go-crypto/openpgp/clearsign" + "github.com/ProtonMail/go-crypto/openpgp/packet" "github.com/pkg/errors" - "golang.org/x/crypto/openpgp" //nolint - "golang.org/x/crypto/openpgp/clearsign" //nolint - "golang.org/x/crypto/openpgp/packet" //nolint "sigs.k8s.io/yaml" hapi "helm.sh/helm/v3/pkg/chart" @@ -314,8 +314,9 @@ func (s *Signatory) decodeSignature(filename string) (*clearsign.Block, error) { func (s *Signatory) verifySignature(block *clearsign.Block) (*openpgp.Entity, error) { return openpgp.CheckDetachedSignature( s.KeyRing, - bytes.NewBuffer(block.Bytes), + bytes.NewReader(block.Bytes), block.ArmoredSignature.Body, + &defaultPGPConfig, ) } diff --git a/pkg/provenance/sign_test.go b/pkg/provenance/sign_test.go index bf6848368..cce39b0d0 100644 --- a/pkg/provenance/sign_test.go +++ b/pkg/provenance/sign_test.go @@ -24,7 +24,8 @@ import ( "strings" "testing" - pgperrors "golang.org/x/crypto/openpgp/errors" //nolint + pgperrors "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/packet" ) const ( @@ -56,6 +57,9 @@ const ( // testTamperedSigBlock is a tampered copy of msgblock.yaml.asc testTamperedSigBlock = "testdata/msgblock.yaml.tampered" + // testMixedKeyring points to a keyring containing RSA and ed25519 keys. + testMixedKeyring = "testdata/helm-mixed-keyring.pub" + // testSumfile points to a SHA256 sum generated by an external tool. // We always want to validate against an external tool's representation to // verify that we haven't done something stupid. This file was generated @@ -232,6 +236,56 @@ func TestClearSign(t *testing.T) { } } +func TestMixedKeyringRSASigningAndVerification(t *testing.T) { + signer, err := NewFromFiles(testKeyfile, testMixedKeyring) + if err != nil { + t.Fatal(err) + } + + if signer.Entity == nil { + t.Fatal("expected signer entity to be loaded") + } + + if signer.Entity.PrivateKey == nil { + t.Fatal("expected signer private key to be loaded") + } + + if signer.Entity.PrivateKey.PubKeyAlgo != packet.PubKeyAlgoRSA { + t.Fatalf("expected RSA key but got %v", signer.Entity.PrivateKey.PubKeyAlgo) + } + + sig, err := signer.ClearSign(testChartfile) + if err != nil { + t.Fatalf("failed to sign chart: %v", err) + } + + sigpath := filepath.Join(t.TempDir(), "hashtest-1.2.3.tgz.prov") + if err := os.WriteFile(sigpath, []byte(sig), 0o644); err != nil { + t.Fatal(err) + } + + verification, err := signer.Verify(testChartfile, sigpath) + if err != nil { + t.Fatalf("failed to verify chart signature: %v", err) + } + + if verification.SignedBy == nil { + t.Fatal("expected verification to include signer") + } + + if verification.SignedBy.PrimaryKey == nil { + t.Fatal("expected verification to include signer primary key") + } + + if verification.SignedBy.PrimaryKey.PubKeyAlgo != packet.PubKeyAlgoRSA { + t.Fatalf("expected verification to report RSA key but got %v", verification.SignedBy.PrimaryKey.PubKeyAlgo) + } + + if _, ok := verification.SignedBy.Identities[testKeyName]; !ok { + t.Fatalf("expected verification to be signed by %q", testKeyName) + } +} + // failSigner always fails to sign and returns an error type failSigner struct{} diff --git a/pkg/provenance/testdata/helm-mixed-keyring.pub b/pkg/provenance/testdata/helm-mixed-keyring.pub new file mode 100644 index 000000000..7985bd20f Binary files /dev/null and b/pkg/provenance/testdata/helm-mixed-keyring.pub differ