From 8a09eb9ef7be82cfe92aae0e7d2169a1520e7972 Mon Sep 17 00:00:00 2001 From: Milind Gokarn Date: Thu, 21 Jul 2022 16:26:30 -0700 Subject: [PATCH] Support multiple trust stores and custom verification level (#164) * Support multiple trust stores and custom verification level * Simplified specifying custom verification level. * Fix custom verification level example * Changes `signatureVerification` to be an object instead of multi-typed attribute. - Updated example policies. Signed-off-by: Milind Gokarn --- signature-envelope-jws.md | 2 +- signature-specification.md | 4 +- trust-store-trust-policy-specification.md | 120 ++++++++++++++++------ 3 files changed, 89 insertions(+), 37 deletions(-) diff --git a/signature-envelope-jws.md b/signature-envelope-jws.md index 18ce30fe..3ee2031b 100644 --- a/signature-envelope-jws.md +++ b/signature-envelope-jws.md @@ -114,7 +114,7 @@ Notary v2 supports following unprotected headers: `timestamp`, `x5c` and `io.cnc ``` - **[`x5c`](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6)** (*array of strings*): This REQUIRED header contains the ordered list of X.509 certificate or certificate chain([RFC5280](https://datatracker.ietf.org/doc/html/rfc5280)) corresponding to the key used to digitally sign the JWS. The certificate chain is represented as a JSON array of certificate value strings, each string in the array is a base64-encoded DER certificate value. The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the first certificate, followed by the intermediate and root certificates in the correct order. Refer [*Certificate Chain* unsigned attribute](signature-specification.md#unsigned-attributes) for more details. -- **`io.cncf.notary.timestampSignature`** (*string*): This OPTIONAL header is used to store countersignature that provides trusted signing time. Only [RFC3161]([rfc3161](https://datatracker.ietf.org/doc/html/rfc3161#section-2.4.2)) compliant `TimeStampToken` are supported. +- **`io.cncf.notary.timestampSignature`** (*string*): This OPTIONAL header is used to store countersignature that provides authentic signing time. Only [RFC3161]([rfc3161](https://datatracker.ietf.org/doc/html/rfc3161#section-2.4.2)) compliant `TimeStampToken` are supported. - **TODO** Define the opaque datum (hash of envelope) that is sent to TSA, and how TSA response (time stamp token) is represented in this header. - **`io.cncf.notary.signingAgent`**(*string*): 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](signature-specification.md#unsigned-attributes) for more details. diff --git a/signature-specification.md b/signature-specification.md index 11bebdff..b27dd534 100644 --- a/signature-specification.md +++ b/signature-specification.md @@ -138,7 +138,7 @@ Notary v2 requires the signature envelope to support the following signed attrib 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). - **Certificate Chain**: This is a REQUIRED attribute that contains the ordered list of X.509 public certificates associated with the signing key used to generate the signature. The ordered list starts with the signing certificates, any intermediate certificates and ends with the root certificate. The certificate chain MUST be authenticated against a trust store as part of signature validation. Specific requirements for the certificates in the chain are provided [here](#certificate-requirements). -- **Timestamp signature** : An OPTIONAL counter signature which provides [trusted timestamp](#signing-time)e.g. Time Stamp Authority (TSA) generated timestamp signature. Only [RFC3161](ietf-rfc3161) compliant TimeStampToken are currently supported. +- **Timestamp signature** : An OPTIONAL counter signature which provides [authentic timestamp](#signing-time)e.g. Time Stamp Authority (TSA) generated timestamp signature. Only [RFC3161](ietf-rfc3161) compliant TimeStampToken are currently supported. - **Signing Agent**: An OPTIONAL claim that provides the identifier of the software (e.g. Notation) that produced the signature on behalf of the user. It is an opaque string set by the software that produces the signature. It's intended primarily for diagnostic and troubleshooting purposes, this attribute is unsigned, the verifier MUST NOT validate formatting, or fail validation based on the content of this claim. The suggested format is one or more tokens of the form `{id}/{version}` containing identifier and version of the software, seperated by spaces. E.g. “notation/1.0.0”, “notation/1.0.0 com.example.nv2plugin/0.8”. ## Signature Algorithm Requirements @@ -222,7 +222,7 @@ For [JWS JSON serialization](./signature-envelope-jwt.md) signature envelope, ve ### Signing time -The signing time denotes the time at which the signature was generated. A X509 certificate has a defined [validity](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5) during which it can be used to generate signatures. The signing time must be greater than or equal to certificate's `notBefore` attribute, and signing time must be less than or equal to certificate's `notAfter` attribute. Signatures generated after the certificate expires are considered invalid. A trusted timestamp, like TSA countersignature, allows a verifier to determine if the signature was generated when the certificate was valid. It also allows a verifier to determine if a signature be treated as valid when a certificate is revoked, if the certificate was revoked after the signature was generated. In the absence of a trusted timestamp, signatures are considered invalid after certificate expires, and all signatures are considered revoked when a certificate is revoked. +The signing time denotes the time at which the signature was generated. A X509 certificate has a defined [validity](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5) during which it can be used to generate signatures. The signing time must be greater than or equal to certificate's `notBefore` attribute, and signing time must be less than or equal to certificate's `notAfter` attribute. Signatures generated after the certificate expires are considered invalid. An authentic timestamp, like TSA countersignature, allows a verifier to determine if the signature was generated when the certificate was valid. It also allows a verifier to determine if a signature be treated as valid when a certificate is revoked, if the certificate was revoked after the signature was generated. In the absence of an authentic timestamp, signatures are considered invalid after certificate expires, and all signatures are considered revoked when a certificate is revoked. ### Expiry diff --git a/trust-store-trust-policy-specification.md b/trust-store-trust-policy-specification.md index 85068334..af8625f5 100644 --- a/trust-store-trust-policy-specification.md +++ b/trust-store-trust-policy-specification.md @@ -38,6 +38,8 @@ $XDG_CONFIG_HOME/notation/trust-store cert2.pem /sub-dir # sub directory is ignored cert-3.pem # certs under sub directory is ignored + /acme-rockets-ca2 + cert1.pem /wabbit-networks cert3.crt /tsa @@ -68,11 +70,13 @@ Trust policy for a simple scenario where ACME Rockets uses only artifacts signed "trustPolicies": [ { // Policy for all artifacts, from any registry location. - "name": "wabbit-networks-images", - "registryScopes": [ "*" ], - "signatureVerification" : "audit", - "trustStore": "ca:acme-rockets", - "trustedIdentities": [ + "name": "wabbit-networks-images", // Name of the policy. + "registryScopes": [ "*" ], // The registry artifacts to which the policy applies. + "signatureVerification": { // The level of verification - strict, permissive, audit, skip. + "level" : "audit" + }, + "trustStores": ["ca:acme-rockets"], // The trust stores that contains the X.509 trusted roots. + "trustedIdentities": [ // Identities that are trusted to sign the artifact. "x509.subject: C=US, ST=WA, L=Seattle, O=acme-rockets.io, OU=Finance, CN=SecureBuilder" ] } @@ -80,7 +84,7 @@ Trust policy for a simple scenario where ACME Rockets uses only artifacts signed } ``` -Trust policy for the scenario where ACME Rockets uses artifacts signed by themselves, and signed and unsigned artifacts from Wabbit Networks: +Trust policy for the scenario where ACME Rockets uses some artifacts signed by Wabbit Networks and some signed by ACME Rockets. ```jsonc { @@ -94,8 +98,10 @@ Trust policy for the scenario where ACME Rockets uses artifacts signed by themse "registry.acme-rockets.io/software/net-monitor", "registry.acme-rockets.io/software/net-logger" ], - "signatureVerification" : "strict", - "trustStore": "ca:acme-rockets", + "signatureVerification": { + "level" : "strict" + }, + "trustStores": ["wabbit-networks"], "trustedIdentities": [ "x509.subject: C=US, ST=WA, L=Seattle, O=wabbit-networks.io, OU=Security Tools" ] @@ -105,30 +111,34 @@ Trust policy for the scenario where ACME Rockets uses artifacts signed by themse // Wabbit Networks repository "name": "unsigned-image", "registryScopes": [ "registry.wabbit-networks.io/software/unsigned/net-utils" ], - "signatureVerification": "skip", + "signatureVerification": { + "level" : "skip" + } }, { - // Policy for with custom verification policy + // Policy that uses custom verification level to relax the strict verification. + // It logs expiry and skips recovocation check for a specific artifact. "name": "use-expired-image", "registryScopes": [ "registry.acme-rockets.io/software/legacy/metrics" ], - "signatureVerification": - { + "signatureVerification": { "level" : "strict", - "override" : - { - "expiry" : "skip" + "override" : { + "expiry" : "log", + "revocation" : "skip" } }, - "trustStore": "ca:acme-rockets", + "trustStores": ["ca:acme-rockets"], "trustedIdentities": ["*"] }, { // Policy for all other artifacts signed by ACME Rockets - // from any registry location + // from any registry location. The policy also specified multiple trust stores. "name": "global-policy-for-all-other-images", "registryScopes": [ "*" ], - "signatureVerification" : "audit", - "trustStore": "ca:acme-rockets", + "signatureVerification": { + "level" : "audit" + }, + "trustStores": ["ca:acme-rockets", "ca:acme-rockets-ca2"], "trustedIdentities": [ "x509.subject: C=US, ST=WA, L=Seattle, O=acme-rockets.io, OU=Finance, CN=SecureBuilder" ] @@ -146,37 +156,79 @@ Trust policy for the scenario where ACME Rockets uses artifacts signed by themse - **`registryScopes`**(*array of strings*): This REQUIRED property determines which trust policy is applicable for the given artifact. The scope field supports filtering based on fully qualified repository URI `${registry-name}/${namespace}/${repository-name}`. For more information, see [registry scopes constraints](#registry-scopes-constraints) section. - - **`signatureVerification`**(*string*): This REQUIRED property dictates how signature verification is performed. Supported values are `strict`, `permissive`, `audit` and `skip`. Detailed explaination of each value is present [here](#signatureverification-details). A custom level can be defined by referencing a supported level value, and overriding individual validation checks. NOTE: Custom signature verification level will not be supported in `notation` RC1. - - **`trustStore`**(*string*): This REQUIRED property specifies a named trust store. Uses the format `{trust-store-type}:{named-store}`. Currently supported values for `trust-store-type` are `ca` and `tsa`. **NOTE**: When support for publicly trusted TSA is available, `tsa:publicly-trusted-tsa` is the default value, and implied without explicitly specifying it. If a custom TSA is used the format `ca:acme-rockets,tsa:acme-tsa` is supported to specify it. + - **`signatureVerification`**(*object*): This REQUIRED property dictates how signature verification is performed. + An *object* that specifies a predefined verification level, with an option to override Notary v2 defined verification level if user wants to specify a [custom verification level](#custom-verification-level). + - **`level` **(*string*): A REQUIRED property that specifies the verification level, supported values are `strict`, `permissive`, `audit` and `skip`. Detailed explaination of each level is present [here](#signatureverification-details). + - **`override` **(*map of string-string*): This OPTIONAL map is used to specify a [custom verification level](#custom-verification-level). + - **`trustStores`**(*array of string*): This REQUIRED property specifies a set of one or more named trust stores, each of which contain the trusted roots against which signatures are verified. Each named trust store uses the format `{trust-store-type}:{named-store}`. Currently supported values for `trust-store-type` are `ca` and `tsa`. + - **NOTE**: When support for publicly trusted TSA is available, `tsa:publicly-trusted-tsa` is the default value, and implied without explictly specifying it. If a custom TSA is used the format `ca:acme-rockets,tsa:acme-tsa` is supported to specify it. - **`trustedIdentities`**(*array of strings*): This REQUIRED property specifies a set of identities that the user trusts. For X.509 PKI, it supports list of elements/attributes of the signing certificate's subject. For more information, see [identities constraints](#trusted-identities-constraints) section. A value `*` is supported if user trusts any identity (signing certificate) issued by the CA(s) in `trustStore`. #### Signature Verification details -Notary v2 defines the following signature verification levels, to provide different levels of enforcement for different scenarios. Signature verification is a multi step process performs the following validation checks - integrity (artifact is unaltered, signature is not corrupted), authenticity (the signature is really from the identity that claims to have signed it), trusted timestamping (the signature was generated when the key/certificate were unexpired), expiry (an optional check if the artifact specifies an expiry time), revocation check (is the signing identity still trusted at the present time). Based on the signature verification level, each of these validation checks is `enforced` or `logged`. If a validation check is `enforced`, a failure is treated as critical failure, and causes the overall signature verification to fail. If a validation check is `logged`, a failure causes details to be logged, and the next validation check is evaluated till all checks succeed or a critical failure is encountered. NOTE: Implementations may change the ordering of these checks based on efficiency, but all validation checks MUST to be performed till the first critical failure is encountered, or all checks succeed, for signature verification process to be considered complete. - -- `strict` : Signature verification is performed at `strict` level, which enforces all of the signature verification checks. If any of these checks fails, the signature verification fails. This is the recommended level in environments where a signature verification failure does not have high impact to other concerns (like application availability). It is recommended that build and development environments where images are initially injested, or at high assurance at deploy time use `strict` level. -- `permissive` : The `permissive` level enforces most signature verification checks, but will only logs failures for revocation and expiry. The `permissive` level is recommended to be used if signature verification is done at deploy time or runtime, and the user only needs integrity and authenticity guarantees. -- `audit` : The `audit` level only enforces integrity check if a signature is present. Failure of all other checks are only logged. + - Signature verification is a multi step process performs the following validations + - integrity (artifact is unaltered, signature is not corrupted) + - authenticity (the signature is really from the identity that claims to have signed it) + - trusted timestamping (the signature was generated when the key/certificate were unexpired) + - expiry (an optional check if the artifact specifies an expiry time) + - revocation check (is the signing identity still trusted at the present time). + - Based on the signature verification level, each of these validations is *enforced* or *logged*. + - If a validation is *enforced*, a failure is treated as critical failure, and causes the overall signature verification to fail. + - If a validation is *logged*, a failure causes details to be logged, and the next validation is evaluated till all validations succeed or a critical failure is encountered. +- Implementations may change the ordering of these validations based on efficiency, but all validation MUST be performed till the first critical failure is encountered, or all validation succeed, for the overall signature verification process to be considered complete. + + Notary v2 defines the following signature verification levels to provide different levels of enforcement for different scenarios. + +- `strict` : Signature verification is performed at `strict` level, which enforces all validations. If any of these validations fail, the signature verification fails. This is the recommended level in environments where a signature verification failure does not have high impact to other concerns (like application availability). It is recommended that build and development environments where images are initially injested, or for high assurance at deploy time use `strict` level. +- `permissive` : The `permissive` level enforces most validations, but will only logs failures for revocation and expiry. The `permissive` level is recommended to be used if signature verification is done at deploy time or runtime, and the user only needs integrity and authenticity guarantees. +- `audit` : The `audit` level only enforces signature integrity if a signature is present. Failure of all other validations are only logged. - `skip` : The `skip` level does not fetch signatures for artifacts and does not perform any signature verification. This is useful when an application uses multiple artifacts, and has a mix of signed and unsigned artifacts. Note that `skip` cannot be used with a global scope (`*`), the value of `registryScopes` MUST contain fully qualified registry URL(s). -The following table shows the resultant behavior `enforced` (verification fails), or `logged` for each of the checks, based on signature verification level. +The following table shows the resultant validation action, either *enforced* (verification fails), or *logged* for each of the checks, based on signature verification level. -|Signature Verification Level|Recommended Usage|Integrity|Authenticity|Trusted timestamp|Expiry|Revocation check| +|Signature Verification Level|Recommended Usage|||Validations||| |----------------------------|-----------------|---------|------------|-----------------|------|----------------| -|strict |Use at development, build and deploy time|enforced|enforced|enforced|enforced|enforced| -|permissive|Use at deploy time or runtime|enforced|enforced|logged|logged|logged| -|audit |Use when adopting signed images, without breaking existing workflows|enforced|logged|logged|logged|logged| -|skip |Use to exclude verification for unsigned images|skipped|skipped|skipped|skipped|skipped| +|||*Integrity*|*Authenticity*|*Authentic timestamp*|*Expiry*|*Revocation check*| +|*strict* |Use at development, build and deploy time|enforced|enforced|enforced|enforced|enforced| +|*permissive*|Use at deploy time or runtime|enforced|enforced|logged|logged|logged| +|*audit* |Use when adopting signed images, without breaking existing workflows|enforced|logged|logged|logged|logged| +|*skip* |Use to exclude verification for unsigned images|skipped|skipped|skipped|skipped|skipped| **Integrity** : Guarantees that the artifact wasn't altered after it was signed, or the signature isn't corrupted. All signature verification levels always enforce integrity. **Authenticity** : Guarantees that the artifact was signed by an identity trusted by the verifier. Its definition does not include revocation, which is when a trusted identity is subsequently untrusted because of a compromise. -**Trusted timestamp** : Guarantees that the signature was generated when the certificate was valid. It also allows a verifier to determine if a signature be treated as valid when a certificate is revoked, if the certificate was revoked after the signature was generated. In the absence of a trusted timestamp, signatures are considered invalid after certificate expires, and all signatures are considered revoked when a certificate is revoked. **NOTE**: `notation` RC1 will generate trusted timestamp using a TSA when the signature is generated, but will not support verification of TSA countersignatures. +**Authentic timestamp** : Guarantees that the signature was generated when the certificate was valid. It also allows a verifier to determine if a signature must be treated as valid when a certificate is revoked, if the certificate was revoked after the signature was generated. In the absence of a authentic timestamp, signatures are considered invalid after certificate expires, and all signatures are considered revoked when a certificate is revoked. + +- **NOTE**: `notation` RC1 will generate trusted timestamp using a TSA when the signature is generated, but will not support verification of TSA countersignatures. Related issue - [#59](https://github.com/notaryproject/roadmap/issues/59). **Expiry** : This is an optional feature that guarantees that artifact is within “best by use” date indicated in the signature. 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. -**Revocation check** : Guarantees that the signing identity is still trusted at signature verification time. Events such as key or system compromise can make a signing identity that was previously trusted, to be subsequently untrusted. This guarantee typically requires a verification-time call to an external system, which may not be consistently reliable. The `permissive` verification level only logs failures of revocation check and does not enforce it. If a particular revocation mechanism is reliable, use `strict` verification level instead. **NOTE** `notation` RC1 will not support revocation check. +**Revocation check** : Guarantees that the signing identity is still trusted at signature verification time. Events such as key or system compromise can make a signing identity that was previously trusted, to be subsequently untrusted. This guarantee typically requires a verification-time call to an external system, which may not be consistently reliable. The `permissive` verification level only logs failures of revocation check and does not enforce it. If a particular revocation mechanism is reliable, use `strict` verification level instead. + +- **NOTE** : `notation` RC1 will not have in built support for Revocation Check feature, see [Key Management - Rescinding Signatures (CRL)](https://github.com/notaryproject/notaryproject/issues/72) for details. When this feature is subsequently implemented, the `strict` verification level will **enforce** this validation, and will fail signatures which would previously pass signature verification when revocation checks were not supported. This is considered a **breaking change**, but is the appropriate behavior for a `strict` verification level. + +#### Custom Verification Level + +Signature verification levels provide Notary v2 defined behavior for each validation e.g. `strict` will always *enforce* authenticity validation. For fine grained control over validations that occur during signature verification, users can define a custom level which overrides the behavior of an existing verification level. + +- To use this feature, the `level` property MUST be specified along with an OPTIONAL `override` map. +- Supported values for `level` are - `strict`, `permissive` and `audit`. A `skip` level cannot be customized. +- Supported keys for `override` map and their supported values are as follows. + - `integrity` validation cannot be overriden, and therefore cannot be specified as a key. + - `authenticity` - Supported values are `enforce` and `log`. + - `authenticTimestamp` - Supported values are `enforce` and `log`. + - `expiry` - Supported values are `enforce` and `log`. + - `revocation` - Supported values are `enforce`, `log`, and `skip`. + +```jsonc + "signatureVerification": { + "level" : "strict", + "override" : { + "expiry" : "log" + } + } +``` #### Registry Scopes Constraints