owned this note changed 4 years ago
Linked with GitHub

Notary v2 Signature Formats

tags: notary

Notary v2 can sign any content within an OCI Distribution based registry. This document provides an iterative collaboration on what the signature content would look like, when persisted as a referenceType to content within a registry:

Target Image

For the purposes of this discussion, we'll focus on signing the Notary v2 prototypical net-monitor:v1 image.

  • repository: net-monitor
  • media type: application/vnd.oci.image.manifest.v1+json
  • digest: sha256:9aab35483ad0ee2e56fc1cf8205d5b7ca2fe029dccfc70988eb067e6b2f38663
  • size: 809
  • tag: :v1

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
NOTE: There is no change to the existing image-spec 1.0 format. This example highlights what already exists.

{
   "schemaVersion": 2,
   "config": {
      "mediaType": "application/vnd.oci.image.config.v1+json",
      "digest": "sha256:e752324f6804d5d0b2c098f84507d095a8fd0031cf06cdb3c7ad1625dcd1b399",
      "size": 7097
   },
   "layers": [
      {
         "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
         "digest": "sha256:83c5cfdaa5385ea6fc4d31e724fd4dc5d74de847a7bdd968555b8f2c558dac0e",
         "size": 25851449
      },
      {
         "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
         "digest": "sha256:7445693bd43e8246a8c166233392b33143f7f5e396c480f74538e5738fb6bd6e",
         "size": 226
      }
   ],
   "annotations": {
      "org.wabbit-networks.important-stuff": "important value",
      "org.wabbit-networks.more-important-stuff": "more important value"
   }
}

A Notary v2 artifact manifest of the net-monitor:v1 signature

  • repository: net-monitor
  • media type: application/vnd.oci.artifact.manifest.v1+json
  • digest: sha256:218e9533924a24d64f2cc6c7db8e5120e415d441a7529919c8d30472341038e7
  • size: 720
  • tag: -none-
{
   "schemaVersion": 3,
   "mediaType": "application/vnd.oci.artifact.manifest.v1+json",
   "artifactType": "cncf.notary.v2-rc1",
   "blobs": [
      {
         "mediaType": "application/tar",
         "digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0",
         "size": 32654
      }
   ],
   "subjectManifest": {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:9aab35483ad0ee2e56fc1cf8205d5b7ca2fe029dccfc70988eb067e6b2f38663",
      "size": 809,
      "annotations": {
         "org.acme-rockets.importDate": "2021-04-23T18:25:43.511Z"
      }
   },
   "annotations": {
      "org.cncf.notary.v2.signature.subject": "acme-rockets.io"
   }
}

The above artifact manifest signs the subjectManifest. Any content within the above net-monitor image is secured with the content in the blob

Justin Cormack: What I think you're suggesting is the following annotation would become part of the signed content stored in the blob?

"annotations": {
   "org.acme-rockets.importDate": "2021-04-23T18:25:43.511Z"
}

Signature Payload

{
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "size": 809,
    "digest": "sha256:9aab35483ad0ee2e56fc1cf8205d5b7ca2fe029dccfc70988eb067e6b2f38663",
    "annotations": {
        "notary.v2.identity": "acme-rockets.io/net-monitor:v1"
    }
}

Signature Examples

JWS JSON Serialization

Payload: aka claims

{
    "notary.v2": { 
        "subjectManifest": {
            "mediaType": "application/vnd.oci.image.manifest.v1+json",
            "digest": "sha256:9aab35483ad0ee2e56fc1cf8205d5b7ca2fe029dccfc70988eb067e6b2f38663",
            "size": 809,
            "annotations": {
                "org.acme-rockets.importDate": "2021-04-23T18:25:43.511Z"
          }
        },
        "signedAttrs": {
            "reserved": {
                "arl": "http://registry.wabbit.net/arl",
                "identity": "acme-rockets.io/net-monitor:v1"
            },
            "custom" : {
                "buildId": "0001",
                "imageScanned": "true"
            }
        }
    },
    "iat": 1627880148,
    "exp": 1627883748
}

Payload contain the subject manifest and other attributes that has to be integrity protected.

  • notary.v2: Top level node and private claim, encapsulating the notary v2 data. This claim MUST be present.
  • subjectManifest: The image manifest that needs to be integrity protected.
  • signedAttrs: Contains additional attributes that needs to be integrity protected.
    • reserved: Collection of attributes reserved for notary v2 use such as artifact revocation list(arl),identity, etc.
    • custom: Collection of user defined attributes such as buildId, imageScanned, etc. Use of this field is OPTIONAL.
  • iat: Issued at identifies the time at which the JWT was issued. This claim MUST be present.
  • exp: Expiration time identifies the expiration time on or after which the JWT must not be accepted for processing. This claim MUST be present.
  • In order to leverage JWS claims validation functionality already provided by libraries we have defined iat, exp as top-level nodes.

ProtectedHeader:

{
    "alg": "RS256",
    "cty": "application/<TBD>",
    "crit":["cty"]
}
  • alg: JWS needs algorithm(alg) to be present in header so we have added it as protected header. This header MUST be present.
  • cty: Content type(cty) used to declare the media type of the secured content(the payload). This header MUST be present.
  • crit: Indicates the list of headers that implementation MUST understood and process. This header MUST be present.

SignatureEnvelope:

{
    "payload": "<Base64Url(Payload)>",
    "protected": "<Base64Url(ProtectedHeader)>"
    "header": {
      "timestamp": "<Base64(TimeStampToken)>",
      "kid": "906ade40b96cff95e5b60f7e96f2cda7979c8ad5",
      "x5c": ["<Base64(DER(leafCert))>", "<Base64(DER(intermediateCACert))>", "<Base64(DER(rootCert))>"]
    },
    "signature": "Base64Url( sign( ASCII( <Base64Url(Payload)>.<Base64Url(ProtectedHeader)> )))"  
}
  • protected: Base64Url encoded JSON object that contains the header parameters that are integrity protected by the JWS Signature digital signature
  • header: JOSE Header containing the parameters describing the cryptographic operations and parameters employed. header is not integrity protected by signature. To start with we will only support reserved set of headers.
    • timestamp: Base64 encoded timestamp token generated by TSA. Use of this is OPTIONAL.
    • kid Hint indicating which key was used to generate the signature. Use of this is OPTIONAL.
    • x5c Contains the X.509 public key certificate or certificate chain corresponding to the key used to generate the signature. Use of this is OPTIONAL. If signature was generated by x509 certificate signature envelop MUST contain x5c.
  • The header node MUST contain either kid or x5c but not both.
  • In case of x5c, the leaf certificate's public key algorithm(with some additional conventions) will be used for signature generation and this algorithm must match with alg header value. The verifier will make sure that the value of alg header is same as that of leaf certificate's signing algorithm.
    • Additional convention for algorithm: If the x509 certificate does not indicate the hash algorithm (as is the case of ECDSA), then the implementation will use hard-code a mapping similar to that of JWS.
  • Implementation should only support predefined set of asymmetric signing algorithms, which means value of alg will always be from a predefined set of values.

Dead Simple Signature Envelop (DSSE)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Working in progress

Extended DSSE

Payload:

{ 
    "subjectManifest": {
        "mediaType": "application/vnd.oci.image.manifest.v1+json",
        "digest": "sha256:9aab35483ad0ee2e56fc1cf8205d5b7ca2fe029dccfc70988eb067e6b2f38663",
        "size": 809,
        "annotations": {
            "org.acme-rockets.importDate": "2021-04-23T18:25:43.511Z"
      }
    },
    "signedAttrs": {
        "reserved": {
            "iat": "2021-06-22T16:02:40.3375379Z",
            "exp": "2021-09-20T16:02:40.3375379Z",
            "arl": "http://registry.wabbit.net/arl",
            "identity": "acme-rockets.io/net-monitor:v1"
        },
        "custom" : {
            "BuildId": "0001",
            "imageScanned": "true"
        }
    }
}

SignatureEnvelope:

{
    "payload": "<Base64(Payload)>",
    "payloadType": "application/<TBD>",
    "signatures": [
        {
            "keyid": "906ade40b96cff95e5b60f7e96f2cda7979c8ad5",
            "x5c": ["<Base64(DER(leafCert))>", "<Base64(DER(intermediateCACert))>", "<Base64(DER(rootCert))>"],
            "timestamp": "<Base64(TimeStampToken)>",
            "sig": "Base64( sign( <Base64(Payload)> ))"
        }
    ]
}

In SignatureEnvelope's signatures node, MUST contain either keyid or x5c but not both. Also, currently DSSE doesn't support timestamp and x5c fields.

Open Questions

  1. In Payload do we need subjectManifest#annotations? We have already defined signedAttrs#custom collection for user defined attributes?
  2. Should signature verification fail if signature envelop contains unsupported/additional fields?
Select a repo