ML-DSA signing scheme for TUF metadata#195
Conversation
Signed-off-by: Fredrik Skogman <kommendorkapten@github.com>
| limitations on message size, and potential interface constraints that | ||
| make pure mode ML-DSA unsuitable for some TUF deployments. | ||
|
|
||
| # Specification |
There was a problem hiding this comment.
I was a bit confused until I got to the "TUF metadata parameters" section about where this fit with TUF. Maybe you could move that sub-section earlier
| ``` | ||
| 0x74 || 0x75 || 0x66 || version || H(MSG) | ||
| ``` | ||
| 5. Sign the pre-signing byte string using an empty context |
There was a problem hiding this comment.
Does it make sense for us to include advice around where to do the signing? (Like do we suggest using a hardware device?) I'm not sure if this makes sense to include, but maybe we can include a link or something to guide implementers in the right direction
There was a problem hiding this comment.
I think this should be out of scope for the TAP, as the decision to use an HSM vs SK vs KMS vs raw key is based on the integrating project's threat model.
Maybe as a separate TAP if this guidance doesn't exist?
There was a problem hiding this comment.
I think we need to sit tight here on any recommendation. I immediately see two different paths in the future:
- Root signing like Sigstore does: HSMs, like YubiKey (assuming they release one soon that supports ML-DSA)
- KMS for online signing of eg. targets.
So I think this TAP benefits from just defining the signing scheme.
mnm678
left a comment
There was a problem hiding this comment.
I left a few minor comments, but overall I think this is a great scheme, and addresses some of the performance concerns around ML-DSA
| 0x74 || 0x75 || 0x66 || <version> || H(MSG) | ||
| ``` | ||
|
|
||
| The domain separators are the ASCII codes for `tuf`. |
| ## Signature generation | ||
|
|
||
| 1. Load the public key from TUF metadata | ||
| 2. Parse the version from the public key's `scheme` and prepare the |
There was a problem hiding this comment.
We could change H to SHA-512 since it's fixed? I think this was from an earlier revision when H was variable.
There was a problem hiding this comment.
My thinking of keeping it is that we still have the hash decided per the version of the protocol. But you are right that for v1 only SHA-512 is allowed. For keeping the spec generic of the version I think it makes sense to keep it as is.
| ``` | ||
| 0x74 || 0x75 || 0x66 || version || H(MSG) | ||
| ``` | ||
| 5. Sign the pre-signing byte string using an empty context |
There was a problem hiding this comment.
I think this should be out of scope for the TAP, as the decision to use an HSM vs SK vs KMS vs raw key is based on the integrating project's threat model.
Maybe as a separate TAP if this guidance doesn't exist?
| * Version byte: `0x01` | ||
| * Hash algorithm: SHA-512 | ||
| * Implementations MUST support | ||
| * `ML-DSA-44` |
There was a problem hiding this comment.
Should we explicitly state the valid KEYTYPEs and SCHEMEs? All lower case to match https://theupdateframework.github.io/specification/latest/#file-formats-keys or upper case?
There was a problem hiding this comment.
ah you do below...maybe that should be higher?
There was a problem hiding this comment.
Yes, @mnm678 had a few similar comments to I'll shuffle things around so more TUF details are in the beginning, I think that will make it easier to read.
| 2. Load up the public key for verification | ||
| 3. Parse `scheme` into parameter set and version | ||
| * Reject if the protocol version is not supported | ||
| * Implementations MUST NOT infer or select an ML-DSA |
There was a problem hiding this comment.
Should implementations verify the length of the public key and/or signature based on the ML-DSA parameter set? Maybe this is already handled by the underlying library?
There was a problem hiding this comment.
That should be managed by the underlying library, and I rather not see any TUF related code try to delve into analyzing the signature/pk size. I clarify this.
Signed-off-by: Fredrik Skogman <kommendorkapten@github.com>
Signed-off-by: Fredrik Skogman <kommendorkapten@github.com>
Signed-off-by: Fredrik Skogman <kommendorkapten@github.com>
|
Thanks for the feedback ❤️ |
TAP 21 is in active draft (PR theupdateframework/taps#195) and adds an ml-dsa keytype with FIPS 204 parameter sets plus an application-level pre-hashing protocol so HSMs can sign large TUF metadata in pure mode. Also bump README counts from 14 to 15 toggleable TAPs.
jku
left a comment
There was a problem hiding this comment.
Looks good to me. Left one question
| The hash function and the canonicalization scheme for the message are | ||
| specified by the version. | ||
|
|
||
| Pure ML-DSA MUST be used with an **empty context**. |
There was a problem hiding this comment.
Out of curiosity, what is the reason for not using context=0x74 || 0x75 || 0x66 || version but instead building the whole pre-signing string ourselves?
There was a problem hiding this comment.
is this what your comment about "μ" is about? I could not follow that one.
There was a problem hiding this comment.
The ML-DSA Sign function is roughly defined as Sign(private_key, msg, context) where it internally computes a hash μ and then calls SignInternal(private_key, μ) (this is a gross over-simplification though). So this means that a compliant ML-DSA can split the internal pre-hashing so that e.g. an HSM can compute the hash client side and only send a smaller payload over the wire. OpenSSL 4.0 exposes this API so clients can use this functionality directly. OpenSSL calls this "ML-DSA-MU". It's not unlikely other crypto libs will expose similar APIs so that's why I added it to the rationale on why we are not using that approach.
Putting the domain separation in the context would yield the same result, but we would still have to define the pre-signing schema (hashing and canonical message representation) so I figure it's just easier to make it into a single input over spreading it onto two fields.
This is related to proposed TAP: theupdateframework/taps#195 This currently enables the key for verification and signing for easier testing: In reality we may want to leave the verify support disabled until the TAP is approved. This makes cryptography 48 a requirement * we could make this more complicated with a separate feature but I'm not going to unless someone has a good reason for it * ML-DSA support was added in 47 already but support via openssl only became available in 48 Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
This is related to proposed TAP: theupdateframework/taps#195 This currently enables the key for verification and signing for easier testing: In reality we may want to leave the verify support disabled until the TAP is approved. This makes cryptography 48 a requirement * we could make this more complicated with a separate feature but I'm not going to unless someone has a good reason for it * ML-DSA support was added in 47 already but support via openssl only became available in 48 Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
Wrote up a TAP on how to encode ML-DSA keys when used within TUF and added a section on pre-auth encoding as certain targets can be fairly larger, and ML-DSA operates in pure mode, that is, no pre-hashing which is the case for RSA and ECDSA.