From 3097b7a4e921b80a7a3e10ecdfc168cc12fca66e Mon Sep 17 00:00:00 2001 From: James Hewitt Date: Fri, 24 Mar 2023 15:19:15 +0000 Subject: [PATCH 1/4] Verify signatures from a trust store Add the ability to use an on-disk trust store to verify signatures. Also allow the user to trust any known fingerprint instead of having to specify one. Signed-off-by: James Hewitt --- cmd/skopeo/signing.go | 29 ++++++++++++++++++++++++++--- cmd/skopeo/signing_test.go | 15 ++++++++++++++- docs/skopeo-standalone-verify.1.md | 6 +++++- integration/signing_test.go | 2 +- systemtest/050-signing.bats | 2 +- 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/cmd/skopeo/signing.go b/cmd/skopeo/signing.go index 5c38a469..07a3ab52 100644 --- a/cmd/skopeo/signing.go +++ b/cmd/skopeo/signing.go @@ -67,6 +67,7 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error { } type standaloneVerifyOptions struct { + truststore string } func standaloneVerifyCmd() *cobra.Command { @@ -74,8 +75,13 @@ func standaloneVerifyCmd() *cobra.Command { cmd := &cobra.Command{ Use: "standalone-verify MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT SIGNATURE", Short: "Verify a signature using local files", - RunE: commandAction(opts.run), + Long: `Verify a signature using local files + +KEY-FINGERPRINT can be an exact fingerprint, or "any" if you trust all the keys in the trust store.`, + RunE: commandAction(opts.run), } + flags := cmd.Flags() + flags.StringVar(&opts.truststore, "truststore", "", "Trust store of public keys. Defaults to using local gpg keys.") adjustUsage(cmd) return cmd } @@ -98,17 +104,34 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error return fmt.Errorf("Error reading signature from %s: %v", signaturePath, err) } - mech, err := signature.NewGPGSigningMechanism() + var mech signature.SigningMechanism + if opts.truststore != "" { + truststore, err := os.ReadFile(opts.truststore) + if err != nil { + return fmt.Errorf("Error reading trust store from %s: %v", opts.truststore, err) + } + mech, _, err = signature.NewEphemeralGPGSigningMechanism(truststore) + } else { + mech, err = signature.NewGPGSigningMechanism() + } if err != nil { return fmt.Errorf("Error initializing GPG: %v", err) } defer mech.Close() + + if expectedFingerprint == "any" { + _, expectedFingerprint, err = mech.Verify(unverifiedSignature) + if err != nil { + return fmt.Errorf("Could not determine fingerprint from signature: %v", err) + } + } + sig, err := signature.VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprint) if err != nil { return fmt.Errorf("Error verifying signature: %v", err) } - fmt.Fprintf(stdout, "Signature verified, digest %s\n", sig.DockerManifestDigest) + fmt.Fprintf(stdout, "Signature verified using fingerprint %s, digest %s\n", expectedFingerprint, sig.DockerManifestDigest) return nil } diff --git a/cmd/skopeo/signing_test.go b/cmd/skopeo/signing_test.go index cbd23de3..56732c8a 100644 --- a/cmd/skopeo/signing_test.go +++ b/cmd/skopeo/signing_test.go @@ -131,7 +131,20 @@ func TestStandaloneVerify(t *testing.T) { out, err = runSkopeo("standalone-verify", manifestPath, dockerReference, fixturesTestKeyFingerprint, signaturePath) assert.NoError(t, err) - assert.Equal(t, "Signature verified, digest "+fixturesTestImageManifestDigest.String()+"\n", out) + assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) + + // Using any known fingerprint + out, err = runSkopeo("standalone-verify", manifestPath, + dockerReference, "any", signaturePath) + assert.NoError(t, err) + assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) + + // Using a trust store + t.Setenv("GNUPGHOME", "") + out, err = runSkopeo("standalone-verify", "--truststore", "fixtures/pubring.gpg", manifestPath, + dockerReference, "any", signaturePath) + assert.NoError(t, err) + assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) } func TestUntrustedSignatureDump(t *testing.T) { diff --git a/docs/skopeo-standalone-verify.1.md b/docs/skopeo-standalone-verify.1.md index d7ed7b8d..72ca4c10 100644 --- a/docs/skopeo-standalone-verify.1.md +++ b/docs/skopeo-standalone-verify.1.md @@ -16,7 +16,7 @@ as per containers-policy.json(5). _docker-reference_ A docker reference expected to identify the image in the signature - _key-fingerprint_ Expected identity of the signing key + _key-fingerprint_ Expected identity of the signing key, or "any" to trust any known key _signature_ Path to signature file @@ -28,6 +28,10 @@ as per containers-policy.json(5). Print usage statement +**--truststore** _truststore_ + +Trust store of public keys to use when verifying signatures. If this is not specified, keys from gpg home are used. + ## EXAMPLES ```console diff --git a/integration/signing_test.go b/integration/signing_test.go index 99742d90..4eff6afe 100644 --- a/integration/signing_test.go +++ b/integration/signing_test.go @@ -73,7 +73,7 @@ func (s *signingSuite) TestSignVerifySmoke() { assertSkopeoSucceeds(t, "^$", "standalone-sign", "-o", sigOutput.Name(), manifestPath, dockerReference, s.fingerprint) - expected := fmt.Sprintf("^Signature verified, digest %s\n$", TestImageManifestDigest) + expected := fmt.Sprintf("^Signature verified using fingerprint %s, digest %s\n$", s.fingerprint, TestImageManifestDigest) assertSkopeoSucceeds(t, expected, "standalone-verify", manifestPath, dockerReference, s.fingerprint, sigOutput.Name()) } diff --git a/systemtest/050-signing.bats b/systemtest/050-signing.bats index 5365d79f..cf191edb 100644 --- a/systemtest/050-signing.bats +++ b/systemtest/050-signing.bats @@ -242,7 +242,7 @@ END_TESTS $fingerprint \ $TESTDIR/busybox.signature # manifest digest - digest=$(echo "$output" | awk '{print $4;}') + digest=$(echo "$output" | awk '{print $NF;}') run_skopeo manifest-digest $TESTDIR/busybox/manifest.json expect_output $digest } From 9b1f1fa1a917f9299044ad7333219da28b423c3a Mon Sep 17 00:00:00 2001 From: James Hewitt Date: Sat, 25 Mar 2023 14:27:41 +0000 Subject: [PATCH 2/4] Rename argument. Only use any with public key file. Double check fingerprint is in public key file. Signed-off-by: James Hewitt --- cmd/skopeo/signing.go | 27 +++++++++++++++++++-------- cmd/skopeo/signing_test.go | 11 ++++++----- docs/skopeo-standalone-verify.1.md | 6 +++--- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/cmd/skopeo/signing.go b/cmd/skopeo/signing.go index 07a3ab52..830ef05d 100644 --- a/cmd/skopeo/signing.go +++ b/cmd/skopeo/signing.go @@ -67,7 +67,7 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error { } type standaloneVerifyOptions struct { - truststore string + publickeyfile string } func standaloneVerifyCmd() *cobra.Command { @@ -77,11 +77,11 @@ func standaloneVerifyCmd() *cobra.Command { Short: "Verify a signature using local files", Long: `Verify a signature using local files -KEY-FINGERPRINT can be an exact fingerprint, or "any" if you trust all the keys in the trust store.`, +KEY-FINGERPRINT can be an exact fingerprint, or "any" if you trust all the keys in the public key file.`, RunE: commandAction(opts.run), } flags := cmd.Flags() - flags.StringVar(&opts.truststore, "truststore", "", "Trust store of public keys. Defaults to using local gpg keys.") + flags.StringVar(&opts.publickeyfile, "public-key-file", "", `File containing public keys. If not specified, will use local GPG keys. When using a public key file, you can specify "any" as the fingerprint to trust any key in the public key file.`) adjustUsage(cmd) return cmd } @@ -105,12 +105,13 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error } var mech signature.SigningMechanism - if opts.truststore != "" { - truststore, err := os.ReadFile(opts.truststore) + var fingerprints []string + if opts.publickeyfile != "" { + publicKeys, err := os.ReadFile(opts.publickeyfile) if err != nil { - return fmt.Errorf("Error reading trust store from %s: %v", opts.truststore, err) + return fmt.Errorf("Error reading public keys from %s: %v", opts.publickeyfile, err) } - mech, _, err = signature.NewEphemeralGPGSigningMechanism(truststore) + mech, fingerprints, err = signature.NewEphemeralGPGSigningMechanism(publicKeys) } else { mech, err = signature.NewGPGSigningMechanism() } @@ -119,11 +120,21 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error } defer mech.Close() - if expectedFingerprint == "any" { + if opts.publickeyfile != "" && expectedFingerprint == "any" { _, expectedFingerprint, err = mech.Verify(unverifiedSignature) if err != nil { return fmt.Errorf("Could not determine fingerprint from signature: %v", err) } + found := false + for _, fingerprint := range fingerprints { + if expectedFingerprint == fingerprint { + found = true + } + } + if !found { + // This is theoretically impossible because mech.Verify only works if it can identify the key based on the signature + return fmt.Errorf("Signature fingerprint not found in public key file: %s", expectedFingerprint) + } } sig, err := signature.VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprint) diff --git a/cmd/skopeo/signing_test.go b/cmd/skopeo/signing_test.go index 56732c8a..c0b0876c 100644 --- a/cmd/skopeo/signing_test.go +++ b/cmd/skopeo/signing_test.go @@ -133,15 +133,16 @@ func TestStandaloneVerify(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) - // Using any known fingerprint - out, err = runSkopeo("standalone-verify", manifestPath, - dockerReference, "any", signaturePath) + // Using a public key file + t.Setenv("GNUPGHOME", "") + out, err = runSkopeo("standalone-verify", "--public-key-file", "fixtures/pubring.gpg", manifestPath, + dockerReference, fixturesTestKeyFingerprint, signaturePath) assert.NoError(t, err) assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) - // Using a trust store + // Using a public key file matching any public key t.Setenv("GNUPGHOME", "") - out, err = runSkopeo("standalone-verify", "--truststore", "fixtures/pubring.gpg", manifestPath, + out, err = runSkopeo("standalone-verify", "--public-key-file", "fixtures/pubring.gpg", manifestPath, dockerReference, "any", signaturePath) assert.NoError(t, err) assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) diff --git a/docs/skopeo-standalone-verify.1.md b/docs/skopeo-standalone-verify.1.md index 72ca4c10..aad71c98 100644 --- a/docs/skopeo-standalone-verify.1.md +++ b/docs/skopeo-standalone-verify.1.md @@ -16,7 +16,7 @@ as per containers-policy.json(5). _docker-reference_ A docker reference expected to identify the image in the signature - _key-fingerprint_ Expected identity of the signing key, or "any" to trust any known key + _key-fingerprint_ Expected identity of the signing key, or "any" to trust any known key when using a public key file _signature_ Path to signature file @@ -28,9 +28,9 @@ as per containers-policy.json(5). Print usage statement -**--truststore** _truststore_ +**--public-key-file** _public key file_ -Trust store of public keys to use when verifying signatures. If this is not specified, keys from gpg home are used. +File containing the public keys to use when verifying signatures. If this is not specified, keys from the GPG homedir are used. ## EXAMPLES From c54f2025d882330eceaa0d3beb0d6dda7a98861b Mon Sep 17 00:00:00 2001 From: James Hewitt Date: Thu, 30 Mar 2023 09:39:30 +0100 Subject: [PATCH 3/4] Review comments (to be squashed later Signed-off-by: James Hewitt --- cmd/skopeo/signing.go | 44 ++++++++++++++++++-------------------- cmd/skopeo/signing_test.go | 5 +++++ 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/cmd/skopeo/signing.go b/cmd/skopeo/signing.go index 830ef05d..c1bbdc74 100644 --- a/cmd/skopeo/signing.go +++ b/cmd/skopeo/signing.go @@ -10,6 +10,7 @@ import ( "github.com/containers/image/v5/pkg/cli" "github.com/containers/image/v5/signature" "github.com/spf13/cobra" + "golang.org/x/exp/slices" ) type standaloneSignOptions struct { @@ -41,12 +42,12 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error { manifest, err := os.ReadFile(manifestPath) if err != nil { - return fmt.Errorf("Error reading %s: %v", manifestPath, err) + return fmt.Errorf("Error reading %s: %w", manifestPath, err) } mech, err := signature.NewGPGSigningMechanism() if err != nil { - return fmt.Errorf("Error initializing GPG: %v", err) + return fmt.Errorf("Error initializing GPG: %w", err) } defer mech.Close() @@ -57,17 +58,17 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error { signature, err := signature.SignDockerManifestWithOptions(manifest, dockerReference, mech, fingerprint, &signature.SignOptions{Passphrase: passphrase}) if err != nil { - return fmt.Errorf("Error creating signature: %v", err) + return fmt.Errorf("Error creating signature: %w", err) } if err := os.WriteFile(opts.output, signature, 0644); err != nil { - return fmt.Errorf("Error writing signature to %s: %v", opts.output, err) + return fmt.Errorf("Error writing signature to %s: %w", opts.output, err) } return nil } type standaloneVerifyOptions struct { - publickeyfile string + publicKeyFile string } func standaloneVerifyCmd() *cobra.Command { @@ -81,7 +82,7 @@ KEY-FINGERPRINT can be an exact fingerprint, or "any" if you trust all the keys RunE: commandAction(opts.run), } flags := cmd.Flags() - flags.StringVar(&opts.publickeyfile, "public-key-file", "", `File containing public keys. If not specified, will use local GPG keys. When using a public key file, you can specify "any" as the fingerprint to trust any key in the public key file.`) + flags.StringVar(&opts.publicKeyFile, "public-key-file", "", `File containing public keys. If not specified, will use local GPG keys.`) adjustUsage(cmd) return cmd } @@ -95,43 +96,40 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error expectedFingerprint := args[2] signaturePath := args[3] + if opts.publicKeyFile == "" && expectedFingerprint == "any" { + return fmt.Errorf("Cannot use any fingerprint without a public key file") + } unverifiedManifest, err := os.ReadFile(manifestPath) if err != nil { - return fmt.Errorf("Error reading manifest from %s: %v", manifestPath, err) + return fmt.Errorf("Error reading manifest from %s: %w", manifestPath, err) } unverifiedSignature, err := os.ReadFile(signaturePath) if err != nil { - return fmt.Errorf("Error reading signature from %s: %v", signaturePath, err) + return fmt.Errorf("Error reading signature from %s: %w", signaturePath, err) } var mech signature.SigningMechanism var fingerprints []string - if opts.publickeyfile != "" { - publicKeys, err := os.ReadFile(opts.publickeyfile) + if opts.publicKeyFile != "" { + publicKeys, err := os.ReadFile(opts.publicKeyFile) if err != nil { - return fmt.Errorf("Error reading public keys from %s: %v", opts.publickeyfile, err) + return fmt.Errorf("Error reading public keys from %s: %w", opts.publicKeyFile, err) } mech, fingerprints, err = signature.NewEphemeralGPGSigningMechanism(publicKeys) } else { mech, err = signature.NewGPGSigningMechanism() } if err != nil { - return fmt.Errorf("Error initializing GPG: %v", err) + return fmt.Errorf("Error initializing GPG: %w", err) } defer mech.Close() - if opts.publickeyfile != "" && expectedFingerprint == "any" { + if opts.publicKeyFile != "" && expectedFingerprint == "any" { _, expectedFingerprint, err = mech.Verify(unverifiedSignature) if err != nil { - return fmt.Errorf("Could not determine fingerprint from signature: %v", err) + return fmt.Errorf("Could not determine fingerprint from signature: %w", err) } - found := false - for _, fingerprint := range fingerprints { - if expectedFingerprint == fingerprint { - found = true - } - } - if !found { + if !slices.Contains(fingerprints, expectedFingerprint) { // This is theoretically impossible because mech.Verify only works if it can identify the key based on the signature return fmt.Errorf("Signature fingerprint not found in public key file: %s", expectedFingerprint) } @@ -139,7 +137,7 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error sig, err := signature.VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprint) if err != nil { - return fmt.Errorf("Error verifying signature: %v", err) + return fmt.Errorf("Error verifying signature: %w", err) } fmt.Fprintf(stdout, "Signature verified using fingerprint %s, digest %s\n", expectedFingerprint, sig.DockerManifestDigest) @@ -175,7 +173,7 @@ func (opts *untrustedSignatureDumpOptions) run(args []string, stdout io.Writer) untrustedSignature, err := os.ReadFile(untrustedSignaturePath) if err != nil { - return fmt.Errorf("Error reading untrusted signature from %s: %v", untrustedSignaturePath, err) + return fmt.Errorf("Error reading untrusted signature from %s: %w", untrustedSignaturePath, err) } untrustedInfo, err := signature.GetUntrustedSignatureInformationWithoutVerifying(untrustedSignature) diff --git a/cmd/skopeo/signing_test.go b/cmd/skopeo/signing_test.go index c0b0876c..c3681cf5 100644 --- a/cmd/skopeo/signing_test.go +++ b/cmd/skopeo/signing_test.go @@ -127,6 +127,11 @@ func TestStandaloneVerify(t *testing.T) { dockerReference, fixturesTestKeyFingerprint, "fixtures/corrupt.signature") assertTestFailed(t, out, err, "Error verifying signature") + // Error using any without a public key file + out, err = runSkopeo("standalone-verify", manifestPath, + dockerReference, "any", signaturePath) + assertTestFailed(t, out, err, "Cannot use any fingerprint without a public key file") + // Success out, err = runSkopeo("standalone-verify", manifestPath, dockerReference, fixturesTestKeyFingerprint, signaturePath) From 4ca2058d01c0ebeaa3c1493440b61b974cd8d8d6 Mon Sep 17 00:00:00 2001 From: James Hewitt Date: Sat, 1 Apr 2023 11:49:27 +0100 Subject: [PATCH 4/4] Use multiple fingerprint function Allow comma separated fingerprint list To be squashed later Signed-off-by: James Hewitt --- cmd/skopeo/signing.go | 29 +++++++++++------------------ cmd/skopeo/signing_test.go | 12 +++++++++--- docs/skopeo-standalone-verify.1.md | 4 ++-- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/cmd/skopeo/signing.go b/cmd/skopeo/signing.go index c1bbdc74..9ea9202f 100644 --- a/cmd/skopeo/signing.go +++ b/cmd/skopeo/signing.go @@ -6,11 +6,11 @@ import ( "fmt" "io" "os" + "strings" "github.com/containers/image/v5/pkg/cli" "github.com/containers/image/v5/signature" "github.com/spf13/cobra" - "golang.org/x/exp/slices" ) type standaloneSignOptions struct { @@ -74,11 +74,11 @@ type standaloneVerifyOptions struct { func standaloneVerifyCmd() *cobra.Command { opts := standaloneVerifyOptions{} cmd := &cobra.Command{ - Use: "standalone-verify MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT SIGNATURE", + Use: "standalone-verify MANIFEST DOCKER-REFERENCE KEY-FINGERPRINTS SIGNATURE", Short: "Verify a signature using local files", Long: `Verify a signature using local files -KEY-FINGERPRINT can be an exact fingerprint, or "any" if you trust all the keys in the public key file.`, +KEY-FINGERPRINTS can be a comma separated list of fingerprints, or "any" if you trust all the keys in the public key file.`, RunE: commandAction(opts.run), } flags := cmd.Flags() @@ -93,10 +93,10 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error } manifestPath := args[0] expectedDockerReference := args[1] - expectedFingerprint := args[2] + expectedFingerprints := strings.Split(args[2], ",") signaturePath := args[3] - if opts.publicKeyFile == "" && expectedFingerprint == "any" { + if opts.publicKeyFile == "" && len(expectedFingerprints) == 1 && expectedFingerprints[0] == "any" { return fmt.Errorf("Cannot use any fingerprint without a public key file") } unverifiedManifest, err := os.ReadFile(manifestPath) @@ -109,13 +109,13 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error } var mech signature.SigningMechanism - var fingerprints []string + var publicKeyfingerprints []string if opts.publicKeyFile != "" { publicKeys, err := os.ReadFile(opts.publicKeyFile) if err != nil { return fmt.Errorf("Error reading public keys from %s: %w", opts.publicKeyFile, err) } - mech, fingerprints, err = signature.NewEphemeralGPGSigningMechanism(publicKeys) + mech, publicKeyfingerprints, err = signature.NewEphemeralGPGSigningMechanism(publicKeys) } else { mech, err = signature.NewGPGSigningMechanism() } @@ -124,23 +124,16 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error } defer mech.Close() - if opts.publicKeyFile != "" && expectedFingerprint == "any" { - _, expectedFingerprint, err = mech.Verify(unverifiedSignature) - if err != nil { - return fmt.Errorf("Could not determine fingerprint from signature: %w", err) - } - if !slices.Contains(fingerprints, expectedFingerprint) { - // This is theoretically impossible because mech.Verify only works if it can identify the key based on the signature - return fmt.Errorf("Signature fingerprint not found in public key file: %s", expectedFingerprint) - } + if len(expectedFingerprints) == 1 && expectedFingerprints[0] == "any" { + expectedFingerprints = publicKeyfingerprints } - sig, err := signature.VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprint) + sig, verificationFingerprint, err := signature.VerifyImageManifestSignatureUsingKeyIdentityList(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprints) if err != nil { return fmt.Errorf("Error verifying signature: %w", err) } - fmt.Fprintf(stdout, "Signature verified using fingerprint %s, digest %s\n", expectedFingerprint, sig.DockerManifestDigest) + fmt.Fprintf(stdout, "Signature verified using fingerprint %s, digest %s\n", verificationFingerprint, sig.DockerManifestDigest) return nil } diff --git a/cmd/skopeo/signing_test.go b/cmd/skopeo/signing_test.go index c3681cf5..ba16b82e 100644 --- a/cmd/skopeo/signing_test.go +++ b/cmd/skopeo/signing_test.go @@ -136,21 +136,27 @@ func TestStandaloneVerify(t *testing.T) { out, err = runSkopeo("standalone-verify", manifestPath, dockerReference, fixturesTestKeyFingerprint, signaturePath) assert.NoError(t, err) - assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) + assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out) + + // Using multiple fingerprints + out, err = runSkopeo("standalone-verify", manifestPath, + dockerReference, "0123456789ABCDEF0123456789ABCDEF01234567,"+fixturesTestKeyFingerprint+",DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", signaturePath) + assert.NoError(t, err) + assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out) // Using a public key file t.Setenv("GNUPGHOME", "") out, err = runSkopeo("standalone-verify", "--public-key-file", "fixtures/pubring.gpg", manifestPath, dockerReference, fixturesTestKeyFingerprint, signaturePath) assert.NoError(t, err) - assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) + assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out) // Using a public key file matching any public key t.Setenv("GNUPGHOME", "") out, err = runSkopeo("standalone-verify", "--public-key-file", "fixtures/pubring.gpg", manifestPath, dockerReference, "any", signaturePath) assert.NoError(t, err) - assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out) + assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out) } func TestUntrustedSignatureDump(t *testing.T) { diff --git a/docs/skopeo-standalone-verify.1.md b/docs/skopeo-standalone-verify.1.md index aad71c98..d9c63a0d 100644 --- a/docs/skopeo-standalone-verify.1.md +++ b/docs/skopeo-standalone-verify.1.md @@ -4,7 +4,7 @@ skopeo\-standalone\-verify - Verify an image signature. ## SYNOPSIS -**skopeo standalone-verify** _manifest_ _docker-reference_ _key-fingerprint_ _signature_ +**skopeo standalone-verify** _manifest_ _docker-reference_ _key-fingerprints_ _signature_ ## DESCRIPTION @@ -16,7 +16,7 @@ as per containers-policy.json(5). _docker-reference_ A docker reference expected to identify the image in the signature - _key-fingerprint_ Expected identity of the signing key, or "any" to trust any known key when using a public key file + _key-fingerprints_ Identities of trusted signing keys (comma separated), or "any" to trust any known key when using a public key file _signature_ Path to signature file