Closed ounsworth closed 1 month ago
In the meeting today, we discussed two versions. I don't think we reached consensus about which was better.
Version 1:
Sign (sk, Message, ctx) -> (signature)
Signature Generation Process:
1. Compute the new Message M' by concatenating the Domain identifier (i.e., the DER encoding of the Composite signature algorithm identifier) with the Hash of the Message
M' := Domain || HASH(ctx || Message)
2. Generate the 2 component signatures independently, by calculating the signature over M'
according to their algorithm specifications that might involve the use of the hash-n-sign paradigm.
S1 := ML-DSA.Sign( K1, A1, M', ctx="" )
S2 := Trad.Sign( K2, A2, M' )
Version 2:
Sign (sk, Message, ctx) -> (signature)
Signature Generation Process:
1. Compute the new Message M' by concatenating the Domain identifier (i.e., the DER encoding of the Composite signature algorithm identifier) with the Hash of the Message
M' := Domain || HASH(Message)
2. Generate the 2 component signatures independently, by calculating the signature over M'
according to their algorithm specifications that might involve the use of the hash-n-sign paradigm.
S1 := ML-DSA.Sign( K1, A1, M', ctx )
S2 := Trad.Sign( K2, A2, HASH(ctx || M') )
We did agree that since FIPS 204 defines the API to be:
ML-DSA.Sign(𝑠𝑘, 𝑀, 𝑐𝑡𝑥)
our Composite-ML-DSA should at least have the same API.
Composite author's meeting, we decided to do this:
Signature Generation Process:
0. IF |ctx| > 255:
fail
1. Compute the new Message M' by concatenating the Domain identifier (i.e., the DER encoding of the Composite signature algorithm identifier) with the Hash of the Message
M' := Domain || HASH(IntegerToBytes(|ctx|, 1) || ctx || Message)
where IntegerToBytes(x, alpha) is defined in [FIPS204].
2. Generate the 2 component signatures independently, by calculating the signature over M'
according to their algorithm specifications that might involve the use of the hash-n-sign paradigm.
S1 := ML-DSA.Sign( K1, A1, M', ctx )
S2 := Trad.Sign( K2, A2, M' )
Note that the `M'` construction here mirrors exactly the analogous construction within ML-DSA Algorithm 2 [FIPS204].
Note: we need to update Felipe's PR: https://github.com/lamps-wg/draft-composite-sigs/pull/38/files
In https://github.com/lamps-wg/draft-composite-sigs/issues/35#issuecomment-2360558887 Falko pointed out that EdDSA also uses context. Shall we pass it to his algo also ?
I was just looking at the ML-DSA draft, and they define the pre-hash as this: 𝑀 ← BytesToBits(IntegerToBytes(1, 1) ∥ IntegerToBytes(|𝑐𝑡𝑥|, 1) ∥ 𝑐𝑡𝑥 ∥ OID ∥ PH
Where OID is equivalent to our Domain, and PH is H (Message)
So to align better with what is in pre-hash format of NIST ML-DSA, why don't we re-order to the following:
M' := Domain || HASH(IntegerToBytes(|ctx|, 1) || ctx || Message) M' := IntegerToBytes(|ctx|, 1) || ctx || Domain || Hash (Message)
The part that is missing is what Max had pointed out. (BytesToBits(IntegerToBytes(1, 1) )
So I think it makes sense to align to:
M' := BytesToBits(IntegerToBytes(1, 1) || IntegerToBytes(|ctx|, 1) || ctx || Domain || Hash (Message)
This would also align with Bert Kaliski's "Modes of Operation" presentation at ICMC. For example, they are advocating the "MTL" mode using something like this:
M' := BytesToBits(IntegerToBytes(129, 1) || IntegerToBytes(|ctx|, 1) || ctx || Domain || Hash (Message)
So we could request a different integer value to indicate composite signing, or just use 1 to indicate it is essentially a standard ML-DSA pre-hash
After many conversations we resolved this by adding both a Pure and PreHash Modes for the message format that Align with the FIPS 204 document.
Pure: M' := Domain || len(ctx) || ctx || M PreHash: M' := Domain || len(ctx) || ctx || HashOID || PH(M)
See discussion here:
https://mailarchive.ietf.org/arch/msg/spasm/YFW4hVCZAQiVge2Z1kzAlIcM4U8/
The idea is that you may want to bind the actually application context, like "MS Firmware Sign" in addition to the composite sig OID. This
If we make this change, the composite sign would become:
or possibly
where
Ctx
is external input to the composite Sign(), and defaults to the empty string.My concern with doing things outside the HASH is that this is the pre-hash for RSA / ECDSA, so a very long context string could overflow the RSA / ECDSA "block" -- therefore I think any kind of variable-length Ctx needs to be inside the hash.