use pqcrypto_hqc::hqc256; use pqcrypto_traits::kem::{Ciphertext as _, PublicKey as _, SharedSecret as _}; use sha2::{Digest as _, Sha256}; const ALGORITHM_NAME: &str = "HQC-256"; pub struct Keypair { public_key: hqc256::PublicKey, secret_key: hqc256::SecretKey, } impl Keypair { /// Returns the encapsulation key. This is sometimes called the public key. /// /// This is the key to send to the peer you want to negotiate a shared secret with. pub fn encapsulation_key(&self) -> Vec { self.public_key.as_bytes().to_vec() } pub fn algorithm_name(&self) -> &'static str { ALGORITHM_NAME } /// Decapsulates a shared secret that was encapsulated to our encapsulation key. /// // Always inline in order to try to avoid potential copies of `shared_secret` to multiple places // on the stack. This is almost pointless as with optimization all bets are off regarding where // the shared secrets will end up in memory. In the future we can try to do better, by // cleaning the stack. But this is not trivial. Please see: // https://github.com/RustCrypto/KEMs/issues/70 #[inline(always)] pub fn decapsulate(&self, ciphertext_slice: &[u8]) -> Result<[u8; 32], super::Error> { let ciphertext = hqc256::Ciphertext::from_bytes(ciphertext_slice).map_err(|_| { super::Error::InvalidCiphertextLength { algorithm: self.algorithm_name(), actual: ciphertext_slice.len(), expected: hqc256::ciphertext_bytes(), } })?; let shared_secret = hqc256::decapsulate(&ciphertext, &self.secret_key); // HQC outputs a 64 byte shared secret. But we need 32 bytes for the WireGuard PSK. // Our ephemeral peer API says shared secrets with a length other than 32 bytes should // be passed through sha256 in order to squash the entropy into 32 bytes. let output_shared_secret = Sha256::digest(shared_secret.as_bytes()); Ok(output_shared_secret.into()) } } pub fn keypair() -> Keypair { let (public_key, secret_key) = hqc256::keypair(); Keypair { public_key, secret_key, } }