5c9f13
% container-signature 5 Container signature format
5c9f13
% Miloslav Trmač
5c9f13
% March 2017
5c9f13
5c9f13
# NAME
5c9f13
container-signature - Container signature format
5c9f13
5c9f13
# DESCRIPTION
5c9f13
This document describes the format of container signatures,
5c9f13
as implemented by the `github.com/containers/image/signature` package.
5c9f13
5c9f13
Most users should be able to consume these signatures by using the `github.com/containers/image/signature` package
5c9f13
(preferably through the higher-level `signature.PolicyContext` interface)
5c9f13
without having to care about the details of the format described below.
5c9f13
This documentation exists primarily for maintainers of the package
5c9f13
and to allow independent reimplementations.
5c9f13
5c9f13
## High-level overview
5c9f13
5c9f13
The signature provides an end-to-end authenticated claim that a container image
5c9f13
has been approved by a specific party (e.g. the creator of the image as their work,
5c9f13
an automated build system as a result of an automated build,
5c9f13
a company IT department approving the image for production) under a specified _identity_
5c9f13
(e.g. an OS base image / specific application, with a specific version).
5c9f13
5c9f13
A container signature consists of a cryptographic signature which identifies
5c9f13
and authenticates who signed the image, and carries as a signed payload a JSON document.
5c9f13
The JSON document identifies the image being signed, claims a specific identity of the
5c9f13
image and if applicable, contains other information about the image.
5c9f13
5c9f13
The signatures do not modify the container image (the layers, configuration, manifest, …);
5c9f13
e.g. their presence does not change the manifest digest used to identify the image in
5c9f13
docker/distribution servers; rather, the signatures are associated with an immutable image.
5c9f13
An image can have any number of signatures so signature distribution systems SHOULD support
5c9f13
associating more than one signature with an image.
5c9f13
5c9f13
## The cryptographic signature
5c9f13
5c9f13
As distributed, the container signature is a blob which contains a cryptographic signature
5c9f13
in an industry-standard format, carrying a signed JSON payload (i.e. the blob contains both the
5c9f13
JSON document and a signature of the JSON document; it is not a “detached signature” with
5c9f13
independent blobs containing the JSON document and a cryptographic signature).
5c9f13
5c9f13
Currently the only defined cryptographic signature format is an OpenPGP signature (RFC 4880),
5c9f13
but others may be added in the future.  (The blob does not contain metadata identifying the
5c9f13
cryptographic signature format. It is expected that most formats are sufficiently self-describing
5c9f13
that this is not necessary and the configured expected public key provides another indication
5c9f13
of the expected cryptographic signature format. Such metadata may be added in the future for
5c9f13
newly added cryptographic signature formats, if necessary.)
5c9f13
5c9f13
Consumers of container signatures SHOULD verify the cryptographic signature
5c9f13
against one or more trusted public keys
5c9f13
(e.g. defined in a [policy.json signature verification policy file](containers-policy.json.5.md))
5c9f13
before parsing or processing the JSON payload in _any_ way,
5c9f13
in particular they SHOULD stop processing the container signature
5c9f13
if the cryptographic signature verification fails, without even starting to process the JSON payload.
5c9f13
5c9f13
(Consumers MAY extract identification of the signing key and other metadata from the cryptographic signature,
5c9f13
and the JSON payload, without verifying the signature, if the purpose is to allow managing the signature blobs,
5c9f13
e.g. to list the authors and image identities of signatures associated with a single container image;
5c9f13
if so, they SHOULD design the output of such processing to minimize the risk of users considering the output trusted
5c9f13
or in any way usable for making policy decisions about the image.)
5c9f13
5c9f13
### OpenPGP signature verification
5c9f13
5c9f13
When verifying a cryptographic signature in the OpenPGP format,
5c9f13
the consumer MUST verify at least the following aspects of the signature
5c9f13
(like the `github.com/containers/image/signature` package does):
5c9f13
5c9f13
- The blob MUST be a “Signed Message” as defined RFC 4880 section 11.3.
5c9f13
  (e.g. it MUST NOT be an unsigned “Literal Message”, or any other non-signature format).
5c9f13
- The signature MUST have been made by an expected key trusted for the purpose (and the specific container image).
5c9f13
- The signature MUST be correctly formed and pass the cryptographic validation.
5c9f13
- The signature MUST correctly authenticate the included JSON payload
5c9f13
  (in particular, the parsing of the JSON payload MUST NOT start before the complete payload has been cryptographically authenticated).
