This document describes how Notary v2 signatures are created and stored. The document consists of following sections:
This section describes how Notary v2 signatures are stored in the OCI Distribution conformant registry. Notary v2 uses ORAS artifact manifest to store the signature in the repository. The signature artifact manifest consists of a signature type, a reference to the manifest of the artifact being signed, a blob referencing to the signature, and a collection of annotations.
artifactType
: Used to identify the Notary signature artifact. The supported value is application/vnd.cncf.notary.v2
.blobs
: A collection consisting of only one artifact descriptor that refers to a signature envelope.
mediaType
: Defines the media type of signature envelope blob. The supported value is application/jose+json
subject
: An artifact descriptor referencing to image manifest that is signed.annotations
: This OPTIONAL property contains arbitrary metadata for the artifact manifest. It can be used to store information about the signature.{
"artifactType": "application/vnd.cncf.notary.v2",
"blobs": [
{
"mediaType": "application/jose+json",
"digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0",
"size": 32654
}
],
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333",
"size": 16724
},
"annotations": {
"org.cncf.notary.signature.subject": "wabbit-networks.io"
}
}
The client should be able to discover all the signatures belonging to an artifact(such as image manifest) by using ORAS Manifest Referrers API. ORAS Manifest Referrers API returns a paginated list of all artifacts belonging to a target artifact(such as image manifest). The implementation can filter Notary signature artifacts by either using ORAS Manifest Referrers API or using custom logic on the client. Each Notary signature artifact refers to a signature envelope blob.
The Signature Envelope is a standard data structure for creating a signed message. A signature envelope consists of the following components:
A signature envelope is e = {m, v, u, s}
where s
is signature.
Notary v2 supports JWS JSON Serialization as signature envelope format with some additional constraints but is extensible to support any other signature envelope format.
Notary v2 requires Payload to be the descriptor of the target manifest that is being signed.
mediaType
, digest
, size
fields.artifactType
field for artifact manifests, or the config.mediaType
for oci.image
based manifests or annotations.{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333",
"size": 16724
}
Notary v2 requires the signature envelope to support the following signed attributes:
In JWS JSON Serialization(RFC7515), data is stored as either claims or headers(protected and unprotected).
Notary v2 uses JWS JSON Serialization for the signature envelope with some additional constraints on the structure of claims and headers.
Unless explicitly specified as OPTIONAL, all fields are mandatory. Also, there shouldn’t be any additional fields other than ones specified in JWSPayload, ProtectedHeader, and UnprotectedHeader.
JWSPayload a.k.a. Claims: Notary v2 is using one private claim(notary) and two public claims(iat and exp). An example of the claim is described below
{
"notary": {
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333",
"size": 16724
},
"attributes": {
"reserved": {
"key1": "value1",
"key2": "value2"
...
},
"custom" : {
"customKey1": "customValue1",
"customKey2": "customValue2"
...
}
}
},
"iat": 0123456789101,
"exp": 1234567891011
}
The payload contains the subject manifest and other attributes that have to be integrity protected.
notary
: The top-level node and a private claim encapsulating the notary v2 data.subject
: The manifest that needs to be integrity protected.attributes
: Contains additional attributes that need to be integrity protected.
reserved
: The collection of attributes reserved for notary v2 use such as artifact revocation list, identity, etc. The attributes MUST be key(string)-value(string) pair. These attributes are yet to be defined.custom
: The collection of user-defined attributes such as buildId, imageScanned, etc. The attributes MUST be key(string)-value(string) pair. The use of this field is OPTIONAL.iat
: Issued at identifies the time at which the signature was issued.exp
: Expiration time identifies the expiration time on or after which the signature must not be considered valid. The use of this claim is OPTIONAL.To leverage JWS claims validation functionality already provided by libraries, we have defined iat
, exp
as top-level nodes.
ProtectedHeaders: Notary v2 uses only 3 protected headers: alg, cty, and crit.
{
"alg": "RS256",
"cty": "application/vnd.cncf.notary.v2.jws.v0",
"crit":["cty"]
}
alg
: JWS needs algorithm(alg) to be present in the header, so we have added it as a protected header.cty
: Content type(cty) used to declare the media type of the secured content(the payload). This will be used to version different variotions of JWS signature. The supported value is application/vnd.cncf.notary.v2.jws.v0
.crit
: Indicates the list of headers that implementation MUST be understood and processed. The value MUST be ["cty"]
.UnprotectedHeaders: Notary v2 uses only 2 unprotected headers: timestamp and x5c.
{
"timestamp": "<Base64Url(TimeStampToken)>",
"x5c": ["<Base64(DER(leafCert))>", "<Base64(DER(intermediateCACert))>", "<Base64(DER(rootCert))>"]
}
timestamp
: Used to store time stamp token. Only RFC3161 compliant TimeStampToken are supported. Use of this header is OPTIONAL.x5c
: Contains the X.509 public key certificate or certificate chain(RFC5280) corresponding to the key used to digitally sign the JWS.Signature: In JWS signature is calculated by combining JWSPayload and protected headers. The process is described below:
ASCII(BASE64URL(UTF8(ProtectedHeaders)) ‘.’ BASE64URL(JWSPayload))
Signature Envelope: The final signature envelope comprises of Claims, ProtectedHeaders, UnprotectedHeaders, and signature.
{
"payload": "<Base64Url(JWSPayload)>",
"signatures": [
{
"protected": "<Base64Url(ProtectedHeaders)>",
"header": {
"timestamp": "<Base64Url(TimeStampToken)>",
"x5c": ["<Base64(DER(leafCert))>", "<Base64(DER(intermediateCACert))>", "<Base64(DER(rootCert))>"]
},
"signature": "Base64Url( sign( ASCII( <Base64Url(JWSPayload)>.<Base64Url(ProtectedHeader)> )))"
}
]
}
If a JWS contains only one signature as above, the JWS can be flattened as
{
"payload": "<Base64Url(JWSPayload)>",
"protected": "<Base64Url(ProtectedHeaders)>"
"header": {
"timestamp": "<Base64Url(TimeStampToken)>",
"x5c": ["<Base64(DER(leafCert))>", "<Base64(DER(intermediateCACert))>", "<Base64(DER(rootCert))>"]
},
"signature": "Base64Url( sign( ASCII( <Base64Url(ProtectedHeader)>.<Base64Url(JWSPayload)> )))"
}
Implementation Constraints Notary v2 implementation MUST enforce the following constraints on signature generation and verification:
alg
header value MUST NOT be none
or any symmetric-key algorithm such as HMAC
.alg
header value MUST be same as that of signature algorithm identified using signing certificate's public key algorithm and size.alg
header values for various signature algorithms:
Signature Algorithm | "alg" Param Value |
---|---|
ECDSA on secp256r1 with SHA-256 | ES256 |
ECDSA on secp256r1 with SHA-512 | ES256 |
RSASSA-PSS with SHA-256 | PS256 |
RSASSA-PSS with SHA-512 | PS512 |
The implementation MUST support the following set of algorithms:
The signing certificate's public key algorithm and size MUST be used to determine the signature algorithm.
Public Key Algorithm | Key Size (bits) | Signature Algorithm |
---|---|---|
RSA | 2048 | RSASSA-PSS with SHA-256 |
RSA | 3072 | RSASSA-PSS with SHA-384 |
RSA | 4096 | RSASSA-PSS with SHA-512 |
ECDSA | 256 | ECDSA on secp256r1 with SHA-256 |
ECDSA | 384 | ECDSA on secp384r1 with SHA-384 |
ECDSA | 512 | ECDSA on secp521r1 with SHA-512 |
How will Notary v2 support multiple signature envelope format?
The general idea is to use mediaType
of artifact manifest's blob to identify the signature artifact(like jws, cms, dsse,etc). The client implementation can use aformentioned mediaType
to parse the signature envelope.
How will Noatary v2 handle non-backward compatible changes to signature format.
Signature envelope MUST have versioning mechanism to support non-backward compatible changes.
notary