# Cosign Signature Specifications

This document aims to describe how `cosign` signs containers.
The goal is to specify the behavior well enough to promote other implementations and enable interoperability.
Container signatures generated with `cosign` should be verifiable in other tools, and vice-versa.

This document is broken up into a few parts:
* [Properties](#properties) details the individual components that are used to create and verify signatures.
* [Storage](#storage) details how signatures are stored and discovered in an OCI registry.
* [Payload](#payload) details the format of to-be-signed payloads.
* [Signature](#signature) details the signature schemes supported.

## Properties

This section describes the REQUIRED and OPTIONAL properties used to sign and verify an image.
Their layout in an OCI object is described below.

* `payload` bytes

  This REQUIRED property contains the contents of signed data in byte-form.
  Because signatures are __detached__, the payload MUST contain the digest of the image it references, in a well-known location.
  This location is dependent on the [payload format](#payloads).

* `mediaType` string

  This REQUIRED property contains the media type of the payload.

* `signature` string

  This REQUIRED property contains the base64-encoded signature.
  This signature MUST be generated by a supported scheme.
  For more details on supported schemes, see the [`Signature`](#signature) section below.

  Example `signature`:
    `MEYCIQDXmXWj59naoPFlLnCADIPLKgLG3LyFtKrbjpnkYiGNGgIhAJ/eNx5zr/l1MJKSFpFMjPKKr4fjh5RHEtT2DhMamZuT`

* `certificate` string

  This OPTIONAL property contains a [PEM-encoded](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) x509 certificate.
  If present, this certificate MUST embed the public key that can be used to verify the signature.

  Example `certificate`:

```
-----BEGIN CERTIFICATE-----

Ej6T/GLK6XJSB28haSPRWB7k
-----END CERTIFICATE-----
```

* `chain` string
  This OPTIONAL property contains a PEM-encoded, DER-formatted, ASN.1 x509 certificate chain.
  The `certificate` property MUST be present if this property is present.
  This chain MAY be used by implementations to verify the `certificate` property.
  Clients MUST validate that any certificates in the chain that are self-signed
  or are expected to be trust anchors with an out-of-band mechanism.

  Example `chain`:

```
----BEGIN CERTIFICATE-----

Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----

92pXqODWYqpW8+IRED5e42Ncu9XtDgS5Pw==
-----END CERTIFICATE-----
```

* `bundle` string

  This OPTIONAL property contains a JSON formatted `bundle` type, which can be used for offline verification.
  Example `bundle`:

```json
{
  "SignedEntryTimestamp": "MEUCIQDHiGUesxPpn+qRONLmKlNIVPhl9gBMnwNeIQmRkRmZVQIgRxPpuYQDZR/8lYKcEfiQn5b+7VDoJIC72ZWHO9ZCp1A=",
  "Payload": {
    "body": " ",
    "integratedTime": 1624396085,
    "logIndex": 5179,
    "logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"
  }
}
```

  The following are REQUIRED properties of the bundle:

  - The `SignedEntryTimestamp` is a rekor-signed signature over the logIndex, body and integratedTime of the Rekor Log Entry
  - The `Payload` consists of all fields required to verify the SET:
    - The `body` is the body of the Rekor Log Entry
    - The `integratedTime` is the UNIX timestamp the log entry was integrated into the transparency log
    - The `logIndex` is the index of the log entry in the transparency log
    - The `logID` is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log

* `rfc3161timestamp` string

  This OPTIONAL property contains a JSON formatted `RFC3161Timestamp` containing the timestamp response from a
  timestamp authority.

### Verification

See the [client specification on verification](https://github.com/sigstore/architecture-docs/blob/main/client-spec.md#4-verification).

## Storage

`cosign` image signatures are stored in an OCI registry and are designed to make use of the existing specifications.
The full specifications for the OCI formats and specifications used are available [here](https://github.com/opencontainers).

### Discovery

Signature object are placed in specific location in an OCI registry to enable consistent, interoperable discovery.

Multiple discovery mechanisms MAY be used.
Implementations MUST support at least the following mechanisms:

* Tag-based Discovery

#### Tag-based Discovery

In this scheme, signatures are stored in an OCI registry in a predictable location, addressable by tag.
The location of signatures corresponding to a specific object can be computed using the digest of the object.

If the object is referenced by tag, the tag must first be resolved to a digest.
Then the digest of the object (of the form `sha256:abcdef...`) is encoded into a tag name using the following rules:

* Replace the `:` character with a `-`
* Append the `.sig` suffix

Example digest->tag mapping:

1. Start with `gcr.io/dlorenc-vmtest2/demo:latest`
2. Resolve this to a digest: `sha256:97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36`
3. Follow the encoding rules: `sha256-97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36.sig`
4. Signature can be found at `gcr.io/dlorenc-vmtest2/demo:sha256-97fc222cee7991b5b061d4d4afdb5f3428fcb0c9054e1690313786befa1e4e36.sig`

Implementations MAY store signatures objects in the same OCI repository as the target image or a different one.

### Object Types

This section describes the way the properties from above are embedded into OCI objects that can be stored in a registry.
Implementations MUST support storing signatures in at least the following object types:

* [OCI Image Manifest V1](#oci-image-manifest-v1)

#### OCI Image Manifest V1

This section describes the way the mandatory and optional signature properties are embedded into an
[OCI Image Manifest V1](https://github.com/opencontainers/image-spec/blob/master/manifest.md) object.

Only one image manifest is created for every signed object.
Multiple signatures can be embedded in one image manifest.

##### Payload and mediaType

The `payload` bytes are uploaded to an OCI registry as a `blob`, and are referenced by `digest`, `size` and `mediaType.`
The digest is embedded into the `Image` manifest as a `layer`, via a [`Descriptor`](https://github.com/opencontainers/image-spec/blob/master/descriptor.md).

The `mediaType` property for the `payload` is included in the same descriptor.

Example `payload`:

```json
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    <omitted for brevity>
  },
  "layers": [
    {
      "mediaType": "application/vnd.dev.cosign.simplesigning.v1+json",
      "size": 210,
      "digest": "sha256:1119abab63e605dcc281019bad0424744178b6f61ba57378701fe7391994c999",
    },
    <other layers here>
  ]
}
```

##### Signature

The `signature` is base64-encoded and stored as an `annotation` on the layer, in the same descriptor.
The `annotation` key is `dev.cosignproject.cosign/signature`.

Example `signature`:

```json
"annotations": {
    "dev.cosignproject.cosign/signature": "MEUCIBKI9FIC+YD3m/lWViyPxsJsbnIHj86sSbb7L3qvpEFoAiEA2ZChO/67CuAPQKJLBVsAc7bs9hBK8RpsdfjBsByGKJM="
}
```

##### Certificate

The `certificate` is stored as an `annotation` on the layer, in the same descriptor.
The `annotation` key is `dev.cosignproject.cosign/certificate`.

Example `certificate`:

```json
"annotations": {
     "dev.sigstore.cosign/certificate": "-----BEGIN CERTIFICATE-----\nMIICrjCCAjSgAwIBAgIUAM4mURWUSkg06fmHmFfTmerYKaUwCgYIKoZIzj0EAwMw\nKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y\nMTA0MDExNTU5MDZaFw0yMTA0MDExNjE4NTlaMDoxGzAZBgNVBAoMEmRsb3JlbmNA\nZ29vZ2xlLmNvbTEbMBkGA1UEAwwSZGxvcmVuY0Bnb29nbGUuY29tMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAE3R0ZtpfBd3Y8DaXuB1gM8JPlhsDIEfXO/WsMJEN1\n4hEn8wajX2HklqL7igZPFICv6tBUGylIHp2mTH2Nhv38mqOCASYwggEiMA4GA1Ud\nDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAMBgNVHRMBAf8EAjAAMB0G\nA1UdDgQWBBTy3UWIop0bNrdNgSrVHHD10qSASTAfBgNVHSMEGDAWgBTIxR0AQZok\nKTJRJOsNrkrtSgbT7DCBjQYIKwYBBQUHAQEEgYAwfjB8BggrBgEFBQcwAoZwaHR0\ncDovL3ByaXZhdGVjYS1jb250ZW50LTYwM2ZlN2U3LTAwMDAtMjIyNy1iZjc1LWY0\nZjVlODBkMjk1NC5zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2NhMzZhMWU5NjI0MmI5\nZmNiMTQ2L2NhLmNydDAdBgNVHREEFjAUgRJkbG9yZW5jQGdvb2dsZS5jb20wCgYI\nKoZIzj0EAwMDaAAwZQIwC15Gtd9F6W9lmJuoXMym9DfWlBpK5HEPak38WPXqowRp\n6p+2/3jSLkFT5Nn5fuISAjEAouVlX4zH2rlkfg45HnDJax7o6ZV+E0/6BdAms44D\nEj6T/GLK6XJSB28haSPRWB7k\n-----END CERTIFICATE-----\n",
}
```

##### Chain

The `chain` is stored as an `annotation` on the layer, in the same descriptor.
The `annotation` key is `dev.cosignproject.cosign/chain`.

Example `chain`:

```json
"annotations": {
      "dev.sigstore.cosign/chain": "-----BEGIN CERTIFICATE-----\nMIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq\nMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx\nMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu\nZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSy\nA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0Jcas\ntaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6Nm\nMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE\nFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2u\nSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJx\nVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup\nHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==\n-----END CERTIFICATE-----"
}
```

## Payloads

Implementations MUST support at least the following payload types:

  * Simple Signing

### Simple Signing

The Simple Signing payload format is [specified here](https://github.com/containers/image/blob/a5061e5a5f00333ea3a92e7103effd11c6e2f51d/docs/containers-signature.5.md#json-data-format)

The following additional semantics are applied:

* The `mediaType` used to identify this payload format is: `application/vnd.dev.cosign.simplesigning.v1+json`.
* The `critical.type` value used to identify `cosign` signatures is: `cosign container image signature`.
* The `critical.identity.docker-reference` field is ignored.
* Optional user-specified claims may be included in the `Optional` section.

For example:
```json
{
    "critical": {
           "identity": {
               "docker-reference": "testing/manifest"
           },
           "image": {
               "Docker-manifest-digest": "sha256:20be...fe55"
           },
           "type": "cosign container image signature"
    },
    "optional": {
           "creator": "atomic",
           "timestamp": 1458239713
    }
}
```

## Signature Schemes

Implementations must support at least the following schemes:

* ECDSA-P256

No information about the signature scheme is included in the object.
Clients must determine the signature scheme out-of-band during the verification process.

### Hashing Algorithms

Signers and verifiers must know the hash algorithm used in addition to the signature scheme.
In an attempt to avoid specifying a particular hashing algorithm, we require that digest be calculated using the SAME algorithm as the OCI registry.
In practice, this means [sha256](https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests).

When the payload is stored as a `blob` in the OCI registry, it is exposed and referenced via a [Content Addressable](https://en.wikipedia.org/wiki/Content-addressable_storage) API.

Example referenced payload:

```json
{
  "mediaType": "application/vnd.dev.cosign.simplesigning.v1+json",
  "size": 210,
  "digest": "sha256:1119abab63e605dcc281019bad0424744178b6f61ba57378701fe7391994c999",
}
```

Here, `1119abab63e605dcc281019bad0424744178b6f61ba57378701fe7391994c999` is the hex-encoded digest, and the `sha256:` prefix specifies the algorithm.
This value is already calculated and verified by both the registry and client-tooling.

This means that our signatures is "linked" to a "container image" happens via two hops:

`Sign(sha256(SimpleSigningPayload(sha256(Image Manifest))))`

Allowing flexibility in hashing algorithms of the digital signature would only allow manipulation of the "outer" one - the image manifest
itself is always referenced by `sha256` today.
This means using a different hashing algorithm as part of the final signature, even one perceived as "stronger", would result in limited cryptographic benefits.

Put simply: implementations MUST use the same hash algorithm used by the underlying registry to reference the payload.
Any future algorithmic-agility will come from the storage layer as part of the OCI specification.

# Rationales and Commentary

This document, while labeled a `Specification`, aims to specify as few things as possible.
Instead, we prescribe the usage of other specifications.

This section contains a rationale for each choice, as well as comparisons to alternatives considered.

## Payload/Attestation Format: Simple Signing

We chose Simple Signing because it has the most existing usage and meets our requirements:

* Critical/Optional sections
* Extensible with more attestations
* Cross-language serialization support

### Alternatives

* OCI Descriptor.

  This has the fields we need, with a few problems.
  * The annotations section is limiting with map[string]string.
  * The `URLs` field is not meant for this use-case.
  * No real benefit, since it's stored as a `blob` (not parsed by the registry).

* Plain digest

  This doesn't have any attestation/annotation support.

* Something new

  See above, we've tried to avoid making any new types where possible.

## OCI Type - Docker Manifest/OCI Manifest

We're currently using Docker but will switch to OCI.
The format support across registries is a toss-up, but likely to improve for OCI.
OCI supports custom media-types and has other "correctness" benefits.

## Discovery - Tag Based

We use the tag based mechanism because it's the only option we can think of.
It meets the two hard requirements: works **everywhere** today and requires no extra services.
It also does not mutate the signed object (like an attached signature or index might).

Support for multiple signatures works but is racy.

### Alternatives Considered

Notary v1/Grafeas both require another database.
A few other proprietary APIs exist, but they're not cross-registry.

## Hash Algorithm - None!

Most common signature schemes support customizable hash algorithms.
These are typically stored with the signature for convenience, presenting a possible attack/confusion vector.

We decided to pin to the registry hash algorithm.
This removes an entire moving part without sacrificing agility.

The registry/client-tooling already perform hash validation as part of the CAS.
While their spec does not completely pin to specific algorithm, SHA256 is ubiquitous in practice.
This means that our signed payload object references the actual image "target" by a sha-256 digest - the entire signature already relies on the strength of this algorithm.

Trading off this perceived agility for the reduction in attack surface is a win.

** Note **: This is only possible if we store the `payload` in a blob by itself.
Serializing the payload and signature together in something like a JWT for storage would mean the payload is no longer directly hashed into the CAS.
There is also a performance benefit: we can validate the signature (stored in the manifest) against the payload (stored as a blob) without fetching the blob, because the blob's digest is also present in the manifest.

## Algorithms

We only require ECDSA-P256 (with the SHA256 hash algorithm, see above), but will allow for other schemes.
We will bias toward algorithms well-supported in the Go ecosystem, but will accept others.
We will bias toward support for algorithms with wide industry adoption, API support, and hardware availability.

## Compatibility

We are compatible with the In-Toto [Metablock](https://in-toto.readthedocs.io/en/latest/model.html) and JWS [rfc7515](https://tools.ietf.org/html/rfc7515) formats.
This means we can convert to these formats during verification, and from these formats during upload/signature.
You can think of this spec as an "on-registry serialization format" for either of these specified formats.
