Further Usage

Signing Git Commits - Three Ways!

You thought Git signatures were always GPG?

Think again!

Easy Mode

Sign the commits and store the signatures and public keys somewhere else.

$ ./cosign sign-blob --key cosign.key <(git rev-parse HEAD)
Using payload from: /dev/fd/63
Enter password for private key:
MEUCIQDLtTbCRCW+o7Gt3WKR4b2UqT947L8JtYzQJk+R8PItxgIgXoYQg1YXw8xDmGWun6wIG2t+/J0HJs9SbscnSLMNWsM=
$ git rev-parse HEAD
455d1988360dcfdcf0fa17b0736fbbc33b4924c0
$ ./cosign verify-blob --key cosign.pub --signature MEUCIQDLtTbCRCW+o7Gt3WKR4b2UqT947L8JtYzQJk+R8PItxgIgXoYQg1YXw8xDmGWun6wIG2t+/J0HJs9SbscnSLMNWsM= <(git rev-parse HEAD)
Verified OK

Medium Mode

Store the signature in the repo as notes, store the public key somewhere else.

$ ./cosign sign-blob --key cosign.key <(git rev-parse HEAD)
Using payload from: /dev/fd/63
Enter password for private key:
MEQCIHXN31pDrZBxs+m/HrcFruavv++oMc+pBZKgl7Hps9jjAiA9QE5uzpFNC5SGpdr4TJuCwh47C24Hwt4yHICae0J1bw==
$ git notes add -m "MEQCIHXN31pDrZBxs+m/HrcFruavv++oMc+pBZKgl7Hps9jjAiA9QE5uzpFNC5SGpdr4TJuCwh47C24Hwt4yHICae0J1bw==" HEAD
$ ./cosign verify-blob --key cosign.pub --signature <(git notes show HEAD) <(git rev-parse HEAD)
Verified OK

Hard Mode

Store the signature in the Transparency Log, and store the public key somewhere else.

$ COSIGN_EXPERIMENTAL=1 ./cosign sign-blob --key cosign.key <(git rev-parse HEAD)
Using payload from: /dev/fd/63
Enter password for private key:
MEYCIQDWX6RjU0Z2ynd1CdiAwo/JaC2Z5+vdx8H5spuDNu/r5wIhAPnP+87+knFEwbE8FgeXCrgkjWal3aBsNR3IVaBDT2XU
tlog entry created with index: 1224

Now find it from the log:

$ uuid=$(rekor-cli search --artifact <(git rev-parse HEAD) | tail -n 1)
$ sig=$(rekor-cli get --uuid=$uuid --format=json | jq -r .Body.RekordObj.signature.content)
$ cosign verify-blob --key cosign.pub --signature <(echo $sig) <(git rev-parse HEAD)
Verified OK

Level 11

Store the signature in the Transparency Log and don't store the keys anywhere.

Other Types

cosign can sign anything in a registry. These examples show signing a single image, but you could also sign a multi-platform Index, or any other type of artifact. This includes Helm Charts, Tekton Pipelines, and anything else currently using OCI registries for distribution.

This also means new artifact types can be uploaded to a registry and signed. One interesting type to store and sign would be TUF repositories. I haven't tried yet, but I'm fairly certain TUF could be implemented on top of this.

Tag Signing

cosign signatures protect the digests of objects stored in a registry. The optional annotations support (via the -a flag to cosign sign) can be used to add extra data to the payload that is signed and protected by the signature. One use-case for this might be to sign a tag->digest mapping.

If you would like to attest that a specific tag (or set of tags) should point at a digest, you can run something like:

$ TAG=sign-me
$ DGST=$(crane digest dlorenc/demo:$TAG)
$ cosign sign --key cosign.key -a tag=$TAG dlorenc/demo@$DGST
Enter password for private key:
Pushing signature to: dlorenc/demo:sha256-97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36.sig

Then you can verify that the tag->digest mapping is also covered in the signature, using the -a flag to cosign verify. This example verifies that the digest $TAG points to (sha256:97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36) has been signed, and also that the $TAG:

