Skip to content

Commit

Permalink
ExternalMu Shuffle (#65)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Panos K. <[email protected]>
  • Loading branch information
3 people authored Jan 14, 2025
1 parent 391d51a commit bf24775
Showing 1 changed file with 144 additions and 86 deletions.
230 changes: 144 additions & 86 deletions draft-ietf-lamps-dilithium-certificates.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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"}

Expand Down

0 comments on commit bf24775

Please sign in to comment.