From bf24775a4d62a9073d57760bd535e48c9ce91f0a Mon Sep 17 00:00:00 2001 From: Sean Turner Date: Tue, 14 Jan 2025 09:45:01 -0500 Subject: [PATCH] ExternalMu Shuffle (#65) * ExternalMu Shuffle Moved " Pre-hash Mode" section to an Appendix. There are editorial tweaks, but more importantly 2119 language is removed from the Appendix. I want to call attention to the four (4) 2119 language changes: * reworked some of this into Security Considerations: This specification uses exclusively ExternalMu-ML-DSA for pre-hashed use cases, and thus HashML-DSA as defined in [FIPS204] and identified by `id-hash-ml-dsa-44-with-sha512`, `id-hash-ml-dsa-65-with-sha512`, and `id-hash-ml-dsa-87-with-sha512` MUST NOT be used in X.509 and related PKIX protocols. * Implementions are RECOMMENDED -> whole paragraph re-written. * An ML-DSA key and certificate [MAY->can] be used with either ML-DSA or ExternalMu-ML-DSA interchangeably. * Implementors [SHOULD->should] to pay careful attention to how the public key or its hash is delivered to the `ExternalMu-ML-DSA.Prehash()` routine, and from where they are sourcing this data. * fixing ref anchor * fixing ref anchor * unlikely discovery * simple->direct * fix typo Co-authored-by: Mike Ounsworth * streamline * remove repeated HashML-DSA considerations * remove second * Adding reference to the section with the rationale why not HashML-DSA (#69) * Apply suggestions from code review * Swapping order security<->implementation reasons --------- Co-authored-by: Mike Ounsworth Co-authored-by: Panos K. --- draft-ietf-lamps-dilithium-certificates.md | 230 +++++++++++++-------- 1 file changed, 144 insertions(+), 86 deletions(-) diff --git a/draft-ietf-lamps-dilithium-certificates.md b/draft-ietf-lamps-dilithium-certificates.md index 1cc848b..c2ae3a1 100644 --- a/draft-ietf-lamps-dilithium-certificates.md +++ b/draft-ietf-lamps-dilithium-certificates.md @@ -162,6 +162,7 @@ levels: ML-DSA-44, ML-DSA-65, and ML-DSA-87. {{FIPS204}} defines two variants of ML-DSA: a pure and a prehash variant. Only the former is specified in this document. +See {{sec-disallow-hash}} for the rationale. The pure variant of ML-DSA supports the typical prehash flow, see {{prehash}}. In short: one cryptographic module can compute the hash *mu* on line 6 of algorithm 7 of {{FIPS204}} and pass it to a second module @@ -396,92 +397,6 @@ in this section. {{examples}} contains example ML-DSA private keys encoded using the textual encoding defined in {{RFC7468}}. -# Pre-hashing (ExternalMu-ML-DSA) {#prehash} - -Some applications require prehashing, where the signature generation -process can be separated into a pre-hash step and a core signature -step in order to ease operational requirements around large or -inconsistently-sized payloads. This can be performed at the -protocol layer, but not all protocols support it. -Examples in [RFC5280] are certificate and certificate revocation list -(CRL) data structures, that do not include message digesting before signing. -This can make signing large CRLs or a high volume of certificates -with large public keys challenging. - -As mentioned in the introduction, pure ML-DSA signing itself -supports a prehashing flow by splitting the operation over two -modules. In this section we make this "ExternalMu-ML-DSA" -more explicit. - -There are two steps. First an `ExternalMu-ML-DSA.Prehash()` -followed by `ExternalMu-ML-DSA.Sign()`. Together these are functionally -equivalent to `ML-DSA.Sign()` from [FIPS204] in that they create -exactly the same signatures as regular pure ML-DSA, which can be -verified by the unmodified `ML-DSA.Verify()`. - -An ML-DSA key and certificate MAY be used with either ML-DSA -or ExternalMu-ML-DSA interchangeably. -Note that ExternalMu-ML-DSA describes a different signature API from ML-DSA -and therefore might require explicit support from hardware or -software cryptographic modules. - -Note that the signing mode defined here is different from HashML-DSA -defined in [FIPS204] section 5.4. This specification uses exclusively -ExternalMu-ML-DSA for pre-hashed use cases, and thus public -keys identified by `id-hash-ml-dsa-44-with-sha512`, -`id-hash-ml-dsa-65-with-sha512`, and `id-hash-ml-dsa-87-with-sha512` -MUST NOT be used in X.509 and related PKIX protocols with the -exception of the Public Key in end-entity X.509 certifacates. -Such public keys could be used beyond PKIX. - -All functions and notation used in {{fig-externalmu-ml-dsa-external}} -and {{fig-externalmu-ml-dsa-internal}} are defined in [FIPS204]. - -External operations: - -~~~ -ExternalMu-ML-DSA.Prehash(pk, M, ctx): - - if |ctx| > 255 then - return error # return an error indication if the context string is - # too long - end if - - M' = BytesToBits(IntegerToBytes(0, 1) ∥ IntegerToBytes(|ctx|, 1) - || ctx) || M - mu = H(BytesToBits(H(pk, 64)) || M', 64) - return mu -~~~ -{: #fig-externalmu-ml-dsa-external title="External steps of ExternalMu-ML-DSA"} - - - -Internal operations: - -~~~ -ExternalMu-ML-DSA.Sign(sk, mu): - - if |mu| != 512 then - return error # return an error indication if the input mu is not - # 64 bytes (512 bits). - end if - - rnd = rand(32) # for the optional deterministic variant, - # set rnd to all zeroes - if rnd = NULL then - return error # return an error indication if random bit - # generation failed - end if - - sigma = ExternalMu-ML-DSA.Sign_internal(sk, mu, rnd) - return sigma - - -ExternalMu-ML-DSA.Sign_internal(sk, mu, rnd): # mu is passed as argument instead of M' - ... identical to FIPS 204 Algorithm 7, but with Line 6 removed. -~~~ -{: #fig-externalmu-ml-dsa-internal title="Internal steps of ExternalMu-ML-DSA"} - # IANA Considerations For the ASN.1 module in {{asn1}}, IANA is requested to assign an object @@ -572,6 +487,51 @@ key serialization. It for this reason the public key structure defined in {{ML-DSA-PubblicKey}} is intentionally encoded as a single OCTET STRING. +## Rationale for disallowing HashML-DSA {#sec-disallow-hash} + +The HashML-DSA mode defined in Section 5.4 of {{FIPS204}} MUST NOT be +used; in other words, public keys identified by +`id-hash-ml-dsa-44-with-sha512`, `id-hash-ml-dsa-65-with-sha512`, and +`id-hash-ml-dsa-87-with-sha512` MUST NOT be in X.509 certificates used for +CRLs, OCSP, certificate issuance and related PKIX protocols (e.g. TLS). +The use of HashML-DSA public keys within end entity certificates is not +prohibited, but conventions for doing so are outside the scope of this +document. + +This restriction is for both implementation and security reasons. + +The implementation reason for disallowing HashML-DSA stems from the fact +that ML-DSA and HashML-DSA are incompatible algorithms that require +different `Verify()` routines. This forwards to the protocol the +complexity of informing the client whether to use `ML-DSA.Verify()` or +`HashML-DSA.Verify()` along with the hash algorithm to use. Additionally, since +the same OIDs are used to identify the ML-DSA +public keys and ML-DSA signature algorithms, an implementation would +need to commit a given public key to be either of type `ML-DSA` or +`HashML-DSA` at the time of certificate creation. This is anticipated +to cause operational issues in contexts where the operator does not +know at key generation time whether the key will need to produce pure +or pre-hashed signatures. ExternalMu-ML-DSA avoids all of these +operational concerns by virtue of having keys and signatures that are +indistinguishable from ML-DSA (i.e., ML-DSA and ExternalMu-ML-DSA are +mathematically equivalent algorithms). The difference between ML-DSA +and ExternalMu-ML-DSA is merely an internal implementation detail of +the signer and has no impact on the verifier or network protocol. + +The security reason for disallowing HashML-DSA is that the design of the +ML-DSA algorithm provides enhanced resistance against signature +collision attacks, compared with conventional RSA or ECDSA signature +algorithms. Specifically, ML-DSA binds the hash of the public key `tr` +to the message to-be-signed prior to hashing, as described in line 6 of +Algorithm 7 of {{FIPS204}}. In practice, this provides binding to the +indended verification public key, preventing some attacks that would +otherwise allow a signature to be successfully verified against a +non-intended public key. Also, this unlikely, theoretical binding means that in the unlikely +discovery of a collision attack against SHA-3, an attacker would +have to perform a public-key-specific collision search in order to find +message pairs such that `H(tr || m1) = H(tr || m2)` since a direct hash +collision `H(m1) = H(m2)` will not suffice. HashML-DSA removes both of +these enhanced security properties. --- back @@ -742,6 +702,104 @@ previous section. {::include ./examples/ML-DSA-44.crt.txt} ~~~ +# Pre-hashing (ExternalMu-ML-DSA) {#prehash} + +Some applications require pre-hashing, where the signature generation +process can be separated into a pre-hash step and a core signature +step in order to ease operational requirements around large or +inconsistently-sized payloads. Pre-hashing can be performed at the +protocol layer, but not all protocols support it. Examples in +{{RFC5280}} are certificates and CRLs; these do not include message +digesting before signing. This can make signing large CRLs or a high +volume of certificates with large public keys challenging. + +As mentioned in the introduction, pure ML-DSA signing itself +supports a pre-hashing flow by splitting the operation over two +modules. In this section, we make this "ExternalMu-ML-DSA" +more explicit. + +There are two steps. First an `ExternalMu-ML-DSA.Prehash()` +followed by `ExternalMu-ML-DSA.Sign()`. Together these are functionally +equivalent to `ML-DSA.Sign()` from {{FIPS204}} in that used in sequence +they create exactly the same signatures as regular pure ML-DSA, which +can be verified by the unmodified `ML-DSA.Verify()`. + +An ML-DSA key and certificate can be used with either ML-DSA +or ExternalMu-ML-DSA interchangeably. +Note that ExternalMu-ML-DSA describes a different signature API from ML-DSA +and therefore might require explicit support from hardware or +software cryptographic modules. + +Note that the signing mode defined here is different from HashML-DSA +defined in Section 5.4 of {{FIPS204}}. This specification uses exclusively +ExternalMu-ML-DSA for pre-hashed use cases. See {{sec-disallow-hash}} for +additional discussion of why HashML-DSA is disallowed in PKIX. + +All functions and notation used in {{fig-externalmu-ml-dsa-external}} +and {{fig-externalmu-ml-dsa-internal}} are defined in {{FIPS204}}. + +External operations: + +~~~ +ExternalMu-ML-DSA.Prehash(pk, M, ctx): + + if |ctx| > 255 then + return error # return an error indication if the context string is + # too long + end if + + M' = BytesToBits(IntegerToBytes(0, 1) ∥ IntegerToBytes(|ctx|, 1) + || ctx) || M + mu = H(BytesToBits(H(pk, 64)) || M', 64) + return mu +~~~ +{: #fig-externalmu-ml-dsa-external title="External steps of ExternalMu-ML-DSA"} + +Internal operations: + +~~~ +ExternalMu-ML-DSA.Sign(sk, mu): + + if |mu| != 512 then + return error # return an error indication if the input mu is not + # 64 bytes (512 bits). + end if + + rnd = rand(32) # for the optional deterministic variant, + # set rnd to all zeroes + if rnd = NULL then + return error # return an error indication if random bit + # generation failed + end if + + sigma = ExternalMu-ML-DSA.Sign_internal(sk, mu, rnd) + return sigma + +ExternalMu-ML-DSA.Sign_internal(sk, mu, rnd): # mu is passed as argument instead of M' + ... identical to FIPS 204 Algorithm 7, but with Line 6 removed. +~~~ +{: #fig-externalmu-ml-dsa-internal title="Internal steps of ExternalMu-ML-DSA"} + +ExternalMu-ML-DSA requires the public key, or its prehash, as input to +the pre-digesting function. This assumes the signer generating the +pre-hash is in possession of the public key before signing and is +different from conventional pre-hashing which only requires the +message and the hash function as input. + +Security-wise, during the signing operation of pure (or "one-step") +ML-DSA, the cryptographic module extracts the public key hash `tr` from +the secret key object, and thus there is no possibility of mismatch +between `tr` and `sk`. In ExternalMu-ML-DSA, the public key or its hash +needs to be provided to the `Prehash()` routine indpedendly of the secret +key, and while the exact mechanism by which it is delivered will be +implementation-specific, it does open a windown for mismatches between +`tr` and `sk`. First, this will produce a signature which will fail to +verify under the intended public key since a compliant `Verify()` routine +will independently compute `tr` from the public key. Implementors should pay careful +attention to how the public key or its hash is delivered to the +`ExternalMu-ML-DSA.Prehash()` routine, and from where they are sourcing +this data. + # Acknowledgments {:numbered="false"}