Virtualization · Image Supply Chain
What an Image Signature Covers and What It Leaves Out
How a signature envelope binds a signing key to a payload digest, why the payload is usually not the image, and the payload types that close different parts of the gap.
A signed image is not signed the way a signed email is signed. The bytes of a container image can be hundreds of megabytes spread across many blobs, and signing them directly would require the verifier to hold all of it, to know exactly which bytes were canonical, and to agree with the signer on a serialization. Instead, image signing signs a small statement that names a digest. The statement is the thing covered by cryptography; the digest is the link to the content. Almost every misunderstanding about image signatures comes from collapsing those two objects into one.
The envelope is a small, typed document
A signature envelope is a JSON object, or a CBOR object, containing a payload and one or more signatures over it. Its essential fields are a payload type, a payload body, and for each signature a key identifier, a signature algorithm, and the signature bytes. The envelope’s purpose is to be self-contained: given the envelope and a trust root, a verifier can decide whether to accept without contacting the signer.
The payload type is the field that determines what the signature means. An envelope carrying a payload of type “this is a digest” and an envelope carrying a payload of type “this is a provenance statement about a digest” can both be valid, and they say different things. A verifier that ignores the payload type and only checks that the signature is valid over whatever bytes are present has verified a signature but not a claim.
Signing algorithms in use are the ones with small, fixed-size signatures and well-understood implementations: ECDSA over the P-256 curve, Ed25519 as defined in RFC 8032, and in some deployments RSA. Key identification is usually by a fingerprint or by a key identifier embedded in the envelope, which is what allows a verifier to select the right public key from a set without trying each one.
The signature is computed over the payload bytes, not over the envelope. The envelope’s other fields (the key identifier, the algorithm, annotations) are not covered. This is a deliberate design choice: it lets one payload carry several signatures from different keys, and it lets a verifier add a counter-signature without invalidating the first. The cost is that the envelope metadata is unauthenticated, so a verifier must not make trust decisions based on an unauthenticated field such as a key identifier without confirming that the identified key actually produced the signature.
{
"payloadType": "application/vnd.in-toto+json",
"payload": "eyJfdHlwZSI6...",
"signatures": [
{
"keyid": "SHA256:3f8a...",
"sig": "MEUCIQ..."
}
]
}
The payload is base64, which is an encoding, not a security boundary. A verifier must decode it, parse it according to the declared type, and apply the rules for that type. Treating the payload as opaque bytes and only checking the signature proves possession of a key, nothing more.
Signing a digest versus signing a manifest
Two payload shapes are common, and the difference is consequential.
The first is a bare digest claim: a document whose payload is a map from a digest string to a size, or simply a manifest digest with a media type. The signer asserts “this digest is one I vouch for”. The verifier checks the signature, extracts the digest, and then requires that the image being deployed resolves to exactly that digest. The binding is direct and the verification is cheap, but the claim is minimal: it says nothing about where the image came from or what is inside it.
The second is a signature over the manifest document itself, or over a canonicalized form of it. This commits to the manifest’s content, which includes the digests of the config and every layer, and therefore transitively to the whole image. The trade is that the verifier must reconstruct the exact bytes the signer hashed. If the signer hashed the manifest as stored in the registry and the verifier re-serializes it before hashing, the signature fails for a reason that has nothing to do with trust. This is the same byte-preservation problem described in Content Digests and Why an Image Tag Is Not an Identity, appearing one layer up.
A third shape signs the index rather than a manifest. Signing an index commits to the digests of its member manifests, and therefore to their layers. It does not commit to anything about manifests that are not in the index, and it does not constrain which member a client selects. A client on arm64 that verifies an index signature and then pulls the amd64 member has verified the signature correctly and deployed the wrong image, because the selection step is outside the signed statement.
The signature says nothing about the tag
A signature over a digest does not mention tags, and this is the point. The tag is a mutable pointer; the digest is content. If the signature covered myapp:latest, it would be invalid the moment the tag moved, and it would be meaningless because the tag’s meaning is registry-defined.
This has an operational consequence that surprises people: verifying a signature at pull time does not prevent a tag from being repointed. It prevents a repointed tag from being accepted. The client resolves myapp:latest to a digest, checks that the digest has a valid signature from a trusted key, and refuses if it does not. The tag can move to an unsigned digest; the pull fails. The signature is doing its job precisely because it does not cover the tag.
Verification therefore has to happen at a point where the digest is still the subject. If a deployment pipeline resolves a tag to a digest and writes the tag into a manifest for a later pull, the later pull re-resolves the tag and the verification applies to a different object. Pinning the digest in the deployment artifact is what keeps the verified object and the deployed object identical.
Four claims a valid signature does not make
Four gaps recur, and each has a different remedy.
Who signed. A signature proves that a particular key signed a particular payload. Whether that key belongs to the organisation it claims is a question about key distribution and identity, not about cryptography. Verifiers handle it with a trust root (a set of public keys, or a certificate chain anchored in a root) and the trust root is the entire security boundary. A verifier configured to accept any key that signs correctly has verified nothing about authorship.
What was built from what. A digest signature says the signer vouches for the bytes. It does not say the bytes came from a particular repository, commit, or build invocation. That claim requires a provenance statement, which is a different payload type and a different verification: parse the statement, check that its subject digest matches the image digest, check that its builder identity is trusted, and check the materials it names. Provenance Statements and the Subject Digest covers how that binding is expressed and where it is forgeable.
Whether the image is safe. A signature attests to origin, not to quality. An image can be signed by its legitimate builder and still contain a vulnerable dependency, a misconfiguration, or a malicious package introduced upstream. Signature verification and vulnerability scanning answer different questions and neither substitutes for the other.
Whether it is still valid. Keys are revoked, and a signature made before revocation is still cryptographically valid. The verifier has to check revocation at verification time, which requires either an online check or a trust root that carries expiry and revocation data. Offline verification against a static key set cannot detect revocation, and a verifier that claims to check signatures without saying how it handles revocation has an unstated gap.
Multiple signatures and the quorum question
An envelope can carry several signatures. This supports a workflow where an artifact is signed by a build system and then countersigned by a release approver, and it supports the case where a policy requires k of n keys.
The mechanics are simple: each signature is checked independently against the same payload, and the verifier applies a policy to the set of keys that produced valid signatures. The hard part is the policy. A threshold expressed over key identifiers is only as strong as the trust root that maps identifiers to keys, and a policy that accepts “any one of these ten keys” is a policy that accepts a compromise of any one of them. Verifiers that support thresholds typically require the policy to be expressed in terms of a root of trust with explicit roles, which is the same structure The Update Framework uses for repository metadata: distinct roles, each with its own key set, each with a defined threshold.
Attachments live beside the image, not inside it
Signatures are stored in the registry as separate artifacts, associated with the signed digest rather than embedded in the manifest. This is what allows an image to be signed after it is pushed, by a party that cannot write to the image’s repository, and it is what allows signatures to be added without changing the image digest.
The association mechanism matters for verification. A verifier asks the registry for artifacts referring to a digest, receives a list of descriptors, fetches the relevant ones, and checks each. The referrers mechanism that makes this a single query rather than a convention is covered in Referrers, Attachments, and the Artifact Graph. Where the association is implemented by a naming convention instead (storing signatures under a derived repository name) the verifier has to know the convention, and a signature stored under a different convention is invisible.
The signature’s own storage is content addressed like everything else, so a signature blob cannot be altered after the fact. What can be altered is the association: an attacker with push access to the repository holding signatures can delete them, causing verification to fail, or add signatures from keys the verifier does not trust, which a correct verifier rejects. The asymmetry is worth stating plainly: signatures are removable by anyone who can write to where they live, so verification availability depends on controlling that repository, not only on holding the signing key.