Open rozbb opened 2 weeks ago
Ok here is a different idea. Just make pub type XSaberCiphertext = [u8; XSABER_CIPHERTEXT_LEN]
. This gets exactly the ref semnatics we want, and lets us avoid inventing 2 newtypes.
Below is what the new API looks like. Notice that this also gives us a natural way to do an encapsulate_in_place
, whereas with a custom ciphertext type we'd need to have a CiphertextMutRef
to hold that.
cc @thomwiggers @pinkforest
use saber_kem::{
kem_traits::{Decapsulate, Encapsulate},
lightsaber::{
LightsaberCiphertext, LightsaberPublicKey, LightsaberSecretKey, LIGHTSABER_CIPHERTEXT_LEN,
},
};
fn main() {
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();
// Alternatively, if you have a buffer and want to avoid an extra allocation, encapsulate in
// place:
let mut ct = [0u8; LIGHTSABER_CIPHERTEXT_LEN];
let ss1 = pk.encapsulate_in_place(&mut rng, &mut ct).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(), LIGHTSABER_CIPHERTEXT_LEN);
let receiver_ct: &LightsaberCiphertext = 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");
}
Currently,
SaberCiphertext
can only be constructed fromfrom_bytes
, which requires a[u8; Self::LEN]
. This requires a ciphertext to be copied before it is decapsulated. This is entirely unnecessary, as it could just as easily be a slice.One solution is to define a
SaberCiphertextRef<'a>
which contains a&'a [u8; Self::LEN]
. Soencap
returnsSaberCiphertext
anddecap
takes aSaberCiphertextRef
. And we can implAsRef<SaberCiphertextRef> for SaberCiphertext
to make conversion straightforward.