notation
notation copied to clipboard
Signature Format Feedback
I've had some more time to look over the signature format here: https://github.com/notaryproject/nv2/blob/prototype-1/docs/signature/README.md
Here's some initial feedback:
- The self-signed x509 usage (at least as documented) seems superfluous. A simple public key or JWK would suffice, without the x509 complexity.
- x509 could make it easier to attach into a large PKI, but I'm not sure which one or if that's in-scope here.
- I don't understand the
typ
andalg
fields.- x509 certs already contain their algorithm inside them, so that field seems redundant at best. I think that's a general criticism with the JOSE standards though.
- The specification declares that
typ
can either bex509
ortuf
- I don't understand whattuf
would do here.
The claims are fine, they look kind of like an OCI descriptor though. What if we just went all in and made it an OCI descriptor by nesting all the other fields under "annotations"?
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:c4516b8a311e85f1f2a60573abf4c6b740ca3ade4127e29b05616848de487d34",
"size": 528,
"annotations": {
"references": [
"registry.example.com/example:latest",
"registry.example.com/example:v1.0"
],
"exp": 1628587119,
"iat": 1597051119,
"nbf": 1597051119
}
}
For the x509 key usage - would code signing make sense instead of digital signature? https://www.globalsign.com/en/code-signing-certificate
The iat
claim seems weak: https://github.com/notaryproject/nv2/blob/prototype-1/docs/signature/README.md#claims, which is noted. Without a way to attest to the time of the signature (a trusted timestamp), the cert expiry date isn't really useful. Compromised keys could be used to sign artifacts at any time, even after the cert has expired.
I'm not sure I understand hte use-case for the nbf
claim.
Some other things I think I would prefer:
- take x509 out of the spec completely and just use a JWS in all it's glory. The x509 stuff is still compatible and can be used with the x5u/x5c/x5t stuff, but we'd also allow the other methods like
kid
orjwk
orjku
. It makes this spec smaller and more flexible. Implementations can be opinionated. - With that, I'm not sure this spec even needs to exist. The signature spec just becomes
jws
. The payload spec is what matters, and the OCI descriptor already exists and is flexible enough.
The type x509
generally refers to public keys of a digital signature scheme in the X.509 format. As you have mentioned, it could be as simple as just a public key in PKCS formats.
The cert does not contain the algorithm for an image signature. Technically, the private key associated with the public key in the cert can be used in whatever signature scheme, which supports the key type. For instances, a RSA key can be used in RSA + SHA256 or RSA + SHA512, and a EC key can be used in DSA or ElGamal or other advanced signature schemes. Having an alg
-like section can let the clients know whether they have the crypto suite available for verification.
For the annotations
section, I don't have a preference since they are all claims. BTW, I think extensions
may sound better than annotations
.
For the x509
key usage, it is a nice-to-have feature. Just as the section Standard vs. EV Code Signing Certificates states, it is more legit and is more friendly to those strict verifiers.
For the timestamp issue, it is related to the key management. Currently, we assume that the private key is kept secret and will be revoked if the key is lost. Also, you are welcome to contribute to how we should do key management.
The nbf
stands for not before and it is optional. It usually used for future signatures and dealing with clock skew.
For the last one, I agree that we could drop the outer spec and keep the payload spec. In fact, the first prototype of nv2
is not in JWT, and later we put the payload into the JWT form. As mentioned in the first sentence in the README, the signature spec is a JWT variant.
The cert does not contain the algorithm for an image signature. Technically, the private key associated with the public key in the cert can be used in whatever signature scheme, which supports the key type. For instances, a RSA key can be used in RSA + SHA256 or RSA + SHA512, and a EC key can be used in DSA or ElGamal or other advanced signature schemes. Having an
alg
-like section can let the clients know whether they have the crypto suite available for verification.
Ah thanks for clarifying! - I think it still introduces some potential for mismatch here like the HMAC attacks here: https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
Again, this is a criticism of the JWS/JWT specs though that and not really anything specific to nv2.
For the
x509
key usage, it is a nice-to-have feature. Just as the section Standard vs. EV Code Signing Certificates states, it is more legit and is more friendly to those strict verifiers.
If we go with x509, I think we would need to settle on a specific key usage to prevent protocol attacks (people getting a cert TLS Web Server Authentication
for a domain and then using it to codesign). I'm not sure what the best would be, Code Signing vs. Digital Signature.
For the timestamp issue, it is related to the key management. Currently, we assume that the private key is kept secret and will be revoked if the key is lost. Also, you are welcome to contribute to how we should do key management.
I'm not sure this is related to key management, but I also disagree with that being a safe assumption. Timestamping protocols are critical to using x509 in code signing. Some more info is here: https://en.wikipedia.org/wiki/Code_signing#Time-stamping
In short, there are two main flows for code signing:
- Certificates expire (in your example the lifetime is 365 days). Without timestamping, signature lifetime must be limited to the lifetime of the cert. When the cert expires, the signatures are no longer valid. This is fine and might be desirable for airgapped situations, but introduces a failure mode (all binaries are no longer valid and might fail to deploy) if someone forgets to rotate a cert.
- Timestamping protocols (RFC3161). More info is here: https://docs.microsoft.com/en-us/windows-hardware/drivers/install/authenticode. With a third-party timestamp attestation, you can trust signatures beyond the lifetime of the certificate. This is more robust to key rotation, and can also remove much of the need for revocation by allowing users to rely on shorter-lived certs. They introduce a dependency on another protocol, though.
But if the trust model assumes private keys are never leaked, then allowing certs to expire after 365 days doesn't really add value and only adds a potential failure point (if someone forgets to rotate).
I'd prefer to embrace short-lived certs with timestamps, which reduces the need for revocation (I've never seen or heard of revocation working well in practice anyway). For one recent point of reference, the Solarwinds Codesigning certs have still not been revoked: https://www.solarwinds.com/sa-overview/new-digital-certificate
Also, you are welcome to contribute to how we should do key management.
Thanks! I guess the fundamental disagreement here though is whether or not we should do key management at all as part of this specification. I would prefer not to and allow implementations to handle that as they see fit, rather than baking it into a specification.
For the last one, I agree that we could drop the outer spec and keep the payload spec. In fact, the first prototype of
nv2
is not in JWT, and later we put the payload into the JWT form. As mentioned in the first sentence in the README, the signature spec is a JWT variant.
This makes the most sense to me. Drop the outer spec, call it a JWT/JWS rather than a variant, and settle on the payload spec.
If there is a specific threat around misuse of a Web PKI / TLS certificate to generate one of these signatures then you'll probably want to enforce a code signing key use in certs. The digital signature key use often appears in TLS client/server certs. I've looked in the past and I've never been able to find any standard mapping between x509 key usage / extended key usage and JWS operations.
+1 on switching to a plain old JWS. Are you planning on supporting signatures using JWKs as well as x509 certs? You'll need to enforce key use there as well. There, the only standard key uses are enc
and sig
. There's no equivalent to x509 "code signing".
Regarding the alg
parameter: yea, it's different than the algorithm identifier in the x509 cert, which is specifying the algorithm the CA used to sign the cert. In this case we're specifying the aglorithm the cert holder is using to sign the JWS payload. That said, that alg
header is a big ole bag of worms...
The idea behind the alg
header is to provide "cryptographic agility". You can change / upgrade your signing algorithms and clients will continue working. This is a gigantic footgun, however, because it also allows an attacker to maliciously downgrade your algorithm to something unsafe / insecure. JWS/JWT went the extra mile by building in the degenerate worst-case scenario here with the "none" algorithm type. So if you write a naive JWS validator that simply looks at the header to find the alg
it's trivially easy to forge a "valid JWS" with someone else's pubkey/identity/cert.
Long story short, it's critical that the verifier not accept any ole algorithm. There are a few ways I've seen this addressed in practice:
- Profile JWS and standardize a fixed set of algorithms that you intend to support
- Distribute the algorithms along with the key material (e.g., signed over in your cert or publish in the
alg
property of your JWKs at a trusted source) - Distribute the algorithm in a protocol discovery document (e.g., see
id_token_signing_alg_values_supported
in OIDC's discovery doc)
Either way, if you're gonna use JWS I think a solution to this problem needs to be baked in and there should be well documented implementation guidance for anyone writing a verifier.
Regarding secure timestamp: it probably is necessary. The threat is that a compromised key can be used to sign an artifact with a "fake" timestamp that claims it was signed before compromise. You either need to stop trusting anything signed with a key after a compromise (which means signatures over old artifacts will no longer validate) or you need some authentic evidence that an artifact was signed before a compromise occurred. Secure timestamps are one way to do that. This is not strictly a key management issue since it involves the signed artifacts, not the keys themselves. I think it does require some thought as part of the protocol: it should either be solved or explicitly identified as out of scope / not a threat that the protocol intends to protect againts.
Hey,
Any interest in discussing any of this stuff further? Is there any kind of timeline or process for deciding on what the actual "notary v2 signature" format will be?
It's kind of hard to tell how firm these specs are while the descriptions are in prototype branches.
An attractive feature of x509 is it could allow signing with a CA signed certificate (not self-signed like the current examples). A private self signed CA could be used to sign leaf certificates, which make Notary signatures. If clients allowed pinning this CA, you could use it to bootstrap trust.
I also agree in with the other comments here though. You need to decide if you're actually doing x509 or not. What fields is the verifier responsible for checking (CN, usage, etc.)? What happens when the certificate is expired? Trusted timestamping is a solution, but I don't think there's a standardized way to integrate it with JWTs. A transparency log could be a solution instead of trusted timestamping, but that's probably out of scope.
Ephemeral certs could enable some safe key management workflows for automated signing in CI.
If the spec becomes just "use JWS", it may make sense to name what types of algorithms to support. x509 does enable some use cases that other algorithms do not.
+1 to all of that @bburky - you basically described the exact design we're trying for over at github.com/sigstore, for all artifact types.
I'm still really hoping we can settle on a standard signature format to work across tools, is there anything I can do to help move this stuff forward? Or any timeline to when we can start iterating on/firming up these things?
@dlorenc Yep, I'm loosely aware of sigstore. It seems like an interesting solution. Something along the lines of "sigstore without fulcio" might make sense for enterprise or CI environments. Enterprises likely have existing PKI infrastructure, and signing in CI would be a non-person entity doing the signing which maps poorly to OIDC.
Notary's half of that would be standardizing the signature format and ensuring x509 is a supported mode (possibly among other signature algorithms). Requiring trusted time-stamping (or something else). Defining what fields of a cert are checked (can I restrict a cert to some registry paths like example.com/foo/*:*
?). Defining what happens on cert expiration. Etc.
Exactly! The certificate semantics, timestamp stuff, payload format and signature format are what I'm hoping we can agree on here and reuse in multiple places.
@mmalone I don't believe there is a misuse issue around PKI certs vs signing certs (ie the security issues are largely around enc vs sign only, and the encryption keys for TLS are ephemeral only). This is why JWS only separates these. As a policy matter you should have keys for different functions for revocation etc reasons, but its not a critical misuse issue.
I took a stab at more formally documenting the signature, payload and OCI formats we're using in sigstore here: https://github.com/sigstore/cosign/pull/214
I'm still hoping we can converge on something. I think the biggest disconnect right now is that we're storing the signature/payload separately rather than encoding into a JWS/JWT.
I wanted to refresh the relevant links and then determine what questions still remain. Here are the signature specification updates:
- Notary signature specification
- https://github.com/notaryproject/notaryproject/pull/148
This issue is stale because it has been opened for 60 days with no activity. Remove stale label or comment. Otherwise, it will be closed in 30 days.
Issue closed due to no activity in the past 30 days.