5c9f13
- The signature MUST NOT be expired.
5c9f13
5c9f13
The consumer SHOULD have tests for its verification code which verify that signatures failing any of the above are rejected.
5c9f13
5c9f13
## JSON processing and forward compatibility
5c9f13
5c9f13
The payload of the cryptographic signature is a JSON document (RFC 7159).
5c9f13
Consumers SHOULD parse it very strictly,
5c9f13
refusing any signature which violates the expected format (e.g. missing members, incorrect member types)
5c9f13
or can be interpreted ambiguously (e.g. a duplicated member in a JSON object).
5c9f13
5c9f13
Any violations of the JSON format or of other requirements in this document MAY be accepted if the JSON document can be recognized
5c9f13
to have been created by a known-incorrect implementation (see [`optional.creator`](#optionalcreator) below)
5c9f13
and if the semantics of the invalid document, as created by such an implementation, is clear.
5c9f13
5c9f13
The top-level value of the JSON document MUST be a JSON object with exactly two members, `critical` and `optional`,
5c9f13
each a JSON object.
5c9f13
5c9f13
The `critical` object MUST contain a `type` member identifying the document as a container signature
5c9f13
(as defined [below](#criticaltype))
5c9f13
and signature consumers MUST reject signatures which do not have this member or in which this member does not have the expected value.
5c9f13
5c9f13
To ensure forward compatibility (allowing older signature consumers to correctly
5c9f13
accept or reject signatures created at a later date, with possible extensions to this format),
5c9f13
consumers MUST reject the signature if the `critical` object, or _any_ of its subobjects,
5c9f13
contain _any_ member or data value which is unrecognized, unsupported, invalid, or in any other way unexpected.
5c9f13
At a minimum, this includes unrecognized members in a JSON object, or incorrect types of expected members.
5c9f13
5c9f13
For the same reason, consumers SHOULD accept any members with unrecognized names in the `optional` object,
5c9f13
and MAY accept signatures where the object member is recognized but unsupported, or the value of the member is unsupported.
5c9f13
Consumers still SHOULD reject signatures where a member of an `optional` object is supported but the value is recognized as invalid.
5c9f13
5c9f13
## JSON data format
5c9f13
5c9f13
An example of the full format follows, with detailed description below.
5c9f13
To reiterate, consumers of the signature SHOULD perform successful cryptographic verification,
5c9f13
and MUST reject unexpected data in the `critical` object, or in the top-level object, as described above.
5c9f13
5c9f13
```json
5c9f13
{
5c9f13
    "critical": {
5c9f13
        "type": "atomic container signature",
5c9f13
        "image": {
5c9f13
            "docker-manifest-digest": "sha256:817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e"
5c9f13
        },
5c9f13
        "identity": {
5c9f13
            "docker-reference": "docker.io/library/busybox:latest"
5c9f13
        }
5c9f13
    },
5c9f13
    "optional": {
5c9f13
        "creator": "some software package v1.0.1-35",
5c9f13
        "timestamp": 1483228800,
5c9f13
    }
5c9f13
}
5c9f13
```
5c9f13
5c9f13
### `critical`
5c9f13
5c9f13
This MUST be a JSON object which contains data critical to correctly evaluating the validity of a signature.
5c9f13
5c9f13
Consumers MUST reject any signature where the `critical` object contains any unrecognized, unsupported, invalid or in any other way unexpected member or data.
5c9f13
5c9f13
### `critical.type`
5c9f13
5c9f13
This MUST be a string with a string value exactly equal to `atomic container signature` (three words, including the spaces).
5c9f13
5c9f13
Signature consumers MUST reject signatures which do not have this member or this member does not have exactly the expected value.
5c9f13
5c9f13
(The consumers MAY support signatures with a different value of the `type` member, if any is defined in the future;
5c9f13
if so, the rest of the JSON document is interpreted according to rules defining that value of `critical.type`,
5c9f13
not by this document.)
5c9f13
5c9f13
### `critical.image`
5c9f13
5c9f13
This MUST be a JSON object which identifies the container image this signature applies to.
5c9f13
5c9f13
Consumers MUST reject any signature where the `critical.image` object contains any unrecognized, unsupported, invalid or in any other way unexpected member or data.
5c9f13
5c9f13
(Currently only the `docker-manifest-digest` way of identifying a container image is defined;
5c9f13
alternatives to this may be defined in the future,
5c9f13
but existing consumers are required to reject signatures which use formats they do not support.)
5c9f13
5c9f13
### `critical.image.docker-manifest-digest`
5c9f13
5c9f13
This MUST be a JSON string, in the `github.com/opencontainers/go-digest.Digest` string format.
5c9f13
5c9f13
The value of this member MUST match the manifest of the signed container image, as implemented in the docker/distribution manifest addressing system.
5c9f13
5c9f13
The consumer of the signature SHOULD verify the manifest digest against a fully verified signature before processing the contents of the image manifest in any other way
5c9f13
(e.g. parsing the manifest further or downloading layers of the image).
5c9f13
5c9f13
Implementation notes:
5c9f13
* A single container image manifest may have several valid manifest digest values, using different algorithms.
5c9f13
* For “signed” [docker/distribution schema 1](https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md) manifests,
5c9f13
the manifest digest applies to the payload of the JSON web signature, not to the raw manifest blob.
5c9f13
5c9f13
### `critical.identity`
5c9f13
5c9f13
This MUST be a JSON object which identifies the claimed identity of the image (usually the purpose of the image, or the application, along with a version information),
5c9f13
as asserted by the author of the signature.
5c9f13
5c9f13
Consumers MUST reject any signature where the `critical.identity` object contains any unrecognized, unsupported, invalid or in any other way unexpected member or data.
5c9f13
5c9f13
(Currently only the `docker-reference` way of claiming an image identity/purpose is defined;
5c9f13
alternatives to this may be defined in the future,
5c9f13
but existing consumers are required to reject signatures which use formats they do not support.)
5c9f13
5c9f13
### `critical.identity.docker-reference`
5c9f13
5c9f13
This MUST be a JSON string, in the `github.com/docker/distribution/reference` string format,
5c9f13
and using the same normalization semantics (where e.g. `busybox:latest` is equivalent to `docker.io/library/busybox:latest`).
5c9f13
If the normalization semantics allows multiple string representations of the claimed identity with equivalent meaning,
5c9f13
the `critical.identity.docker-reference` member SHOULD use the fully explicit form (including the full host name and namespaces).
5c9f13
5c9f13
The value of this member MUST match the image identity/purpose expected by the consumer of the image signature and the image
5c9f13
(again, accounting for the `docker/distribution/reference` normalization semantics).
5c9f13
5c9f13
In the most common case, this means that the `critical.identity.docker-reference` value must be equal to the docker/distribution reference used to refer to or download the image.
5c9f13
However, depending on the specific application, users or system administrators may accept less specific matches
5c9f13
(e.g. ignoring the tag value in the signature when pulling the `:latest` tag or when referencing an image by digest),
5c9f13
or they may require `critical.identity.docker-reference` values with a completely different namespace to the reference used to refer to/download the image
5c9f13
(e.g. requiring a `critical.identity.docker-reference` value which identifies the image as coming from a supplier when fetching it from a company-internal mirror of approved images).
5c9f13
The software performing this verification SHOULD allow the users to define such a policy using the [policy.json signature verification policy file format](containers-policy.json.5.md).
5c9f13
5c9f13
The `critical.identity.docker-reference` value SHOULD contain either a tag or digest;
5c9f13
in most cases, it SHOULD use a tag rather than a digest.  (See also the default [`matchRepoDigestOrExact` matching semantics in `policy.json`](containers-policy.json.5.md#signedby).)
5c9f13
5c9f13
### `optional`
5c9f13
5c9f13
This MUST be a JSON object.
5c9f13
5c9f13
Consumers SHOULD accept any members with unrecognized names in the `optional` object,
5c9f13
and MAY accept a signature where the object member is recognized but unsupported, or the value of the member is valid but unsupported.
5c9f13
Consumers still SHOULD reject any signature where a member of an `optional` object is supported but the value is recognized as invalid.
5c9f13
5c9f13
### `optional.creator`
5c9f13
5c9f13
If present, this MUST be a JSON string, identifying the name and version of the software which has created the signature.
5c9f13
5c9f13
The contents of this string is not defined in detail; however each implementation creating container signatures:
5c9f13
5c9f13
- SHOULD define the contents to unambiguously define the software in practice (e.g. it SHOULD contain the name of the software, not only the version number)
5c9f13
- SHOULD use a build and versioning process which ensures that the contents of this string (e.g. an included version number)
5c9f13
  changes whenever the format or semantics of the generated signature changes in any way;
5c9f13
  it SHOULD not be possible for two implementations which use a different format or semantics to have the same `optional.creator` value
5c9f13
- SHOULD use a format which is reasonably easy to parse in software (perhaps using a regexp),
5c9f13
  and which makes it easy enough to recognize a range of versions of a specific implementation
5c9f13
  (e.g. the version of the implementation SHOULD NOT be only a git hash, because they don’t have an easily defined ordering;
5c9f13
  the string should contain a version number, or at least a date of the commit).
5c9f13
5c9f13
Consumers of container signatures MAY recognize specific values or sets of values of `optional.creator`
5c9f13
(perhaps augmented with `optional.timestamp`),
5c9f13
and MAY change their processing of the signature based on these values
5c9f13
(usually to accommodate violations of this specification in past versions of the signing software which cannot be fixed retroactively),
5c9f13
as long as the semantics of the invalid document, as created by such an implementation, is clear.
5c9f13
5c9f13
If consumers of signatures do change their behavior based on the `optional.creator` value,
5c9f13
they SHOULD take care that the way they process the signatures is not inconsistent with
5c9f13
strictly validating signature consumers.
5c9f13
(I.e. it is acceptable for a consumer to accept a signature based on a specific `optional.creator` value
5c9f13
if other implementations would completely reject the signature,
5c9f13
but it would be very undesirable for the two kinds of implementations to accept the signature in different
5c9f13
and inconsistent situations.)
5c9f13
5c9f13
### `optional.timestamp`
5c9f13
5c9f13
If present, this MUST be a JSON number, which is representable as a 64-bit integer, and identifies the time when the signature was created
5c9f13
as the number of seconds since the UNIX epoch (Jan 1 1970 00:00 UTC).