summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSebastian Holmin <sebastian.holmin@mullvad.net>2025-01-08 18:00:07 +0100
committerSebastian Holmin <sebastian.holmin@mullvad.net>2025-01-09 15:13:10 +0100
commitd6c89bd1704b3603b7d2fec89430369d42b0f8bc (patch)
tree260fe5bcb442c512406dc9ff0fd5de8906a7e0ff
parentf008ce5571d89d437d4bd025558823de0e6810a1 (diff)
downloadmullvadvpn-d6c89bd1704b3603b7d2fec89430369d42b0f8bc.tar.xz
mullvadvpn-d6c89bd1704b3603b7d2fec89430369d42b0f8bc.zip
Generate McEliece key pairs in separate thread
-rw-r--r--talpid-tunnel-config-client/src/classic_mceliece.rs32
1 files changed, 27 insertions, 5 deletions
diff --git a/talpid-tunnel-config-client/src/classic_mceliece.rs b/talpid-tunnel-config-client/src/classic_mceliece.rs
index 7f7edd43a7..7c6db29b68 100644
--- a/talpid-tunnel-config-client/src/classic_mceliece.rs
+++ b/talpid-tunnel-config-client/src/classic_mceliece.rs
@@ -1,30 +1,52 @@
+use std::sync::OnceLock;
+
use classic_mceliece_rust::{keypair_boxed, Ciphertext, CRYPTO_CIPHERTEXTBYTES};
pub use classic_mceliece_rust::{PublicKey, SecretKey, SharedSecret};
+use tokio::sync::{mpsc, Mutex};
/// The `keypair_boxed` function needs just under 1 MiB of stack in debug
/// builds.
const STACK_SIZE: usize = 2 * 1024 * 1024;
+/// Number of McEliece key pairs to buffer
+const BUFSIZE: usize = 2;
+
/// Use the smallest CME variant with NIST security level 3. This variant has significantly smaller
/// keys than the larger variants, and is considered safe.
pub const ALGORITHM_NAME: &str = "Classic-McEliece-460896f-round3";
-pub async fn generate_keys() -> (PublicKey<'static>, SecretKey<'static>) {
- let (tx, rx) = tokio::sync::oneshot::channel();
+static KEYPAIR_RX: OnceLock<Mutex<mpsc::Receiver<KeyPair>>> = OnceLock::new();
+
+type KeyPair = (PublicKey<'static>, SecretKey<'static>);
+fn spawn_keypair_worker(bufsize: usize) -> mpsc::Receiver<KeyPair> {
+ let bufsize = bufsize.checked_sub(1).expect("bufsize must be at least 1");
+ let (tx, rx) = mpsc::channel(bufsize);
// We fork off the key computation to a separate thread for two reasons:
// * The computation uses a lot of stack, and we don't want to rely on the default
// stack being large enough or having enough space left.
// * The computation takes a long time and must not block the async runtime thread.
std::thread::Builder::new()
.stack_size(STACK_SIZE)
- .spawn(move || {
+ .spawn(move || loop {
let keypair = keypair_boxed(&mut rand::thread_rng());
- let _ = tx.send(keypair);
+ if tx.blocking_send(keypair).is_err() {
+ return;
+ }
})
.unwrap();
- rx.await.unwrap()
+ rx
+}
+
+pub async fn generate_keys() -> KeyPair {
+ KEYPAIR_RX
+ .get_or_init(|| Mutex::new(spawn_keypair_worker(BUFSIZE)))
+ .lock()
+ .await
+ .recv()
+ .await
+ .expect("Failed to receive key pair, generating working expectedly closed.")
}
pub fn decapsulate(