This crate is a pure-Rust, no-std implementation of draft 3 of the Saber key encapsulation mechanism (KEM). Saber is a lattice-based KEM that is designed to be secure against classical and quantum adversaries. It comes three variants:
This crate has not been audited in any sense of the word. Use at your own risk.
In general, if you are looking to use a post-quantum KEM and have no other requirements, you should use ML-KEM (aka "Kyber", its pre-standardization name), since it is faster and more standardized than Saber. However, Saber has two small benefits over Kyber:
This crate is compatible with Saber's C reference implementation. Known-answer tests (KATs) test vectors can be found in tests/
. Test vectors were taken directly from the previously linked repo, and converted to JSON using tests/convert_rsp_to_json.py
.
The following code can be found in examples/simple.rs
.
use saber_kem::{
kem_traits::{Decapsulate, Encapsulate},
lightsaber::{LightsaberCiphertext, LightsaberPublicKey, LightsaberSecretKey},
};
let mut rng = rand::thread_rng();
// Generate a keypair
let sk = LightsaberSecretKey::generate(&mut rng);
let pk = sk.public_key();
// Serialize the secret key, maybe to save on disk
let mut sk_bytes = [0u8; LightsaberSecretKey::SERIALIZED_LEN];
sk.to_bytes(&mut sk_bytes);
let slice_containing_sk = sk_bytes.as_slice();
// Deserialize the secret key
// The API only accepts fixed-len slices, so we have to cast it first
let sk_arr = slice_containing_sk[..LightsaberSecretKey::SERIALIZED_LEN]
.try_into()
.unwrap();
let sk = LightsaberSecretKey::from_bytes(sk_arr);
// Also serialize and deserialize the public key
let mut pk_bytes = [0u8; LightsaberPublicKey::SERIALIZED_LEN];
pk.to_bytes(&mut pk_bytes);
let slice_containing_pk = pk_bytes.as_slice();
// The API only accepts fixed-len slices, so we have to cast it first
let pk_arr = slice_containing_pk[..LightsaberPublicKey::SERIALIZED_LEN]
.try_into()
.unwrap();
let pk = LightsaberPublicKey::from_bytes(pk_arr);
// Encapsulate a shared secret, ss1, to pk
let (ct, ss1) = pk.encapsulate(&mut rng).unwrap();
// The ciphertext is just bytes, so serializing is straightforward
let ct_bytes = ct.as_ref();
// Deserializing is also straightforward
assert_eq!(ct_bytes.len(), LightsaberCiphertext::LEN);
let receiver_ct = LightsaberCiphertext::from_bytes(ct_bytes.try_into().unwrap());
// Use the secret key to decapsulate the ciphertext
let ss2 = sk.decapsulate(&receiver_ct).unwrap();
// Check the shared secrets are equal. NOTE is not a constant-time check (ie not secure). We
// only do this for testing purposes.
assert_eq!(ss1.as_bytes(), ss2.as_bytes());
println!("KEM ran successfully");
We have implemented benchmarks for key generation, encapsulation, and decapsulation for all variants. Simply run cargo bench
.
Licensed under either of
at your option.