This specification implements the Notary Project signature specification using CBOR Object Signing and Encryption (COSE). COSE (RFC8152) is a CBOR based envelope format for digital signatures over any type of payload (e.g. CBOR, JSON, binary). Notary Project specifically supports COSE_Sign1_Tagged as a signature envelope.
A COSE signature envelope will be stored in an OCI registry as a blob, and referenced in the signature manifest as a layer blob with mediaType
of "application/cose"
.
Signature Manifest Example
{
"mediaType": "application/vnd.oci.artifact.manifest.v1+json",
"config": {
"mediaType": "application/vnd.cncf.notary.signature",
"size": 2,
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
},
"layers": [
{
"mediaType": "application/cose",
"digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0",
"size": 32654
}
],
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333",
"size": 16724
},
"annotations": {
"io.cncf.notary.x509chain.thumbprint#S256":
"[\"B7A69A70992AE4F9FF103EBE04A2C3BA6C777E439253CE36562E6E98375068C3\",\"932EB6F5598435D4EF23F97B0B5ACB515FAE2B8D8FAC046AB813DDC419DD5E89\"]"
}
}
For detached signatures associated with arbitrary blobs, a COSE signature envelope will be stored on the file system as a binary file with cose
as the file extension.
The COSE envelope contains the Notary Project signature Payload.
Example of the Notary Project OCI signature payload:
{
"targetArtifact": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:73c803930ea3ba1e54bc25c2bdc53edd0284c62ed651fe7b00369da519a3c333",
"size": 16724,
"annotations": {
"io.wabbit-networks.buildId": "123" // user defined metadata
}
}
}
Example of the Notary Project blob signature payload:
{
"targetArtifact": {
"mediaType": "application/octet-stream",
"digest": "sha256:2f3a23b6373afb134ddcd864be8e037e34a662d090d33ee849471ff73c873345",
"size": 1024,
"annotations": {
"io.wabbit-networks.buildId": "123" // user defined metadata
}
}
}
The COSE envelope for the Notary Project signature uses the following header parameters:
- Common parameters
- Label
1
:alg
- Label
2
:crit
- Label
3
:content type
- Label
- Additional parameters with collision resistant names.
io.cncf.notary.signingScheme
io.cncf.notary.signingTime
io.cncf.notary.authenticSigningTime
io.cncf.notary.expiry
Example with Signing Scheme notary.x509
{
/ alg / 1: / PS384 / -38,
/ crit / 2: [
'io.cncf.notary.signingScheme',
'io.cncf.notary.expiry'
],
/ cty / 3: 'application/vnd.cncf.notary.payload.v1+json',
'io.cncf.notary.signingScheme': 'notary.x509',
'io.cncf.notary.signingTime': 1(1667411812),
'io.cncf.notary.expiry': 1(1667415412)
}
Example with Signing Scheme notary.x509.signingAuthority
{
/ alg / 1: / PS384 / -38,
/ crit / 2: [
'io.cncf.notary.signingScheme',
'io.cncf.notary.authenticSigningTime',
'io.cncf.notary.expiry'
],
/ cty / 3: 'application/vnd.cncf.notary.payload.v1+json',
'io.cncf.notary.signingScheme': 'notary.x509.signingAuthority',
'io.cncf.notary.authenticSigningTime': 1(1667411812),
'io.cncf.notary.expiry': 1(1667415412)
}
Note: The above examples are represented using the extended CBOR diagnostic notation.
alg
(int): This REQUIRED parameter (label1
) defines which signing algorithm was used to generate the signature. The signature algorithm of the signing key (first certificate inx5chain
) is the source of truth, and during signing the value ofalg
MUST be set corresponding to signature algorithm of the signing key using this mapping that lists the Notary Project signature allowed subset ofalg
values supported by COSE. Similarly verifier of the signature MUST matchalg
with signature algorithm of the signing key to mitigate algorithm substitution attacks.crit
(array of int/tstr): This REQUIRED parameter (label2
) lists the header parameters that implementations MUST understand and process. It MUST only contain parameters apart from integer labels in the range of 0 to 8. This header MUST containio.cncf.notary.signingScheme
which is a required critical header, and optionally containio.cncf.notary.authenticSigningTime
andio.cncf.notary.expiry
if these critical headers are present in the signature.content type
(tstr): The REQUIRED parameter content type (label3
) is used to declare the media type of the secured content (the payload). The supported value isapplication/vnd.cncf.notary.payload.v1+json
.io.cncf.notary.signingScheme
(tstr, critical): This REQUIRED header specifies the Notary Project signing scheme used by the signature. Supported values arenotary.x509
andnotary.x509.signingAuthority
.io.cncf.notary.signingTime
(date/time): This header specifies the time at which the signature was generated. This is an untrusted date/time, and therefore not used in trust decisions. Its value is an Epoch-Based Date/Time defined in RFC 8949. The optional fractional seconds SHOULD NOT be used. This claim is REQUIRED and only valid when signing scheme isnotary.x509
.io.cncf.notary.authenticSigningTime
(date/time, critical): This header specifies the authenticated time at which the signature was generated. Its value is an Epoch-Based Date/Time defined in RFC 8949. The optional fractional seconds SHOULD NOT be used. This claim is REQUIRED and only valid when signing scheme isnotary.x509.signingAuthority
.io.cncf.notary.expiry
(date/time, critical): This OPTIONAL header provides a "best by use" time for the artifact, as defined by the signer. Its value is an Epoch-Based Date/Time defined in RFC 8949. The optional fractional seconds SHOULD NOT be used.
The Notary Project signature supports the following unprotected header parameters:
io.cncf.notary.timestampSignature
- Label
33
:x5chain
io.cncf.notary.signingAgent
{
/ x5chain / 33: [
<< DER(leafCert) >>,
<< DER(intermediate CACert) >>,
<< DER(rootCert) >>
],
'io.cncf.notary.timestampSignature': << TimeStampToken >>,
'io.cncf.notary.signingAgent': 'notation/1.0.0'
}
Note: <<
and >>
are used to notate the CBOR byte string resulting from encoding the data item.
x5chain
(array of bstr): This REQUIRED parameter (label33
by IANA) contains the ordered list of X.509 certificate or certificate chain (RFC5280) corresponding to the key used to digitally sign the COSE. The certificate chain is represented as an array of certificate, each certificate in the array is DER encoded and then wrapped in a CBOR byte string. The certificate containing the public key corresponding to the key used to digitally sign the COSE MUST be the first certificate, followed by the intermediate and root certificates in the correct order. Refer Certificate Chain unsigned attribute for more details. Optionally, this header can be presented in the protected header.io.cncf.notary.timestampSignature
(bstr): This OPTIONAL header is used to store a countersignature that proves the signature was generated before the timestamp. Only RFC 3161 compliantTimeStampToken
are supported. If present, this header is validated and used solely under thenotary.x509
signing scheme. Refer Timestamp Signature unsigned attribute for more details.io.cncf.notary.signingAgent
(tstr): This OPTIONAL header provides the identifier of a client (e.g. Notation) that produced the signature. E.g.notation/1.0.0
. Refer Signing Agent unsigned attribute for more details.
In COSE, signature is calculated by constructing the Sig_structure
for COSE_Sign1
.
The process is described below:
- Encode the protected header into a CBOR object as a byte string named
body_protected
. - Construct the
Sig_structure
forCOSE_Sign1
. The fieldcontext
is set toSignature1
forCOSE_Sign1
as specified by RFC9052.Sig_structure = [ / context / 'Signature1', / body_protected / << ProtectedHeaders >>, / external_aad / h'', / payload / << Payload >>, ]
- Encode
Sig_structure
into a CBOR object as a byte string namedToBeSigned
. - Compute the signature on the
ToBeSigned
constructed in the previous step by using the signature algorithm of the signing key, which MUST match the corresponding protected header elementalg
. This is the value of the signature property used in the signature envelope.
The final signature envelope is a COSE_Sign1_Tagged
object, consisting of Payload, ProtectedHeaders, UnprotectedHeaders, and Signature.
18(
[
/ protected / << {
/ alg / 1: / PS384 / -38,
/ crit / 2: [
'io.cncf.notary.signingScheme',
'io.cncf.notary.authenticSigningTime',
'io.cncf.notary.expiry'
],
/ cty / 3: 'application/vnd.cncf.notary.payload.v1+json',
'io.cncf.notary.signingScheme': 'notary.x509.signingAuthority',
'io.cncf.notary.authenticSigningTime': 1(1667411812),
'io.cncf.notary.expiry': 1(1667415412)
} >>,
/ unprotected / {
/ x5chain / 33: [
<< DER(leafCert) >>,
<< DER(intermediate CACert) >>,
<< DER(rootCert) >>
],
'io.cncf.notary.timestampSignature': << TimeStampToken >>,
'io.cncf.notary.signingAgent': 'notation/1.0.0'
},
/ payload / << descriptor >>,
/ signature / << sign( << Sig_structure >> ) >>
]
)
Implementations of the Notary Project signature specification MUST enforce the following constraints on signature generation and verification:
alg
parameter value MUST NOT be a symmetric-key algorithm such asHMAC
.alg
parameter value MUST be same as that of signature algorithm identified using signing certificate's public key algorithm and size.alg
parameter values for various signature algorithms is a subset of values supported by COSE.
Mapping of the Notary Project signature approved algorithms to COSE alg
header parameter values
Signature Algorithm | alg Label |
---|---|
RSASSA-PSS with SHA-256 | -37 |
RSASSA-PSS with SHA-384 | -38 |
RSASSA-PSS with SHA-512 | -39 |
ECDSA on secp256r1 with SHA-256 | -7 |
ECDSA on secp384r1 with SHA-384 | -35 |
ECDSA on secp521r1 with SHA-512 | -36 |