-
Notifications
You must be signed in to change notification settings - Fork 151
Add safe Rust wrappers for ML-KEM-768 and ML-KEM-1024 #456
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
base: master
Are you sure you want to change the base?
Add safe Rust wrappers for ML-KEM-768 and ML-KEM-1024 #456
Conversation
3daa1d4 to
131049c
Compare
cjpatton
left a comment
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.
It should be possible to deduplicate this code using traits.
pub trait Kem {
/// Note: SEED_BYTES and SHARED_SECRET_BYTES are the same for all ML-KEM parameters
const PUBLIC_KEY_BYTES: usize;
const CIPHERTEXT_BYTES: usize;
type PublicKey;
type PrivateKey;
}
pub trait KemOps: Kem {
fn generate() -> (Kem::PublicKey, Kem::PrivateKey) { ... }
fn encapsulate ..
fn decapsulate ...
}719707e to
377c7b3
Compare
377c7b3 to
cb22731
Compare
cjpatton
left a comment
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.
Hmm, I think defining the Kem and KemOps traits are worth it unless we can actually deduplicate between MLKEM768 and 1024. I think we'll either need to live with duplicated code (I'd be OK with this) or figure out something else.
I think the fundamental problem is that the API we're trying to design doesn't quite match the API of BoringSSL.
I think hash.rs in this directory may provide the inspiration we need. There is a struct Hasher that has a constructor (new) that takes in MessageDigest that defines the algorithm in use (SHA-256, SHA-384, etc.). Then the API calls for actually using the hash is the same regardless of which algorithm is used.
I think we could do the same thing for a struct Kem. Its constructor would determine whether we're using 768 or 1024, and it would have a unified API. One wrinkle is that we would need to handle the the public key and private key as byte strings, but I think that's pretty reasonable.
For example:
pub MlKemParam(...);
pub struct MlKem { ... }
impl MlKem {
pub fn new(p: MlKemParam) -> Result<Self, ErrorStack> { ... }
pub fn key_gen(&self) -> Result<(Vec<u8>, [SEED_BYTES; u8]), ErrorStack> { ... } // returns public key, private key
pub fn encapsulate(&self, pk: &[u8]) -> Result<(Vec<u8>, [SHARED_SECRET_BYTES; u8], ErrorStack> { ... } // returns ciphertext
pub fn decapsulate(&self, sk: &[u8], ciphertext: &[u8]) -> Result<[u8; SHARED_SECRET_BYTES], ErrorStack> { ... }
}Notes:
- Elsewhere in this crate crypto operations return ErrorStack instead of a module-specific error type. I think that's probably appropriate here as well.
- The length of the public key and ciphertext depend on the KEM type, which is why I've handled them as a vector. Unfortunately we can't use an array without adding the length of the array as a generic
const. This could work, but would clutter the API unnecessarily. An alternative that we can make alloc-free is have the user pass a&mut [u8]buffer and fail if it's the wrong size.
Adds safe Rust wrappers around BoringSSL's ML-KEM (FIPS 203) key encapsulation, covering both ML-KEM-768 and ML-KEM-1024 parameter sets. - Key generation, encapsulation, and decapsulation - Seed-based private key storage and restoration - Public key derivation, serialization, and validation - Wipes private key material from memory on drop - Tests for roundtrip, implicit rejection, debug redaction, and validation
a711162 to
afaefeb
Compare
Co-authored-by: Christopher Patton <chrispatton@gmail.com>
afaefeb to
3de4e6a
Compare
Adds safe Rust wrappers around BoringSSL's ML-KEM (FIPS 203) key encapsulation, covering both ML-KEM-768 and ML-KEM-1024 parameter sets.