feat: add bindings for level 2 and 4; generate randomness from provided CSRNG
This commit is contained in:
60
Cargo.lock
generated
60
Cargo.lock
generated
@@ -69,18 +69,6 @@ version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
@@ -141,9 +129,9 @@ name = "mlkem-native-rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"make-cmd",
|
||||
"rand_core",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -184,6 +172,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -236,21 +230,32 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
@@ -314,12 +319,3 @@ name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
@@ -5,8 +5,8 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
getrandom = "0.3.1"
|
||||
libc = "0.2.171"
|
||||
rand_core = "0.9.3"
|
||||
thiserror = "2.0.12"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.71.1"
|
||||
|
||||
49
build.rs
49
build.rs
@@ -2,25 +2,19 @@ use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// This is the directory where the `c` library is located.
|
||||
let libdir_path = PathBuf::from("mlkem-native")
|
||||
// Canonicalize the path as `rustc-link-search` requires an absolute
|
||||
// path.
|
||||
.canonicalize()
|
||||
.expect("cannot canonicalize path");
|
||||
|
||||
// This is the path to the `c` headers file.
|
||||
let headers_path = libdir_path.join("mlkem/mlkem_native.h");
|
||||
let headers_path_str = headers_path.to_str().expect("Path is not a valid string");
|
||||
|
||||
// Tell cargo to look for shared libraries in the specified directory
|
||||
println!(
|
||||
"cargo:rustc-link-search={}",
|
||||
libdir_path.join("test/build").to_str().unwrap()
|
||||
);
|
||||
|
||||
// Tell cargo to tell rustc to link our `hello` library. Cargo will
|
||||
// automatically know it must look for a `libhello.a` file.
|
||||
println!("cargo:rustc-link-lib=mlkem512");
|
||||
println!("cargo:rustc-link-lib=mlkem768");
|
||||
println!("cargo:rustc-link-lib=mlkem1024");
|
||||
|
||||
@@ -37,24 +31,39 @@ fn main() {
|
||||
panic!("could not compile mlkem-native");
|
||||
}
|
||||
|
||||
// The bindgen::Builder is the main entry point
|
||||
// to bindgen, and lets you build up options for
|
||||
// the resulting bindings.
|
||||
let bindings = bindgen::Builder::default()
|
||||
// The input header we would like to generate
|
||||
// bindings for.
|
||||
let bindings_level2 = bindgen::Builder::default()
|
||||
.header(headers_path_str)
|
||||
// Tell cargo to invalidate the built crate whenever any of the
|
||||
// included header files changed.
|
||||
.clang_arg("-DMLKEM_K=2")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
// Finish the builder and generate the bindings.
|
||||
.generate()
|
||||
// Unwrap the Result and panic on failure.
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
|
||||
bindings
|
||||
let bindings_level3 = bindgen::Builder::default()
|
||||
.header(headers_path_str)
|
||||
.clang_arg("-DMLKEM_K=3")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
let bindings_level4 = bindgen::Builder::default()
|
||||
.header(headers_path_str)
|
||||
.clang_arg("-DMLKEM_K=4")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings_level2.rs");
|
||||
bindings_level2
|
||||
.write_to_file(out_path)
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings_level3.rs");
|
||||
bindings_level3
|
||||
.write_to_file(out_path)
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings_level4.rs");
|
||||
bindings_level4
|
||||
.write_to_file(out_path)
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
|
||||
240
src/lib.rs
240
src/lib.rs
@@ -1,10 +1,22 @@
|
||||
use rand_core::TryCryptoRng;
|
||||
use std::default::Default;
|
||||
use thiserror::Error;
|
||||
|
||||
mod unsafe_bindings;
|
||||
mod unsafe_bindings_level2;
|
||||
mod unsafe_bindings_level3;
|
||||
mod unsafe_bindings_level4;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MLKEMNativeError {
|
||||
#[error("the CSRNG failed due to insufficent entropy")]
|
||||
InsufficentEntropy,
|
||||
#[error("the library function encountered an internal error")]
|
||||
LibraryError,
|
||||
}
|
||||
|
||||
macro_rules! reexport_const {
|
||||
( $type_name:ident, $struct_name:ident ) => {
|
||||
pub const $type_name: usize = unsafe_bindings::$type_name as usize;
|
||||
pub const $type_name: usize = unsafe_bindings_level2::$type_name as usize;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct $struct_name([u8; $type_name]);
|
||||
@@ -17,7 +29,7 @@ macro_rules! reexport_const {
|
||||
};
|
||||
}
|
||||
|
||||
reexport_const!(MLKEM512_SECRETKEYBYTES, MLKEM512Secretkey);
|
||||
reexport_const!(MLKEM512_SECRETKEYBYTES, MLKEM512SecretKey);
|
||||
reexport_const!(MLKEM512_PUBLICKEYBYTES, MLKEM512PublicKey);
|
||||
reexport_const!(MLKEM512_CIPHERTEXTBYTES, MLKEM512Ciphertext);
|
||||
reexport_const!(MLKEM512_BYTES, MLKEM512SharedSecret);
|
||||
@@ -32,48 +44,66 @@ reexport_const!(MLKEM1024_PUBLICKEYBYTES, MLKEM1024PublicKey);
|
||||
reexport_const!(MLKEM1024_CIPHERTEXTBYTES, MLKEM1024Ciphertext);
|
||||
reexport_const!(MLKEM1024_BYTES, MLKEM1024SharedSecret);
|
||||
|
||||
pub fn mlkem768_keypair() -> Option<(MLKEM768SecretKey, MLKEM768PublicKey)> {
|
||||
let mut sk = MLKEM768SecretKey::default();
|
||||
let mut pk = MLKEM768PublicKey::default();
|
||||
pub fn mlkem512_keypair(
|
||||
rng: &mut impl TryCryptoRng,
|
||||
) -> Result<(MLKEM512SecretKey, MLKEM512PublicKey), MLKEMNativeError> {
|
||||
let mut sk = MLKEM512SecretKey::default();
|
||||
let mut pk = MLKEM512PublicKey::default();
|
||||
|
||||
let mut coins = [0u8; 2 * (unsafe_bindings_level2::MLKEM512_SYMBYTES as usize)];
|
||||
rng.try_fill_bytes(&mut coins)
|
||||
.map_err(|_| MLKEMNativeError::InsufficentEntropy)?;
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings::PQCP_MLKEM_NATIVE_MLKEM768_keypair(pk.0.as_mut_ptr(), sk.0.as_mut_ptr())
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Some((sk, pk))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem768_enc(pk: &MLKEM768PublicKey) -> Option<(MLKEM768Ciphertext, MLKEM768SharedSecret)> {
|
||||
let mut ct = MLKEM768Ciphertext::default();
|
||||
let mut ss = MLKEM768SharedSecret::default();
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings::PQCP_MLKEM_NATIVE_MLKEM768_enc(
|
||||
ct.0.as_mut_ptr(),
|
||||
ss.0.as_mut_ptr(),
|
||||
pk.0.as_ptr(),
|
||||
unsafe_bindings_level2::PQCP_MLKEM_NATIVE_MLKEM512_keypair_derand(
|
||||
pk.0.as_mut_ptr(),
|
||||
sk.0.as_mut_ptr(),
|
||||
coins.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Some((ct, ss))
|
||||
Ok((sk, pk))
|
||||
} else {
|
||||
None
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem768_dec(
|
||||
pub fn mlkem512_enc(
|
||||
rng: &mut impl TryCryptoRng,
|
||||
pk: &MLKEM768PublicKey,
|
||||
) -> Result<(MLKEM768Ciphertext, MLKEM768SharedSecret), MLKEMNativeError> {
|
||||
let mut ct = MLKEM768Ciphertext::default();
|
||||
let mut ss = MLKEM768SharedSecret::default();
|
||||
|
||||
let mut coins = [0u8; unsafe_bindings_level2::MLKEM768_SYMBYTES as usize];
|
||||
rng.try_fill_bytes(&mut coins)
|
||||
.map_err(|_| MLKEMNativeError::InsufficentEntropy)?;
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings_level2::PQCP_MLKEM_NATIVE_MLKEM512_enc_derand(
|
||||
ct.0.as_mut_ptr(),
|
||||
ss.0.as_mut_ptr(),
|
||||
pk.0.as_ptr(),
|
||||
coins.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Ok((ct, ss))
|
||||
} else {
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem512_dec(
|
||||
sk: &MLKEM768SecretKey,
|
||||
ct: &MLKEM768Ciphertext,
|
||||
) -> Option<MLKEM768SharedSecret> {
|
||||
) -> Result<MLKEM768SharedSecret, MLKEMNativeError> {
|
||||
let mut ss = MLKEM768SharedSecret::default();
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings::PQCP_MLKEM_NATIVE_MLKEM768_dec(
|
||||
unsafe_bindings_level2::PQCP_MLKEM_NATIVE_MLKEM512_dec(
|
||||
ss.0.as_mut_ptr(),
|
||||
ct.0.as_ptr(),
|
||||
sk.0.as_ptr(),
|
||||
@@ -81,8 +111,154 @@ pub fn mlkem768_dec(
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Some(ss)
|
||||
Ok(ss)
|
||||
} else {
|
||||
None
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem768_keypair(
|
||||
rng: &mut impl TryCryptoRng,
|
||||
) -> Result<(MLKEM768SecretKey, MLKEM768PublicKey), MLKEMNativeError> {
|
||||
let mut sk = MLKEM768SecretKey::default();
|
||||
let mut pk = MLKEM768PublicKey::default();
|
||||
|
||||
let mut coins = [0u8; 2 * (unsafe_bindings_level2::MLKEM768_SYMBYTES as usize)];
|
||||
rng.try_fill_bytes(&mut coins)
|
||||
.map_err(|_| MLKEMNativeError::InsufficentEntropy)?;
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings_level3::PQCP_MLKEM_NATIVE_MLKEM768_keypair_derand(
|
||||
pk.0.as_mut_ptr(),
|
||||
sk.0.as_mut_ptr(),
|
||||
coins.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Ok((sk, pk))
|
||||
} else {
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem768_enc(
|
||||
rng: &mut impl TryCryptoRng,
|
||||
pk: &MLKEM768PublicKey,
|
||||
) -> Result<(MLKEM768Ciphertext, MLKEM768SharedSecret), MLKEMNativeError> {
|
||||
let mut ct = MLKEM768Ciphertext::default();
|
||||
let mut ss = MLKEM768SharedSecret::default();
|
||||
|
||||
let mut coins = [0u8; unsafe_bindings_level2::MLKEM768_SYMBYTES as usize];
|
||||
rng.try_fill_bytes(&mut coins)
|
||||
.map_err(|_| MLKEMNativeError::InsufficentEntropy)?;
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings_level3::PQCP_MLKEM_NATIVE_MLKEM768_enc_derand(
|
||||
ct.0.as_mut_ptr(),
|
||||
ss.0.as_mut_ptr(),
|
||||
pk.0.as_ptr(),
|
||||
coins.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Ok((ct, ss))
|
||||
} else {
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem768_dec(
|
||||
sk: &MLKEM768SecretKey,
|
||||
ct: &MLKEM768Ciphertext,
|
||||
) -> Result<MLKEM768SharedSecret, MLKEMNativeError> {
|
||||
let mut ss = MLKEM768SharedSecret::default();
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings_level3::PQCP_MLKEM_NATIVE_MLKEM768_dec(
|
||||
ss.0.as_mut_ptr(),
|
||||
ct.0.as_ptr(),
|
||||
sk.0.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Ok(ss)
|
||||
} else {
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem1024_keypair(
|
||||
rng: &mut impl TryCryptoRng,
|
||||
) -> Result<(MLKEM768SecretKey, MLKEM768PublicKey), MLKEMNativeError> {
|
||||
let mut sk = MLKEM768SecretKey::default();
|
||||
let mut pk = MLKEM768PublicKey::default();
|
||||
|
||||
let mut coins = [0u8; 2 * (unsafe_bindings_level2::MLKEM1024_SYMBYTES as usize)];
|
||||
rng.try_fill_bytes(&mut coins)
|
||||
.map_err(|_| MLKEMNativeError::InsufficentEntropy)?;
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings_level4::PQCP_MLKEM_NATIVE_MLKEM1024_keypair_derand(
|
||||
pk.0.as_mut_ptr(),
|
||||
sk.0.as_mut_ptr(),
|
||||
coins.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Ok((sk, pk))
|
||||
} else {
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem1024_enc(
|
||||
rng: &mut impl TryCryptoRng,
|
||||
pk: &MLKEM768PublicKey,
|
||||
) -> Result<(MLKEM768Ciphertext, MLKEM768SharedSecret), MLKEMNativeError> {
|
||||
let mut ct = MLKEM768Ciphertext::default();
|
||||
let mut ss = MLKEM768SharedSecret::default();
|
||||
|
||||
let mut coins = [0u8; unsafe_bindings_level2::MLKEM1024_SYMBYTES as usize];
|
||||
rng.try_fill_bytes(&mut coins)
|
||||
.map_err(|_| MLKEMNativeError::InsufficentEntropy)?;
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings_level4::PQCP_MLKEM_NATIVE_MLKEM1024_enc_derand(
|
||||
ct.0.as_mut_ptr(),
|
||||
ss.0.as_mut_ptr(),
|
||||
pk.0.as_ptr(),
|
||||
coins.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Ok((ct, ss))
|
||||
} else {
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlkem1024_dec(
|
||||
sk: &MLKEM768SecretKey,
|
||||
ct: &MLKEM768Ciphertext,
|
||||
) -> Result<MLKEM768SharedSecret, MLKEMNativeError> {
|
||||
let mut ss = MLKEM768SharedSecret::default();
|
||||
|
||||
let success = unsafe {
|
||||
unsafe_bindings_level4::PQCP_MLKEM_NATIVE_MLKEM1024_dec(
|
||||
ss.0.as_mut_ptr(),
|
||||
ct.0.as_ptr(),
|
||||
sk.0.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
Ok(ss)
|
||||
} else {
|
||||
Err(MLKEMNativeError::LibraryError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::slice;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn randombytes(buf: *mut u8, len: libc::size_t) -> libc::c_int {
|
||||
let buf = slice::from_raw_parts_mut(buf, len);
|
||||
getrandom::fill(buf).expect("RNG Failed");
|
||||
0
|
||||
}
|
||||
6
src/unsafe_bindings_level2.rs
Normal file
6
src/unsafe_bindings_level2.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings_level2.rs"));
|
||||
6
src/unsafe_bindings_level3.rs
Normal file
6
src/unsafe_bindings_level3.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings_level3.rs"));
|
||||
6
src/unsafe_bindings_level4.rs
Normal file
6
src/unsafe_bindings_level4.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings_level4.rs"));
|
||||
Reference in New Issue
Block a user