lamps-wg / draft-composite-sigs

IETF Internet-Draft about X.509 certificates with composite keys and signatures.
Other
3 stars 1 forks source link

We should also expose a context string / domain separator as external input to the composite-sign #42

Closed ounsworth closed 1 month ago

ounsworth commented 2 months ago

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:

M' := Domain || Ctx || HASH(Message)

or possibly

M' := Domain || HASH(Ctx || Message)

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.

ounsworth commented 2 months 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') )
ounsworth commented 2 months ago

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.

ounsworth commented 2 months ago

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

janklaussner commented 2 months ago

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 ?

johngray-dev commented 2 months ago

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

johngray-dev commented 1 month ago

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)