$ cosign verify --key cosign.pub -a tag=$TAG dlorenc/demo:$TAG | jq .
{
  "Critical": {
    "Identity": {
      "docker-reference": ""
    },
    "Image": {
      "Docker-manifest-digest": "97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36"
    },
    "Type": "cosign container image signature"
  },
  "Optional": {
    "tag": "sign-me"
  }
}

Timestamps could also be added here, to implement TUF-style freeze-attack prevention.

Base Image/Layer Signing

Again, cosign can sign anything in a registry. You could use cosign to sign an image that is intended to be used as a base image, and inlcude that provenance metadata in resulting derived images. This could be used to enforce that an image was built from an authorized base image.

Rough Idea:

  • OCI manifests have an ordered list of layer Descriptors, which can contain annotations. See here for the specification.
  • A base image is an ordered list of layers to which other layers are appended, as well as an initial configuration object that is mutated.
    • A derived image is free to completely delete/destroy/recreate the config from its base image, so signing the config would provided limited value.
  • We can sign the full set of ordered base layers, and attach that signature as an annotation to the last layer in the resulting child image.

This example manifest represents an image that has been built from a base image with two layers. One additional layer is added, forming the final image.

{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 16724,
      "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
      "annotations": {
        "dev.cosign.signature.baseimage": "Ejy6ipGJjUzMDoQFePWixqPBYF0iSnIvpMWps3mlcYNSEcRRZelL7GzimKXaMjxfhy5bshNGvDT5QoUJ0tqUAg=="
      }
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 73109,
      "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
    }
  ],
}

Note that this could be applied recursively, for multiple intermediate base images.

Counter-Signing

Cosign signatures (and their protected payloads) are stored as artifacts in a registry. These signature objects can also be signed, resulting in a new, "counter-signature" artifact. This "counter-signature" protects the signature (or set of signatures) and the referenced artifact, which allows it to act as an attestation to the signature(s) themselves.

Before we sign the signature artifact, we first give it a memorable name so we can find it later.

$ cosign sign --key cosign.key -a sig=original dlorenc/demo
Enter password for private key:
Pushing signature to: dlorenc/demo:sha256-97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36.sig
$ cosign verify --key cosign.pub dlorenc/demo | jq .
{
  "Critical": {
    "Identity": {
      "docker-reference": ""
    },
    "Image": {
      "Docker-manifest-digest": "97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36"
    },
    "Type": "cosign container image signature"
  },
  "Optional": {
    "sig": "original"
  }
}

Now give that signature a memorable name, then sign that:

$ crane tag $(cosign triangulate dlorenc/demo) mysignature
2021/02/15 20:22:55 dlorenc/demo:mysignature: digest: sha256:71f70e5d29bde87f988740665257c35b1c6f52dafa20fab4ba16b3b1f4c6ba0e size: 556
$ cosign sign --key cosign.key -a sig=counter dlorenc/demo:mysignature
Enter password for private key:
Pushing signature to: dlorenc/demo:sha256-71f70e5d29bde87f988740665257c35b1c6f52dafa20fab4ba16b3b1f4c6ba0e.sig
$ cosign verify --key cosign.pub dlorenc/demo:mysignature
{"Critical":{"Identity":{"docker-reference":""},"Image":{"Docker-manifest-digest":"71f70e5d29bde87f988740665257c35b1c6f52dafa20fab4ba16b3b1f4c6ba0e"},"Type":"cosign container image signature"},"Optional":{"sig":"counter"}}

Finally, check the original signature:

$ crane manifest dlorenc/demo@sha256:71f70e5d29bde87f988740665257c35b1c6f52dafa20fab4ba16b3b1f4c6ba0e
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 233,
    "digest": "sha256:3b25a088710d03f39be26629d22eb68cd277a01673b9cb461c4c24fbf8c81c89"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.descriptor.v1+json",
      "size": 217,
      "digest": "sha256:0e79a356609f038089088ec46fd95f4649d04de989487220b1a0adbcc63fadae",
      "annotations": {
        "dev.sigstore.cosign/signature": "5uNZKEP9rm8zxAL0VVX7McMmyArzLqtxMTNPjPO2ns+5GJpBeXg+i9ILU+WjmGAKBCqiexTxzLC1/nkOzD4cDA=="
      }
    }
  ]
}
Edit this page on GitHub Updated at Sat, May 21, 2022