Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Ounsworth KEM Combiner
This KEM Combiner is based on this draft specification. Unlike X-Wing, the Ounsworth combiner (named after its main author) offers great flexibility by supporting any KEM combination. X-Wing, on the other hand, only specifies a single KEM combination (ML-KEM-768 with X25519). This flexibility is beneficial when using BSI-recommended algorithms such as FrodoKEM and brainpool curves.
Since the current draft is still in its early stages and lacks test vectors, the specification may change in the future. Additionally, I may have misinterpreted certain parts of the specification. Therefore, I have marked the implementation as experimental using the
BOTAN_UNSTABLE_API
macro.Structure of the Implementation
The Ounsworth KEM Combiner is implemented as a regular KEM. It combines any number of sub-KEMs with a specified KDF. To load/create Ounsworth private and public keys users must define how to load or create the individual keys respectively. This is done using the
Ounsworth::PrivateKeyImportInfo
,Ounsworth::PublicKeyImportInfo
, andOunsworth::PublicKeyGenerationInfo
classes.To provide flexibility in defining sub-KEMs, I want to create an interface that allows users to define and use their own KEMs in the Ounsworth Combiner. This is useful in one of our research projects where experimental PKCS#11-based instances must be combined with the Ounsworth Combiner.
However, I also understand that most applications have more straightforward requirements. To accommodate this, I provide a straightforward constructor for the predefined algorithms such as Kyber, FrodoKEM for PQC, and X25519, X448, and ECDH for the classic choice. These algorithms can be selected using an enum value.
Example:
Some limitations with the current public/private key interface make generalization a bit messy. For example, to concatenate public keys without any encoding, we need to know the length of the public key in advance. The same goes for private keys. To address this, we may want to add methods like
pk_byte_length()
andsk_byte_length()
for the private and public key interfaces in the future. For now, I worked around this limitation using algorithm-dependent methods.I decided to compose the Ounsworth key using the raw public and private key bytes. Although the encoding is not specified in the draft, I believe this is the best approach. However, in most cases, this prevents me from using Botan's generic interfaces, which adds further complexity to the implementation (see
ounsworth_mode.cpp
).Testing
Since no test vectors are available yet, I used some tests covering most of the draft's requirements.
The underlying One-Step KDF of NIST.SP.800-56C is extended and tested in One-Step Key Derivation Method with KMAC #4121.
To ensure that the predefined KEMs are configured correctly, I conducted a roundtrip test using all predefined algorithms simultaneously.
The only potential source of error remaining is the creation of the KDF inputs, specifically the composition of various fields and lengths to form the KDF input. To test this, I created a handcrafted test vector that should represent other combinations.
I also tested the key interfaces using the generic keygen tests.
General Remarks
The draft is still in its early stages, so I had to make some assumptions. For instance, the structure of the private and public keys is not specified in the draft. To address this, I documented my decisions in the doxygen documentation of the private key class.
I've already defined some OIDs in Botan's private arc to make it easier to use in protocols. These OIDs use sensible combinations for different key strengths. I've even included combinations with Frodo and Brainpool curves, which the BSI recommends. I crafted these combinations myself, but feel free to suggest other options.
I welcome any feedback on the implementation.
Pull request dependencies