diff --git a/signature-envelope-jws.md b/signature-envelope-jws.md index 3ee2031b..f2eb4824 100644 --- a/signature-envelope-jws.md +++ b/signature-envelope-jws.md @@ -101,6 +101,13 @@ Example with Signing Scheme `notary.x509.signingAuthority` - **`io.cncf.notary.expiry`**(*string*)(critical): This OPTIONAL header provides a “best by use” time for the artifact, as defined by the signer. Its value is a [RFC 3339][rfc3339] formatted date time, the optional fractional second ([time-secfrac][rfc3339][[1](https://datatracker.ietf.org/doc/html/rfc3339#section-5.3)]) SHOULD NOT be used. - **[`crit`](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.11)**(*array of strings*): This REQUIRED (optional as per JWS spec, but required in Notary v2 JWS signature) header lists the headers that implementation MUST understand and process. It MUST only contain headers apart from registered headers (e.g. `alg`, `cty`) in JWS specification. This header MUST contain `io.cncf.notary.signingScheme` which is a required critical header, and optionally contain `io.cncf.notary.authenticSigningTime` and `io.cncf.notary.expiry` if these critical headers are present in the signature. +## Extended Protected Headers for *Notation* Plugins + +See [Extended attributes for *Notation* Plugins](./signature-specification.md#extended-attributes-for-notation-plugins) for detailed description of these headers. + +- **`io.cncf.notary.verificationPlugin`**(*string*)(critical): An OPTIONAL header that specifies the name of the verification plugin that MAY be used to verify the signature. +- **`io.cncf.notary.verificationPluginMinVersion`**(*string*)(critical): An OPTIONAL header that specifies the minimum version of the verification plugin that MUST be used to verify the signature. + ## Unprotected Headers Notary v2 supports following unprotected headers: `timestamp`, `x5c` and `io.cncf.notary.signingAgent` diff --git a/signature-specification.md b/signature-specification.md index b27dd534..e1600e75 100644 --- a/signature-specification.md +++ b/signature-specification.md @@ -133,6 +133,30 @@ Notary v2 requires the signature envelope to support the following signed attrib - **Expiry** (critical): An OPTIONAL claim that provides a “best by use” time for the artifact, as defined by the signer. More details [here](#expiry). - **Content Type** (critical): A REQUIRED claim that indicates the content type of the [payload](#payload). The supported value is `application/vnd.cncf.notary.payload.v1+json`. Other payload types MAY be supported in future. +#### Extended attributes + +Implementations of Notary v2 signature spec MAY include additional signed attributes in the signature envelope. +These attributes MAY be marked critical, i.e. the attribute MUST be understood and processed by a verifier, unknown critical attributes MUST cause signature verification to fail. +Usage of extended signed attributes which are marked critical in signature will have implications on portability of the signature, these are discussed in [Signature Portability](#signature-portability) section. + +#### Extended attributes for *Notation* Plugins + +This section documents extended attributes used by Notary v2 reference implementation *Notation* to support plugins. +Plugins is a *Notation* concept that allows parts of signing and verification logic to be performed by an external provider. +*Signing plugins* allow *Notation* to be extended for integration with remote keys remote key management services and signing services, where as *verification plugins* allow for customization of verification logic. +Detailed specification for plugins can be found [here](https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#notation-extensibility-for-signing-and-verification). +These extended attributes are documented in this spec, as other Notary V2 implementations may encounter these attributes if they verify a signature that indicated it required a verification plugin for complete signature verification. + +- **Verification Plugin** (critical): An OPTIONAL attribute that specifies the name of the verification plugin that MAY be used to verify the signature e.g. “com.example.nv2plugin”. +[Notation plugin](https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#plugin-contract) aware implementations use this attribute to load and execute a *Notation* compliant plugin. +The plugin participates in the overall signature verification workflow and performs specific steps in it. +- **Verification Plugin Minimum Version** (critical): An OPTIONAL attribute that specifies the minimum version of the verification plugin that MUST be used to verify the signature. +A Notation plugin aware implementations MUST use this attribute to verify the signature with a plugin with matching or higher plugin version. +The plugin MUST use [Semantic Versioning](https://semver.org/) (SemVer) to use this feature i.e the `get-plugin-metadata` plugin command MUST return a SemVer compliant version in the response. +A use case for this feature is for a plugin publisher to address security bug in older plugin version, by setting the minimum version to the plugin version with fixes. + +See [Guidelines for Notary v2 Implementors](#guidelines-for-notary-v2-implementors) for options to handle these attributes during signature verification. + ### Unsigned Attributes These attributes are considered unsigned with respect to the signing key that generates the signature. These attributes are typically signed by a third party (e.g. CA, TSA). @@ -228,6 +252,54 @@ The signing time denotes the time at which the signature was generated. A X509 c This is an optional feature that provides a “best by use” time for the artifact, as defined by the signer. Notary v2 allows users to include an optional expiry time when they generate a signature. The expiry time is not set by default and requires explicit configuration by users at the time of signature generation. The artifact is considered expired when the current time is greater than or equal to expiry time, users performing verification can either configure their trust policies to fail the verification or even accept the artifact with expiry date in the past using policy. This is an advanced feature that allows implementing controls for user defined semantics like deprecation for older artifacts, or block older artifacts in a production environment. Users should only include an expiry time in the signed artifact after considering the behavior they expect for consumers of the artifact after it expires. Users can choose to consume an artifact even after the expiry time based on their specific needs. +### Signature Portability + +Portability of signatures is associated with the portability of associated artifacts which are being signed. +OCI artifacts are inherently location agnostic, artifacts can be pulled from and pushed to any OCI compliant registry to which a user has access. +The artifacts themselves can be classified as follow. + +1. *Public Artifacts* - Artifacts that are distributed publicly for broad consumption. +Artifacts distributed via public registries fall in this category. +E.g. Public images for software distributed by software vendors, and open source projects. +Signatures associated with these artifacts require broad portability. +1. *Private Artifacts* - Artifacts that are private to a user or organization, and may be shared with limited parties. +E.g. Images for containerized applications and services used within an organization, or shared with limited authorized parties. +Based on user requirements a private artifact can have different levels of portability, the signature’s portability should at least match the the artifact’s portability. + +*Notary v2 signature portability* is based on the following + +**Signature discovery** + +Notary v2 addressed signature discovery by storing signatures in the same registry (location) where an artifact is present. +This is supported through [ORAS artifact spec](https://github.com/oras-project/artifacts-spec/blob/main/manifest-referrers-api.md) which allows reference artifacts such as signatures, SBOMs to be associated with existing artifacts like Images. +Notary v2 allows multiple signatures to be associated with an artifact, and clients may automatically push signatures for an artifact to a destination registry when a signed artifact moves from one registry to other. + +**Verification requirements** + +Notary v2 supports a range of signed artifacts intended for public and private distribution. +Signatures generated by Notary v2 without extended signature attributes marked critical can be verified in any environment where Notation client or another Notary v2 standards compliant verification tool is available, without any additional dependencies. +It should be noted that revocations checks, which usually relies on an external mechanism such as CRL/OCSP may require the verification environment to have access to local network or public internet, to have access to the CRL/OCSP endpoint. + +Notary v2 also supports signatures generated using compliant signing plugins, which allow vendors to optionally provide additional features on top of Notary v2 standard features. +Verification of these signatures may require additional dependencies like Notary v2 compliant verification plugin, making these signatures more appropriate to use where broad portability may not be required for the associated signed artifact. +This allows users to implement security controls required for their organizations, that are not broadly applicable and may take time to standardize. +E.g. Integration with a signature transparency log as part of signature verification. + +Based on user’s requirements, a user can select appropriate signing mechanism that produces signatures with desired portability. +Notation signatures without any critical extended attributes do not impose any additional dependency requirements for verifiers as these can be validated with just the Notation client. +Whereas, Notation signatures that contain critical extended attributes will require additional dependencies for signature validation, either on Notary v2 compliant plugins or equivalent tooling which may not be available in all environments. +Similarly, Notary v2 compliant plugin vendors should be aware that usage of extended signed attributes which are marked critical in signature will have implications on portability of the signature. + +### Guidelines for Notary v2 Implementors + +Implementations of Notary v2, can choose to be [Notation plugin protocol](./specs/plugin-extensibility.md#plugin-contract) aware or not. If an implementation chooses to be plugin protocol aware, and it encounters the Verification Plugin and Verification Plugin minimum version attributes during signature verification, it MUST process these attributes. This involves finding the appropriate plugin and the version to use, and executing `verify-signature` plugin command with correct inputs and processing the plugin response, as per the [Verification Plugin interface](../specs/../notaryproject-specs/specs/plugin-extensibility.md#verification-extensibility). + +Alternatively, an implementation of Notary v2 can choose not to implement plugin protocol. + +- The implementation MUST itself perform equivalent verification logic that is usually performed by plugin specified in the signature. +- An implementation MUST fail signature verification if it cannot perform the equivalent verification logic, as skipping the plugin equivalent verification logic will cause incorrect and inconsistent signature verification behavior. +- An implementation MAY choose to support a set of known plugin’s verification logic and fail others. + [annotation-rules]: https://github.com/opencontainers/image-spec/blob/main/annotations.md#rules [oci-descriptor]: https://github.com/opencontainers/image-spec/blob/main/descriptor.md [artifact-descriptor]: https://github.com/oras-project/artifacts-spec/blob/main/descriptor.md diff --git a/specs/plugin-extensibility.md b/specs/plugin-extensibility.md index 4b922ded..c8129ac7 100644 --- a/specs/plugin-extensibility.md +++ b/specs/plugin-extensibility.md @@ -1,6 +1,6 @@ # Notation Extensibility for Signing and Verification -Keys and associated certificates used for signing artifacts using Notary could be available to users through varied solutions that provide secure key generation, storage and cryptographic operations. Some are well established with standards like [PIV](https://csrc.nist.gov/projects/piv/piv-standards-and-supporting-documentation) and [PKCS #11](https://docs.oasis-open.org/pkcs11/pkcs11-base/v3.0/os/pkcs11-base-v3.0-os.html) implemented by hardware tokens, smart cards. More recent options which use varied authentication and API protocols are remote key management services and signing services by third party vendors and cloud service providers. Notation will support a few built-in integrations with standard providers, and will provide plugin interfaces for users, and vendors to implement their own integrations with the solutions they use. This allows a plugin publisher to implement, test, release and patch their solutions independent of Notation’s development and release cycle. This document provides specification for the plugin model, and interfaces to implement. This specification aims to work both for [existing](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md) and future signature formats adopted by Notary. +Keys and associated certificates used for signing artifacts using Notary could be available to users through varied solutions that provide secure key generation, storage and cryptographic operations. Some are well established with standards like [PIV](https://csrc.nist.gov/projects/piv/piv-standards-and-supporting-documentation) and [PKCS #11](https://docs.oasis-open.org/pkcs11/pkcs11-base/v3.0/os/pkcs11-base-v3.0-os.html) implemented by hardware tokens, smart cards. More recent options which use varied authentication and API protocols are remote key management services and signing services by third party vendors and cloud service providers. Notation will support a few built-in integrations with standard providers, and will provide plugin interfaces for users, and vendors to implement their own integrations with the solutions they use. This allows a plugin publisher to implement, test, release and patch their solutions independent of Notation’s development and release cycle. This document provides specification for the plugin model, and interfaces to implement. This specification aims to work both for [existing](../signature-specification.md) and future signature formats adopted by Notary. ## Terminology @@ -102,6 +102,7 @@ Plugin config can be also set/overriden during signing with the `notation sign` * Notation will invoke the plugin executable for each command (e.g. sign, verify), pass inputs through `stdin` and get output through `stdout` and `stderr`. * The command will be passed as the first argument to the plugin e.g. `notary-{plugin-name} `. A JSON request is passed using `stdin`. The plugin is expected to return a JSON response through `stdout` with a `0` exit code for successful response, and a non-zero exit code with a JSON error response in `stderr` for error response. Each command defines its request, response and error contract. To avoid any additional content like debug or info level logging from dependencies and inbuilt libraries, the plugin implementation should redirect any output to `stdout` on initialization, and only send the JSON response away from `stdout` when the command execution completes. E.g. For golang, set [`os.Stdout`](https://pkg.go.dev/os#pkg-variables) to point to a log file. * Every request JSON will contain a `contractVersion` top level attribute whose value will indicate the plugin contract version. Contract version is revised when there are changes to command request/response, new plugin commands are introduced, and supported through Notation. +* To maintain forward compatibility plugin implementors MUST ignore unrecognized attributes in command request which are introduced in minor version updates of the plugin contract. * For an error response, every command returns a non-zero exit code 1, with an OPTIONAL JSON error response in `stderr`. It is recommended to return an error response to help user troubleshoot the error. There is no need to send different exit codes for different error conditions, as Notation (which will call the plugin and parse the response) will use `error-code` in the error response to interpret different error conditions if needed. Notation will attempt to parse the error response in `stderr` when exit code is 1, else treat it as a general error for any other non-zero exit codes. ```jsonc @@ -126,7 +127,14 @@ Plugin config can be also set/overriden during signing with the `notation sign` ***get-plugin-metadata*** -*Request* - None +*Request* + +```jsonc +{ + // Optional plugin configuration, map of string-string + "pluginConfig" : { } +} +``` *Response* All response attributes are required. @@ -146,9 +154,18 @@ All response attributes are required. // List of contract versions supported by the plugin, one per major version "supportedContractVersions" : [ ], - // Currently one of - // SIGNATURE_GENERATOR or - // SIGNATURE_ENVELOPE_GENERATOR + // List of one or more capabilities supported by plugin. + // Valid values are + // SIGNATURE_GENERATOR.RAW + // SIGNATURE_GENERATOR.ENVELOPE + // SIGNATURE_VERIFIER.TRUSTED_IDENTITY + // SIGNATURE_VERIFIER.REVOCATION_CHECK + // + // A signing plugin implements either SIGNATURE_GENERATOR.RAW + // or SIGNATURE_GENERATOR.ENVELOPE capability. + // + // A verification plugin implements + // one or more of SIGNATURE_VERIFIER capabilities. "capabilities" : [ ] @@ -157,9 +174,9 @@ All response attributes are required. *plugin-name* - Plugin name uses reverse domain name notation to avoid plugin name collisions. -*supported-contract-versions* - The list of contract versions supported by the plugin. Currently this list must include only one version, per major version. Post initial release, Notation may add new features through plugins, in the form of new commands (e.g. tsa-sign for timestamping), or additional request and response parameters. Notation will publish updates to plugin interface along with appropriate contract version update. Backwards compatible changes (changes for which older version of plugin continue to work with versions of Notation using newer contract version) like new optional parameters on existing contracts, and new commands will be supported through minor version contract updates, breaking changes through major version updates. Plugin `get-plugin-metadata` command returns the contract version a plugin supports. Notation will evaluate the minimum plugin version required to satisfy a user's request, and reject the request if the plugin does not support the required version. +*supported-contract-versions* - The list of contract versions supported by the plugin. Currently this list must include only one version, per major version. Post initial release, Notation may add new features through plugins, in the form of new commands (e.g. tsa-sign for timestamping), or additional request and response parameters. Notation will publish updates to plugin interface along with appropriate contract version update. Backwards compatible changes (changes for which older version of plugin continue to work with versions of Notation using newer contract version) like new optional parameters on existing contracts, and new commands will be supported through minor version contract updates, breaking changes through major version updates. To maintain forward compatibility plugin implementors MUST ignore unrecognized attributes in command request which are introduced in minor version updates of the plugin contract. Plugin `get-plugin-metadata` command returns the contract version a plugin supports. Notation will evaluate the minimum plugin version required to satisfy a user's request, and reject the request if the plugin does not support the required version. -*capabilities* - Non empty list of features supported by a plugin. Each capability such as `SIGNATURE_ENVELOPE_GENERATOR` requires one of more commands to be implemented by the plugin. When new features are available for plugins to implement, an implementation may choose to not implement it, and therefore will not include the feature in capabilities. Notation will evaluate the capability required to satisfy a user’s request, and reject the request if the plugin does not support the required capability. +*capabilities* - Non empty list of features supported by a plugin. Each capability such as `SIGNATURE_GENERATOR.RAW` requires one of more commands to be implemented by the plugin. When new features are available for plugins to implement, an implementation may choose to not implement it, and therefore will not include the feature in capabilities. Notation will evaluate the capability required to satisfy a user’s request, and reject the request if the plugin does not support the required capability. ## Signing interfaces @@ -180,23 +197,23 @@ This interface targets plugins that integrate with providers of basic cryptograp 3. Append any user provided metadata and Notary metadata as descriptor annotations. 4. Determine if the registered key uses a plugin 5. Execute the plugin with `get-plugin-metadata` command - 1. If plugin supports capability `SIGNATURE_GENERATOR` + 1. If plugin supports capability `SIGNATURE_GENERATOR.RAW` 1. Execute the plugin with `describe-key` command, set `request.keyId` and the optional `request.pluginConfig` to corresponding values associated with signing key `keyName` in `config.json`. - 2. Generate the payload to be signed for [JWS](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#supported-signature-envelopes) envelope format. - 1. Create the JWS protected headers collection and set `alg` to value corresponding to `describe-key.response.keySpec` as per [signature algorithm selection](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection). - 2. Create the `JWSPayload` with appropriate private (`subject`) and public (`iat,exp`) claims. + 2. Generate the payload to be signed for [JWS](../signature-specification.md#supported-signature-envelopes) envelope format. + 1. Create the JWS protected headers collection and set `alg` to value corresponding to `describe-key.response.keySpec` as per [signature algorithm selection](../signature-specification.md#algorithm-selection). + 2. Create the Notary v2 Payload (JWS Payload) as defined [here](../signature-specification.md#payload). 3. The *payload to sign* is then created as - `ASCII(BASE64URL(UTF8(ProtectedHeaders)) ‘.’ BASE64URL(JWSPayload))` 3. Execute the plugin with `generate-signature` command. 1. Set `request.keyId` and the optional `request.pluginConfig` to corresponding values associated with signing key `keyName` in `config.json`. 2. Set `request.payload` as base64 encoded *payload to sign* (the JWS *payload to sign* is double encoded, this is a shortcoming of using plugin contract with JSON encoding). - 3. Set `keySpec` to value returned by `describe-key` command in `response.keySpec`, and `hashAlgorithm` to hash algorithm corresponding to the key spec, as per [signature algorithm selection](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection). The algorithm specified in `hashAlgorithm` MUST be used by the plugin to hash the payload (`request.payload`) as part of signature generation. + 3. Set `keySpec` to value returned by `describe-key` command in `response.keySpec`, and `hashAlgorithm` to hash algorithm corresponding to the key spec, as per [signature algorithm selection](../signature-specification.md#algorithm-selection). The algorithm specified in `hashAlgorithm` MUST be used by the plugin to hash the payload (`request.payload`) as part of signature generation. 4. Validate the generated signature, return an error if any of the checks fails. - 1. Check if `response.signingAlgorithm` is one of [supported signing algorithms](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection). + 1. Check if `response.signingAlgorithm` is one of [supported signing algorithms](../signature-specification.md#algorithm-selection). 2. Check that the plugin did not modify `request.payload` before generating the signature, and the signature is valid for the given payload. Verify the hash of the `request.payload` against `response.signature`, using the public key of signing certificate (leaf certificate) in `response.certificateChain` along with the `response.signingAlgorithm`. This step does not include certificate chain validation (certificate chain leads to a trusted root configured in Notation's Trust Store), or revocation check. - 3. Check that the `response.certificateChain` conforms to [Certificate Requirements](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#certificate-requirements). + 3. Check that the `response.certificateChain` conforms to [Certificate Requirements](../signature-specification.md#certificate-requirements). 5. Assemble the JWS Signature envelope using `response.signature`, `response.signingAlgorithm` and `response.certificateChain`. Notation may also generate and include timestamp signature in this step. 6. Generate a signature manifest for the given signature envelope. - 2. Else if, plugin supports capability `SIGNATURE_ENVELOPE_GENERATOR` *(covered in next section)* + 2. Else if, plugin supports capability `SIGNATURE_GENERATOR.ENVELOPE` *(covered in next section)* 3. Return an error #### describe-key @@ -232,7 +249,7 @@ This command is used to get metadata for a given key. } ``` -*keySpec* : One of following [supported key types](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection) - `RSA_2048`, `RSA_3072`, `RSA_4096`, `EC_256`, `EC_384`, `EC_512`. +*keySpec* : One of following [supported key types](../signature-specification.md#algorithm-selection) - `RSA_2048`, `RSA_3072`, `RSA_4096`, `EC_256`, `EC_384`, `EC_512`. NOTE: This command can also be used as part of `notation key describe {key-name}` which will include the following output @@ -275,7 +292,7 @@ This command is used to generate the raw signature for a given payload. *pluginConfig* : Optional field for plugin configuration. For details, see [Plugin Configuration section](#plugin-configuration). -*keySpec* : Required field that has one of following [supported key types](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection) - `RSA_2048`, `RSA_3072`, `RSA_4096`, `EC_256`, `EC_384`, `EC_512`. Specifies the key type and size for the key. +*keySpec* : Required field that has one of following [supported key types](../signature-specification.md#algorithm-selection) - `RSA_2048`, `RSA_3072`, `RSA_4096`, `EC_256`, `EC_384`, `EC_512`. Specifies the key type and size for the key. *hashAlgorithm* : Required field that specifies Hash algorithm corresponding to the signature algorithm determined by `keySpec` for the key. @@ -295,7 +312,7 @@ All response attributes are required. } ``` -*signingAlgorithm* : One of following [supported signing algorithms](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection), Notation uses this validate the signature, and to set the appropriate attribute in signature envelope (e.g. JWS `alg`). `RSASSA_PSS_SHA_256`, `RSASSA_PSS_SHA_384`, `RSASSA_PSS_SHA_512`, `ECDSA_SHA_256`, `ECDSA_SHA_384`, `ECDSA_SHA_512`. +*signingAlgorithm* : One of following [supported signing algorithms](../signature-specification.md#algorithm-selection), Notation uses this validate the signature, and to set the appropriate attribute in signature envelope (e.g. JWS `alg`). `RSASSA_PSS_SHA_256`, `RSASSA_PSS_SHA_384`, `RSASSA_PSS_SHA_512`, `ECDSA_SHA_256`, `ECDSA_SHA_384`, `ECDSA_SHA_512`. *certificateChain* : Ordered list of certificates starting with leaf certificate and ending with root certificate. @@ -319,18 +336,18 @@ This interface targets plugins that in addition to signature generation want to 1. Append any user provided metadata and Notary metadata as descriptor annotations. 1. Determine if the registered key uses a plugin 1. Execute the plugin with `get-plugin-metadata` command - 1. If plugin supports capability `SIGNATURE_ENVELOPE_GENERATOR` - 1. Execute the plugin with `generate-envelope` command. Set `request.keyId` and the optional `request.pluginConfig` to corresponding values associated with signing key `keyName` in `config.json`. Set `request.payload` to base64 encoded descriptor, `request.payloadType` to `application/vnd.oci.descriptor.v1+json` and `request.signatureEnvelopeType` to a pre-defined type (default to `application/vnd.cncf.notary.v2.jws.v1`). - 1. `response.signatureEnvelope` contains the base64 encoded signature envelope, value of `response.signatureEnvelopeType` MUST match `request.signatureEnvelopeType`. - 1. Validate the generated signature, return an error if of the checks fails. - 1. Check if `response.signatureEnvelopeType` is a supported envelope type and `response.signatureEnvelope`'s format matches `response.signatureEnvelopeType`. - 1. Check if the signing algorithm in the signature envelope is one of [supported signing algorithms](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection). - 1. Check that the [`subject` descriptor](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#supported-signature-envelopes) in JWSPayload in `response.signatureEnvelope` matches `request.payload`. Plugins MAY append additional annotations but MUST NOT replace/override existing descriptor attributes and annotations. - 1. Check that `response.signatureEnvelope` can be verified using the public key and signing algorithm specified in the signing certificate, which is embedded as part of certificate chain in `response.signatureEnvelope` . This step does not include certificate chain validation (certificate chain leads to a trusted root configure in Notation), or revocation check. - 1. Check that the certificate chain in `response.signatureEnvelope` confirm to [Certificate Requirements]. - 1. Generate a signature manifest for the given signature envelope, and append `response.annotations` to manifest annotations. - 1. Else if plugin supports capability `SIGNATURE_GENERATOR` *(covered in previous section)* - 1. Return an error + 1. If plugin supports capability `SIGNATURE_GENERATOR.ENVELOPE` + 1. Execute the plugin with `generate-envelope` command. Set `request.keyId` and the optional `request.pluginConfig` to corresponding values associated with signing key `keyName` in `config.json`. Set `request.payload` to base64 encoded [Notary v2 Payload](../signature-specification.md#payload), `request.payloadType` to `application/vnd.cncf.notary.payload.v1+json` and `request.signatureEnvelopeType` to a pre-defined type (`application/vnd.cncf.notary.v2.jws.v1` for JWS). + 2. `response.signatureEnvelope` contains the base64 encoded signature envelope, value of `response.signatureEnvelopeType` MUST match `request.signatureEnvelopeType`. + 3. Validate the generated signature, return an error if of the checks fails. + 1. Check if `response.signatureEnvelopeType` is a supported envelope type and `response.signatureEnvelope`'s format matches `response.signatureEnvelopeType`. + 2. Check if the signing algorithm in the signature envelope is one of [supported signing algorithms](../signature-specification.md#algorithm-selection). + 3. Check that the [`targetArtifact` descriptor](../signature-specification.md#payload) in JWSPayload in `response.signatureEnvelope` matches `request.payload`. Plugins MAY append additional annotations but MUST NOT replace/override existing descriptor attributes and annotations. + 4. Check that `response.signatureEnvelope` can be verified using the public key and signing algorithm specified in the signing certificate, which is embedded as part of certificate chain in `response.signatureEnvelope` . This step does not include certificate chain validation (certificate chain leads to a trusted root configure in Notation), or revocation check. + 5. Check that the certificate chain in `response.signatureEnvelope` confirm to [Certificate Requirements]. + 4. Generate a signature manifest for the given signature envelope, and append `response.annotations` to manifest annotations. + 2. Else if plugin supports capability `SIGNATURE_GENERATOR.RAW` *(covered in previous section)* + 3. Return an error #### generate-envelope @@ -353,7 +370,7 @@ All request attributes are required. "payload" : "", // The type of payload - currently a descriptor - "payloadType" : "application/vnd.oci.descriptor.v1+json", + "payloadType" : "application/vnd.cncf.notary.payload.v1+json", // The expected response signature envelope "signatureEnvelopeType" : "application/vnd.cncf.notary.v2.jws.v1" @@ -394,7 +411,160 @@ All response attributes are required. ## Verification extensibility -TBD [notaryproject/roadmap#27](https://github.com/notaryproject/roadmap/issues/27) +[notaryproject/roadmap#27](https://github.com/notaryproject/roadmap/issues/27) + +### Requirements + +* The interface MUST NOT be tied to a specific signature envelope format. +* The interface MUST NOT allow a plugin to customize the complete [signature verification workflow](../trust-store-trust-policy-specification.md#signature-verification). Certain steps like identifying the applicable trust policy for the artifact, signature envelope schema validation, integrity checks, certificate chain validation against trust store will be performed by Notation, and cannot be customized by a plugin. +* The interface MUST allow plugins to customize verification logic for specific supported steps + * Trusted Identity validation + * Revocation check validation +* The interface MAY be extended in future to support additional steps which can be customized through a plugin. +* The interface MUST be agnostic of sequencing of steps in signature verification workflow as implemented in Notation or other implementations. +* The interface MUST allow processing of [extended attributes](../signature-specification.md#extended-attributes) that are not part of Notary v2 [standard attributes](../signature-specification.md#standard-attributes). + +### Guidelines for Verification plugin publishers + +* Usage of extended signed attributes which are marked critical in signature will have implications on portability of the signature. +The environment where verification occurs will require dependencies on either a compatible verification plugin in addition to Notation, or a compliant verification tool that understands the extended signed attributes. +Therefore, signatures intended for public distribution which require broad signature portability SHOULD avoid extended signed attributes which are marked critical. + +* Signatures which require a plugin for verification may be distributed privately (e.g. within an organization) or publicly (e.g. via a public registry). +If the plugin publisher wants their plugin used publicly they SHOULD publish specifications for the verification logic the plugin performs and test vectors. +This allows Notary v2 implementations to perform the same logic themselves, if they choose to. + +### Signature Verifier + +#### Verification workflow using plugin + +1. If signature envelope contains *Verification Plugin* attribute, check if the plugin with the given name exists, else fail signature verification. + 1. For the resolved plugin, call the `get-plugin-metadata` plugin command to get plugin version and capabilities. + 2. If signature envelope contains *Verification plugin minimum version* attribute. + 1. Validate that *Verification plugin minimum version* and plugin version are in SemVer format + 2. Validate that plugin version is greater than or equal to *Verification plugin minimum version* + 3. Fail signature verification if these validations fail + 3. Validate if plugin capabilities contains any `SIGNATURE_VERIFIER` capabilities + 1. Fail signature verification if a matching plugin with `SIGNATURE_VERIFIER` capability cannot be found. Include *Verification Plugin Name* attribute as a hint in error response. +2. Complete steps *Identify applicable trust policy* and *Proceed based on signature verification level* from [signature verification workflow](../trust-store-trust-policy-specification.md#steps). +3. Complete steps *Validate Integrity, Validate Expiry* and *Validate Trust Store* from [signature verification workflow](../trust-store-trust-policy-specification.md#steps). +4. Based on the signature verification level, each validation may be enforced, logged or skipped. +5. Populate `verify-signature` request. NOTE: The processing order of remaining known attributes does not matter as long as they are processed before the end of signature verification workflow. + 1. Set `request.signature.criticalAttributes` to the set of [standard Notary v2 attributes](../signature-specification.md#standard-attributes) that are marked critical, from the signature envelope. + 2. Set `request.signature.criticalAttributes.extendedAttributes` to extended attributes that Notation does not recognize. Notation only supports JSON primitive types for critical extended attributes. If a signature producer required complex types, it MUST set the value to a primitive type by encoding it (e.g. Base64), and MUST be decoded by the plugin which processed the attribute. + 3. Set `request.signature.unprocessedAttributes` to set of critical attribute names that are marked critical, but unknown to Notation, and therefore cannot be processed by Notation. + 4. Set `request.signature.certificateChain` to ordered array of certificates from signing envelope. + 5. Set `request.trustPolicy.trustedIdentities` to corresponding values from user configured trust policy that is applicable for given artifact and was identified in step 2. + 6. Set `request.trustPolicy.signatureVerification` to the set of verification checks that are supported by the plugin, and are required to be performed as per step 2. Notation may not populate some checks in this array, if it determined them as “skipped” as per user’s trust policy. +6. Process `verify-signature.response` + 1. Validate `response.verificationResults` map keys match the set of verifications requested in `request.trustPolicy.signatureVerification` . Fail signature verification if they don't match. + 2. For each element in `response.verificationResults` map, process each result as follows + 1. If `verificationResult.success` is true, proceed to next element. + 2. Else if `verificationResult.success` is `false`, check if the failure should be enforced or logged based on value of `signatureVerification` level in Trust Policy. Proceed to next element if failure is logged, else fail signature verification with `verificationResult.reason`. + 3. Validate values in `response.processedAttributes` match the set of values in `request.signature.unprocessedAttributes`. If they don't, fail signature verification, as the plugin did not process a critical attribute that is unknown to the caller. +7. Perform any remaining steps in [signature verification workflow](../trust-store-trust-policy-specification.md#signature-verification) which are not covered by plugin's verification capabilities. These steps MUST process any remaining critical attributes. Fail signature verification if any critical attributes are unprocessed at the end of this step. + +#### *verify-signature* command + +*Request* + +```jsonc +{ + "contractVersion" : "", + + "signature" : { + // Array of all Notary V2 defined critical attributes and their values + // in the signature envelope. Agnostic of header names and value serialization + // in specific envelope formats like JWS or COSE. + "criticalAttributes" : + { + "contentType" : "application/vnd.cncf.notary.payload.v1+json", + // One of notary.default.x509 or notary.signingAuthority.x509 + "signingScheme" : "notary.default.x509", + // Value is always RFC 3339 formatted date time string + "expiry": "2022-10-06T07:01:20Z", + "extendedAttributes" : { + // Map of extended attributes + // Extended attributes values support JSON primitive values string, number and null. + "name" : primitive-value + } + }, + + // Array of names of critical attributes that plugin + // caller does not understand and does not intend to process. + "unprocessedAttributes" : [ ], + + // Certificate chain from signature envelope. + "certificateChain" : [ ] + }, + + "trustPolicy" : { + // Array of trusted identities as specified in + // trust policy. + "trustedIdentities": [], + // Array of verification checks to be performed by the plugin + "signatureVerification" : [ + "SIGNATURE_VERIFIER.TRUSTED_IDENTITY", + "SIGNATURE_VERIFIER.REVOCATION_CHECK" + ] + }, + + // Optional plugin configuration, map of string-string + "pluginConfig" : { } +} +``` + +The request can be extended in future by including other elements of signature envelope and trust policy as required to customize additional steps in verification. + +*Response* +All response attributes are required. + +```jsonc +{ + "verificationResults" : { + // Map of results. Key must match the set of + // verification capabilities + // in verify-signature request's trustPolicy.signatureVerification attribute. + // SIGNATURE_VERIFIER.TRUSTED_IDENTITY + // SIGNATURE_VERIFIER.REVOCATION_CHECK + "SIGNATURE_VERIFIER.TRUSTED_IDENTITY" : + { + // The result of a verification check + "success" : true | false, + // Reason for check being successful or not, + // required if value of success attribute is false. + "reason" : "" + }, + "SIGNATURE_VERIFIER.REVOCATION_CHECK" : + { + // The result of a verification check + "success" : true | false, + // Reason for check being successful or not, + // required if value of success attribute is false. + "reason" : "" + } + }, + // Array of strings containing critical attributes processed by the plugin. + "processedAttributes" : [ + ] +} +``` + +*verificationResults* : Verifications performed by the plugin. This is a map where the keys MUST match set of verify capabilities in `verify-signature` request's `trustPolicy.signatureVerification` attribute, and values are objects with following attributes + +* *success* (required): The `boolean` verification result. +* *reason* (optional): Reason associated with verification being successful or not, REQUIRED if value of success field is `false`. + +*processedAttributes* (required): Array of strings containing critical attributes processed by the plugin. Values must be one or more of attribute names in `verify-signature` request's `signature.unprocessedAttributes`. + +#### Error codes for *verify-signature* + +* VALIDATION_ERROR - Any of the required request fields was empty, or a value was malformed/invalid. +* UNSUPPORTED_CONTRACT_VERSION - The contract version used in the request is unsupported. +* ACCESS_DENIED - Authentication/authorization during signature verification, this may be due to external calls made by the plugin. +* TIMEOUT - The verification process timed out and can be retried by Notation. +* THROTTLED - The verification process was throttled and can be retried by Notation. +* ERROR - Any general error that does not fall into previous error categories. ## Threat Model