-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an ML-KEM implementation #2
Conversation
cc @dsprenkels |
Note that the Go version linked above has a very thorough battery of tests. I think it would be a good idea to have that coverage here as well, but it can be handled as a follow-on PR. |
ml-kem/src/lib.rs
Outdated
/// A key that can be used to decapsulated an encapsulated shared key | ||
pub trait DecapsulationKey<SharedKey, Ciphertext>: EncodedSizeUser { | ||
/// Decapsulate the ciphertext to obtain the encapsulated shared key | ||
fn decapsulate(&self, ciphertext: &Ciphertext) -> SharedKey; | ||
} | ||
|
||
/// A key that can be used to encapsulate a shared key such that it can only be decapsulated | ||
/// by the holder of the corresponding decapsulation key | ||
pub trait EncapsulationKey<SharedKey, Ciphertext>: EncodedSizeUser { | ||
/// Encapsulate a fresh secret to the holder of the corresponding decapsulation key | ||
fn encapsulate(&self, rng: &mut impl CryptoRngCore) -> (SharedKey, Ciphertext); | ||
|
||
/// Encapsulate a specific shared key to the holder of the corresponding decapsulation key | ||
#[cfg(feature = "deterministic")] | ||
fn encapsulate_deterministic(&self, m: &B32) -> (SharedKey, Ciphertext); | ||
} | ||
|
||
/// A generic interface to a Key Encapsulation Method | ||
pub trait KemCore { | ||
/// The size of a shared key generated by this KEM | ||
type SharedKeySize: ArrayLength; | ||
|
||
/// The size of a ciphertext encapsulating a shared key | ||
type CiphertextSize: ArrayLength; | ||
|
||
/// A decapsulation key for this KEM | ||
type DecapsulationKey: DecapsulationKey<SharedKey<Self>, Ciphertext<Self>> + Debug + PartialEq; | ||
|
||
/// An encapsulation key for this KEM | ||
type EncapsulationKey: EncapsulationKey<SharedKey<Self>, Ciphertext<Self>> + Debug + PartialEq; | ||
|
||
/// Generate a new (decapsulation, encapsulation) key pair | ||
fn generate(rng: &mut impl CryptoRngCore) -> (Self::DecapsulationKey, Self::EncapsulationKey); | ||
|
||
/// Generate a new (decapsulation, encapsulation) key pair deterministically | ||
#[cfg(feature = "deterministic")] | ||
fn generate_deterministic(d: &B32, z: &B32) | ||
-> (Self::DecapsulationKey, Self::EncapsulationKey); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do have some existing traits for KEMs here:
cc @rozbb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, though its tires haven't been thoroughly kicked so I'm curious if @bifurcation thinks it's an OK fit.
Also it probably needs to be upgraded to hybrid-array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh nice re KEM traits, somehow I had missed that. Obviously should use that, was already thinking that that trait should be outside this crate.
@tarcieri if you concur re hybrid_array
, happy to switch to that. (Assuming there are no roadblocks, e.g., due to some of the typenum craziness we're going here.) Might do some benchmarking to see if there are perf impacts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear: I haven't taken a deep look at the KEM traits yet, will do and report back. But it's correct to have the KEM traits outside this crate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're migrating to hybrid-array
elsewhere. It should be a drop-in replacement for generic-array
. There's a migration guide here (it should mostly be find/replace): https://docs.rs/hybrid-array/0.2.0-rc.5/hybrid_array/#migrating-from-generic-array
I can also look at updating the kem
crate to use hybrid-array
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On trying to build tests, I find that I need ArraySize
implementations for a bunch more sizes: 480, 800, 1088, 1152, 1184, 1536, 1568, 1600, 1632, 2336, 2368, 2400, 3104, 3136, 3168. So increasing the base set size up to 4096 would cover me. Note, however, that typenum
doesn't actually define U$n
for all of these $n
, so you might have different problems.
Given this dependency, we might want to consider migrating to hybrid-array
as a follow-on to this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that gets a bit tricky and I see typenum::Const<N>
doesn't cover that range either. It will require encoding the bit pattern for those sizes, and in such a case I could probably consider e.g. multiples of 32 in the range 1024-4096 which should cover your needs at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this dependency, we might want to consider migrating to hybrid-array as a follow-on to this PR.
Yes, agreed. Sorry for the unnecessary detour. I just started trying to migrate the crate myself and hit the same large-const issues for Saber.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries @rozbb! It does look a bit nicer than generic-array
, so I like it as a long-term direction. It's unfortunate that the state of const generics is such that it requires all this hacking around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should at least be a stopgap to support the sizes you need with hybrid-array
: https://github.com/RustCrypto/hybrid-array/pull/49/files
I'll see if I can come up with a better/consistent/more automated way of implementing these additional sizes.
@rozbb I filed a bunch of comments on the interface in RustCrypto/traits#1508. Basically, I think it's usable, just could be nicer. It is correct in that it focuses on the encap/decap keys instead of trying to capture the whole system as I do here. I would probably prefer to get that fixed before implementing here. |
This adds support for sizes identified as needed for post-quantum KEM/DSA use cases, namely the ones from this comment: RustCrypto/KEMs#2 (comment) These should ideally get expanded into some consistent multiples above 1024, e.g. multiples of 32, and generated in a purely automated manner (e.g. by a script that can break down the bit representation and build the generic syntax), but this should at least be sufficient to unblock these use cases. Note that `UInt<UInt<..<UTerm, B#>, B#...` aliases expressing the explicit bits for a given number are used instead of e.g. `<U1024 as Add<U32>>::Output` because when the latter is used it causes similar errors for conflicting trait impls as we saw with `typenum::U<N>` for whatever reason: error[E0119]: conflicting implementations of trait `traits::ArraySize` for type `UTerm` --> src/sizes.rs:82:13 | 82 | unsafe impl ArraySize for $ty { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | first implementation here | conflicting implementation for `UTerm`
This adds support for sizes identified as needed for post-quantum KEM/DSA use cases, namely the ones from this comment: RustCrypto/KEMs#2 (comment) These should ideally get expanded into some consistent multiples above 1024, e.g. multiples of 32, and generated in a purely automated manner (e.g. by a script that can break down the bit representation and build the generic syntax), but this should at least be sufficient to unblock these use cases. Note that `UInt<UInt<..<UTerm, B#>, B#...` aliases expressing the explicit bits for a given number are used instead of e.g. `<U1024 as Add<U32>>::Output` because when the latter is used it causes similar errors for conflicting trait impls as we saw with `typenum::U<N>` for whatever reason: error[E0119]: conflicting implementations of trait `traits::ArraySize` for type `UTerm` --> src/sizes.rs:82:13 | 82 | unsafe impl ArraySize for $ty { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | first implementation here | conflicting implementation for `UTerm`
Latest commits address the comments so far:
I have not attempted to use the KEM traits, given RustCrypto/traits#1508 The latest commit here builds against the @tarcieri how would you like to proceed here? |
I can cut another |
@bifurcation also would love to get your input on RustCrypto/traits#1509 and see if that addresses your comments. It's very very minimal now. Const-size arrays aren't even needed anymore. |
|
There's no current CI configuration, although I could potentially follow up with one, since I don't think it would run anyway until this PR is merged. @bifurcation can you check the code is clean under clippy if you haven't already? I'm noticing some function names with capital letters like |
Thanks for the nudge @rozbb, I had missed that PR. I sent a review. d0945d4 copy/pastes those interfaces into this repo; when that PR gets merged and released, we can switch over to it here. Re clippy -- Yes, it runs clean. I added an Re CI -- I think adding CI in a PR will run it on that PR. @tarcieri do you happen to have a favorite example that could be copy/pasted here? |
I can add it in a separate PR (and since I'm a member, it should run it in that PR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK with the caveat that my review is largely for style and consistency, and not the correctness of the implementation
This PR adds an implementation of ML-KEM variant of the Kyber KEM as described
in the initial public draft of FIPS 203. This implementation covers all three
parameter sets described in the specification by making heavy use of Rust
generics and the
typenum
crate.In addition to self-compatibility testing, we test correctness by verifying the
test vectors supplied by NIST; see
tests/nist.rs
.This PR also includes a benchmark that tests the speed of the ML-KEM-768
variant, the most commonly implemented variant. At least on my MacBook, this
implementation is competitive with / slightly slower than the fastest known implementations (units
are µs/op; only Go and
ml_kem
measured locally, others from this blog post):ml_kem