Skip to content

Reduced combinations - #6

Open
feventura wants to merge 40 commits into
upstreamfrom
reduced-combinations
Open

Reduced combinations#6
feventura wants to merge 40 commits into
upstreamfrom
reduced-combinations

Conversation

@feventura

@feventura feventura commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

This PR was created to facilitate the identification of the changes made to implement composite signatures and certificates.

This branch only contains:

  • MLDSA65-RSA3072-PKCS15-SHA512
  • MLDSA65-ECDSA-P256-SHA512

feventura and others added 30 commits August 15, 2025 14:22
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
…in those methods. Changed RSA depricated function. Added text to CHANGES.md (this needs enhancement)

Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
… start of composite files.

Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
feventura and others added 9 commits August 14, 2026 15:12
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
Signed-off-by: feventura <felipe.ventura@entrust.com>
adding tests to reduced combinations branch
Signed-off-by: feventura <felipe.ventura@entrust.com>

-- Taken from https://datatracker.ietf.org/doc/draft-ietf-lamps-pq-composite-sigs/

sigAlgs OBJECT IDENTIFIER ::= { 1 3 6 1 5 5 7 6 }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is actually "pkixAlgs", not "sigAlgs". For example, key exchanges and KEMs are also in the same arc.

https://www.iana.org/assignments/smi-numbers#smi-numbers-1.3.6.1.5.5.7.6

I suggest renaming this to "pkixAlgs". Also, if you're trying to be really efficient this arc is probably already defined somewhere in the openssl includes (but maybe it's just easier to define it fresh here).

if (key->classic_key == NULL)
return 0;
return 1;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function seems unintuitive to me and is probably worth a header comment.

(Part of my problem is that I don't know what openssl ossl_ml_dsa_key_has is, but a quick Google AI search tells me that this is checking for the ML-DSA private key in Full, Seed, or Both mode).

Will a composite key ever have the ML-DSA component in Full or Both mode? Or only ever Seed?

if (key->classic_key == NULL)
return ml_dsa_len; // if there is not classic key, composite is malformed
return ml_dsa_len + (size_t)((EVP_PKEY_get_bits(key->classic_key) + 7) / 8);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know from the reference implementation that the RSA and EC public keys can change their length by one or two bytes (I think, depending on leading zeros, which get truncated in the DER encoding). You also need to account for the DER length tags in the classic component.

Is this code actually counting the encoded length, or is it assuming a maximum length? It looks like EVP_PKEY_get_bits is the maximum size that you could ever need for a buffer for this.

If that's the case, that the size_t returned from ossl_composite_key_get_pub_len might actually be longer than the key it contains, then you should say that in the header comment.

/*
* Security bits of the composite = minimum of ML-DSA collision strength and
* the classic component's security bits.
*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a bit of a weird way to measure the security strength of a composite. In the draft, we tried very hard to not do this because it's rather difficult to compare the security of PQ and classical algs.

Is this as function that you're required to implement in order for the EVP APIs to work correctly? If you deleted this function, does anything break?

return 1;
}

static int composite_validate(const void *keydata, int selection,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be my unfamiliarity with openssl, but what is this validating? That the public keys and private keys match? It's not validating a signature.
It's probably worth adding a header comment.
(also, maybe there is a header file somewhere that declares these functions, and there's a comment on that?)

*/
static const COMPOSITE_ALG_INFO composite_alg_table[] = {
/* name, label, oid, oid_sz, prehash, phlen, classic_hash,
classic_type, pss_salt_len, mgf1_hash */

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is excellent and shows that you're ready for the RSA-PSS stuff, even though it's not in this PR.

uint8_t *out)
{
return EVP_Q_digest(libctx, info->prehash_alg, NULL, msg, msg_len, out, NULL);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not super convinced that a single-line helper function is all that helpful, but that's really a nit-pick.

*siglen = ml_dsa_sig_max
+ (size_t)EVP_PKEY_get_size(ctx->key->classic_key);
return 1;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be a bit careful here because, as I understand this, this will return a max length, not necessarily the exact length.
That should be noted in the comment here for future maintainers, and check that this is only used for sizing buffers, which are ok to be oversized and contain some trailing un-used bytes, and nothing is expecting this to be an exact length.

}

return 1;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to pre-compute the DER strings and hard-code them as hex, rather than having this code?

"Better" in the sense that it's easier to code review?

{
ERR_raise(ERR_LIB_PROV, ERR_R_UNSUPPORTED);
return 0;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could be implemented, right?
If I am guessing correctly at what these are supposed to do, they could be supported.
_init() would need to initialize the composite PH, each chunk of message handed into _update() would be fed into that hash, and _final() would finish the PH, construct the M' and finish the operation.

You might find it easier to refactor so that you have a composite_sign_ph() and composite_verify_ph() as a shared building block between the streaming and one-shot functions.

Moreover, it would be worth exposing the composite_sign_ph() externally to openssl because calling applications may want to pre-compute the PH themselves (for example on another machine so that you only need to send the hash value, and not the whole message, over the network).

I think these are definitely worth implementing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants