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