summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2023-01-30 14:25:10 +0100
committerLinus Färnstrand <linus@mullvad.net>2023-01-30 14:25:10 +0100
commit23d4e88dbddc6c8640da053c32fc67b2adb3d8d9 (patch)
treedbafaaa221aedb2aeda04e030596f6a5434b0d39
parent5d52ebc582d42fe51a28e3476b48b97059d2b8d2 (diff)
parent9ffc820b579e2f948c5faaf2982ab33ffe00b4ae (diff)
downloadmullvadvpn-23d4e88dbddc6c8640da053c32fc67b2adb3d8d9.tar.xz
mullvadvpn-23d4e88dbddc6c8640da053c32fc67b2adb3d8d9.zip
Merge branch 'post-quantum-add-kyber1024'
-rw-r--r--CHANGELOG.md8
-rw-r--r--Cargo.lock32
-rw-r--r--mullvad-cli/src/cmds/tunnel.rs2
-rw-r--r--talpid-tunnel-config-client/Cargo.toml4
-rw-r--r--talpid-tunnel-config-client/examples/tuncfg-server.rs28
-rw-r--r--talpid-tunnel-config-client/proto/tunnel_config.proto30
-rw-r--r--talpid-tunnel-config-client/src/classic_mceliece.rs27
-rw-r--r--talpid-tunnel-config-client/src/kyber.rs28
-rw-r--r--talpid-tunnel-config-client/src/lib.rs79
9 files changed, 156 insertions, 82 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ede4526fb9..0c739113eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,14 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
+### Added
+- Add Kyber1024 KEM algorithm into the Post-Quantum secure key exchange algorithm. This means the
+ Quantum-resistant-tunnels feature now mixes both Classic McEliece and Kyber for added protection.
+
+### Changed
+- Update the Post-Quantum secure key exchange gRPC client to use the stabilized
+ `PskExchangeV1` endpoint
+
### Fixed
#### Android
- Fix adaptive app icon which previously had a displaced nose and some other oddities.
diff --git a/Cargo.lock b/Cargo.lock
index 6bef7c336c..e4a510edc8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -290,9 +290,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
-version = "1.0.71"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
+checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cesu8"
@@ -457,7 +457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
dependencies = [
"generic-array 0.14.4",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
"subtle",
"zeroize",
]
@@ -712,7 +712,7 @@ dependencies = [
"crypto-bigint",
"der",
"generic-array 0.14.4",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
"sec1",
"subtle",
"zeroize",
@@ -2302,6 +2302,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
+name = "pqc_kyber"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b79004a05337e54e8ffc0ec7470e40fa26eca6fe182968ec2b803247f2283c"
+dependencies = [
+ "rand_core 0.6.4",
+ "zeroize",
+]
+
+[[package]]
name = "prettyplease"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2471,7 +2481,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
]
[[package]]
@@ -2491,7 +2501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
]
[[package]]
@@ -2505,9 +2515,9 @@ dependencies = [
[[package]]
name = "rand_core"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.3",
]
@@ -2952,7 +2962,7 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
dependencies = [
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
]
[[package]]
@@ -3287,6 +3297,7 @@ version = "0.0.0"
dependencies = [
"classic-mceliece-rust",
"log",
+ "pqc_kyber",
"prost",
"rand 0.8.5",
"talpid-types",
@@ -3294,6 +3305,7 @@ dependencies = [
"tonic",
"tonic-build",
"tower",
+ "zeroize",
]
[[package]]
@@ -4223,7 +4235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df"
dependencies = [
"curve25519-dalek",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
"zeroize",
]
diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs
index e47ac5afe5..a40aa985b9 100644
--- a/mullvad-cli/src/cmds/tunnel.rs
+++ b/mullvad-cli/src/cmds/tunnel.rs
@@ -60,7 +60,7 @@ fn create_wireguard_mtu_subcommand() -> clap::App<'static> {
fn create_wireguard_quantum_resistant_tunnel_subcommand() -> clap::App<'static> {
clap::App::new("quantum-resistant-tunnel")
- .about("EXPERIMENTAL: Enables quantum-resistant PSK exchange in the tunnel")
+ .about("Controls the quantum-resistant PSK exchange in the tunnel")
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
.subcommand(clap::App::new("get"))
.subcommand(clap::App::new("set").arg(clap::Arg::new("policy").required(true)))
diff --git a/talpid-tunnel-config-client/Cargo.toml b/talpid-tunnel-config-client/Cargo.toml
index c6b2d1d655..62542da683 100644
--- a/talpid-tunnel-config-client/Cargo.toml
+++ b/talpid-tunnel-config-client/Cargo.toml
@@ -15,7 +15,9 @@ tonic = "0.8"
prost = "0.11"
tower = "0.4"
tokio = "1"
-classic-mceliece-rust = { version = "2.0.0", features = ["mceliece460896f"] }
+classic-mceliece-rust = { version = "2.0.0", features = ["mceliece460896f", "zeroize"] }
+pqc_kyber = { version = "0.4.0", features = ["std", "kyber1024", "zeroize"] }
+zeroize = "1.5.7"
[dev-dependencies]
tokio = { version = "1", features = ["rt-multi-thread"] }
diff --git a/talpid-tunnel-config-client/examples/tuncfg-server.rs b/talpid-tunnel-config-client/examples/tuncfg-server.rs
index 31a9fcff8b..4d29d764a2 100644
--- a/talpid-tunnel-config-client/examples/tuncfg-server.rs
+++ b/talpid-tunnel-config-client/examples/tuncfg-server.rs
@@ -1,4 +1,4 @@
-//! A server implementation of the tuncfg PskExchangeExperimentalV1 RPC to test
+//! A server implementation of the tuncfg PskExchangeV1 RPC to test
//! the client side implementation.
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -8,8 +8,7 @@ mod proto {
use classic_mceliece_rust::{PublicKey, CRYPTO_PUBLICKEYBYTES};
use proto::{
post_quantum_secure_server::{PostQuantumSecure, PostQuantumSecureServer},
- PskRequestExperimentalV0, PskRequestExperimentalV1, PskResponseExperimentalV0,
- PskResponseExperimentalV1,
+ PskRequestV1, PskResponseV1,
};
use talpid_types::net::wireguard::PresharedKey;
@@ -20,17 +19,10 @@ pub struct PostQuantumSecureImpl {}
#[tonic::async_trait]
impl PostQuantumSecure for PostQuantumSecureImpl {
- async fn psk_exchange_experimental_v0(
+ async fn psk_exchange_v1(
&self,
- _request: Request<PskRequestExperimentalV0>,
- ) -> Result<Response<PskResponseExperimentalV0>, Status> {
- unimplemented!("Use V1 instead");
- }
-
- async fn psk_exchange_experimental_v1(
- &self,
- request: Request<PskRequestExperimentalV1>,
- ) -> Result<Response<PskResponseExperimentalV1>, Status> {
+ request: Request<PskRequestV1>,
+ ) -> Result<Response<PskResponseV1>, Status> {
let mut rng = rand::thread_rng();
let request = request.into_inner();
@@ -45,7 +37,7 @@ impl PostQuantumSecure for PostQuantumSecureImpl {
for kem_pubkey in request.kem_pubkeys {
println!("\tKEM algorithm: {}", kem_pubkey.algorithm_name);
let (ciphertext, shared_secret) = match kem_pubkey.algorithm_name.as_str() {
- "Classic-McEliece-460896f" => {
+ "Classic-McEliece-460896f-round3" => {
let key_data: [u8; CRYPTO_PUBLICKEYBYTES] =
kem_pubkey.key_data.as_slice().try_into().unwrap();
let public_key = PublicKey::from(&key_data);
@@ -53,6 +45,12 @@ impl PostQuantumSecure for PostQuantumSecureImpl {
classic_mceliece_rust::encapsulate_boxed(&public_key, &mut rng);
(ciphertext.as_array().to_vec(), *shared_secret.as_array())
}
+ "Kyber1024" => {
+ let public_key = kem_pubkey.key_data.as_slice();
+ let (ciphertext, shared_secret) =
+ pqc_kyber::encapsulate(public_key, &mut rng).unwrap();
+ (ciphertext.to_vec(), shared_secret)
+ }
name => panic!("Unsupported KEM algorithm: {name}"),
};
@@ -66,7 +64,7 @@ impl PostQuantumSecure for PostQuantumSecureImpl {
let psk = PresharedKey::from(psk_data);
println!("psk: {psk:?}");
println!("==============================================");
- Ok(Response::new(PskResponseExperimentalV1 { ciphertexts }))
+ Ok(Response::new(PskResponseV1 { ciphertexts }))
}
}
diff --git a/talpid-tunnel-config-client/proto/tunnel_config.proto b/talpid-tunnel-config-client/proto/tunnel_config.proto
index 215aa941c8..af7f7f158e 100644
--- a/talpid-tunnel-config-client/proto/tunnel_config.proto
+++ b/talpid-tunnel-config-client/proto/tunnel_config.proto
@@ -5,11 +5,6 @@ option go_package = "github.com/mullvad/wg-manager/server/tuncfg";
package tunnel_config;
service PostQuantumSecure {
- // PskExchangeExperimentalV0 uses the common API defined by LibOQS. See:
- // https://github.com/open-quantum-safe/liboqs
- // This endpoint is deprecated in favor for `PskExchangeExperimentalV1`. Please use that instead.
- rpc PskExchangeExperimentalV0(PskRequestExperimentalV0) returns (PskResponseExperimentalV0) {}
-
// Allows deriving a preshared key (PSK) using one or multiple PQ-secure key-encapsulation
// mechanisms (KEM). The preshared key is added to WireGuard's preshared-key field in a new
// ephemeral peer (PQ-peer). This makes the tunnel resistant towards attacks using
@@ -71,35 +66,20 @@ service PostQuantumSecure {
// Mixing with XOR (A = B ^ C) is fine since nothing about A is revealed even if one of B or C
// is known. Both B *and* C must be known to compute any bit in A. This means all involved
// KEM algorithms must be broken before the PSK can be computed by an attacker.
- rpc PskExchangeExperimentalV1(PskRequestExperimentalV1) returns (PskResponseExperimentalV1) {}
-}
-
-message PskRequestExperimentalV0 {
- bytes wg_pubkey = 1;
- bytes wg_psk_pubkey = 2;
- KemPubkeyExperimentalV0 kem_pubkey = 3;
-}
-
-message KemPubkeyExperimentalV0 {
- string algorithm_name = 1;
- bytes key_data = 2;
-}
-
-message PskResponseExperimentalV0 {
- bytes ciphertext = 1;
+ rpc PskExchangeV1(PskRequestV1) returns (PskResponseV1) {}
}
-message PskRequestExperimentalV1 {
+message PskRequestV1 {
bytes wg_pubkey = 1;
bytes wg_psk_pubkey = 2;
- repeated KemPubkeyExperimentalV1 kem_pubkeys = 3;
+ repeated KemPubkeyV1 kem_pubkeys = 3;
}
-message KemPubkeyExperimentalV1 {
+message KemPubkeyV1 {
string algorithm_name = 1;
bytes key_data = 2;
}
-message PskResponseExperimentalV1 {
+message PskResponseV1 {
repeated bytes ciphertexts = 1;
}
diff --git a/talpid-tunnel-config-client/src/classic_mceliece.rs b/talpid-tunnel-config-client/src/classic_mceliece.rs
index dbba95f067..780566958d 100644
--- a/talpid-tunnel-config-client/src/classic_mceliece.rs
+++ b/talpid-tunnel-config-client/src/classic_mceliece.rs
@@ -1,4 +1,6 @@
-use classic_mceliece_rust::{keypair_boxed, SharedSecret};
+use classic_mceliece_rust::{
+ keypair_boxed, Ciphertext, PublicKey, SecretKey, SharedSecret, CRYPTO_CIPHERTEXTBYTES,
+};
/// The `keypair_boxed` function needs just under 1 MiB of stack in debug
/// builds. Even though it probably works to run it directly on the main
@@ -6,7 +8,9 @@ use classic_mceliece_rust::{keypair_boxed, SharedSecret};
/// keys on a separate thread with a large enough stack.
const STACK_SIZE: usize = 2 * 1024 * 1024;
-pub use classic_mceliece_rust::{Ciphertext, PublicKey, SecretKey, CRYPTO_CIPHERTEXTBYTES};
+/// 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();
@@ -22,6 +26,21 @@ pub async fn generate_keys() -> (PublicKey<'static>, SecretKey<'static>) {
rx.await.unwrap()
}
-pub fn decapsulate(secret: &SecretKey, ciphertext: &Ciphertext) -> SharedSecret<'static> {
- classic_mceliece_rust::decapsulate_boxed(ciphertext, secret)
+pub fn decapsulate(
+ secret: &SecretKey,
+ ciphertext_slice: &[u8],
+) -> Result<SharedSecret<'static>, super::Error> {
+ let ciphertext_array =
+ <[u8; CRYPTO_CIPHERTEXTBYTES]>::try_from(ciphertext_slice).map_err(|_| {
+ super::Error::InvalidCiphertextLength {
+ algorithm: ALGORITHM_NAME,
+ actual: ciphertext_slice.len(),
+ expected: CRYPTO_CIPHERTEXTBYTES,
+ }
+ })?;
+ let ciphertext = Ciphertext::from(ciphertext_array);
+ Ok(classic_mceliece_rust::decapsulate_boxed(
+ &ciphertext,
+ secret,
+ ))
}
diff --git a/talpid-tunnel-config-client/src/kyber.rs b/talpid-tunnel-config-client/src/kyber.rs
new file mode 100644
index 0000000000..0946e7f5c2
--- /dev/null
+++ b/talpid-tunnel-config-client/src/kyber.rs
@@ -0,0 +1,28 @@
+use pqc_kyber::{SecretKey, KYBER_CIPHERTEXTBYTES};
+
+pub use pqc_kyber::{keypair, KyberError};
+
+/// Use the strongest variant of Kyber. It is fast and the keys are small, so there is no practical
+/// benefit of going with anything lower.
+pub const ALGORITHM_NAME: &str = "Kyber1024";
+
+// Always inline in order to try to avoid potential copies of `shared_secret` to multiple places on the stack.
+#[inline(always)]
+pub fn decapsulate(
+ secret_key: SecretKey,
+ ciphertext_slice: &[u8],
+) -> Result<[u8; 32], super::Error> {
+ // The `pqc_kyber` library takes a byte slice. But we convert it into an array
+ // in order to catch the length mismatch error and report it better than `pqc_kyber` would.
+ let ciphertext_array =
+ <[u8; KYBER_CIPHERTEXTBYTES]>::try_from(ciphertext_slice).map_err(|_| {
+ super::Error::InvalidCiphertextLength {
+ algorithm: ALGORITHM_NAME,
+ actual: ciphertext_slice.len(),
+ expected: KYBER_CIPHERTEXTBYTES,
+ }
+ })?;
+ let shared_secret = pqc_kyber::decapsulate(ciphertext_array.as_slice(), secret_key.as_slice())
+ .map_err(super::Error::FailedDecapsulateKyber)?;
+ Ok(shared_secret)
+}
diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs
index 4cb7cd2c91..a4beee9be3 100644
--- a/talpid-tunnel-config-client/src/lib.rs
+++ b/talpid-tunnel-config-client/src/lib.rs
@@ -1,8 +1,10 @@
use std::{fmt, net::IpAddr};
use talpid_types::net::wireguard::{PresharedKey, PrivateKey, PublicKey};
use tonic::transport::Channel;
+use zeroize::Zeroize;
mod classic_mceliece;
+mod kyber;
#[allow(clippy::derive_partial_eq_without_eq)]
mod proto {
@@ -13,8 +15,15 @@ mod proto {
pub enum Error {
GrpcConnectError(tonic::transport::Error),
GrpcError(tonic::Status),
- InvalidCiphertextLength { actual: usize, expected: usize },
- InvalidCiphertextCount { actual: usize },
+ InvalidCiphertextLength {
+ algorithm: &'static str,
+ actual: usize,
+ expected: usize,
+ },
+ InvalidCiphertextCount {
+ actual: usize,
+ },
+ FailedDecapsulateKyber(kyber::KyberError),
}
impl std::fmt::Display for Error {
@@ -23,13 +32,18 @@ impl std::fmt::Display for Error {
match self {
GrpcConnectError(_) => "Failed to connect to config service".fmt(f),
GrpcError(status) => write!(f, "RPC failed: {status}"),
- InvalidCiphertextLength { actual, expected } => write!(
+ InvalidCiphertextLength {
+ algorithm,
+ actual,
+ expected,
+ } => write!(
f,
- "Expected a ciphertext of length {expected}, got {actual} bytes"
+ "Expected a {expected} bytes ciphertext for {algorithm}, got {actual} bytes"
),
InvalidCiphertextCount { actual } => {
- write!(f, "Expected 1 ciphertext in the response, got {actual}")
+ write!(f, "Expected 2 ciphertext in the response, got {actual}")
}
+ FailedDecapsulateKyber(_) => "Failed to decapsulate Kyber1024 ciphertext".fmt(f),
}
}
}
@@ -38,6 +52,7 @@ impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::GrpcConnectError(error) => Some(error),
+ Self::FailedDecapsulateKyber(error) => Some(error),
_ => None,
}
}
@@ -48,10 +63,6 @@ type RelayConfigService = proto::post_quantum_secure_client::PostQuantumSecureCl
/// Port used by the tunnel config service.
pub const CONFIG_SERVICE_PORT: u16 = 1337;
-/// Use the smallest CME variant with NIST security level 3. This variant has significantly smaller
-/// keys than the larger variants, and is considered safe.
-const CLASSIC_MCELIECE_VARIANT: &str = "Classic-McEliece-460896f";
-
/// Generates a new WireGuard key pair and negotiates a PSK with the relay in a PQ-safe
/// manner. This creates a peer on the relay with the new WireGuard pubkey and PSK,
/// which can then be used to establish a PQ-safe tunnel to the relay.
@@ -62,42 +73,58 @@ pub async fn push_pq_key(
) -> Result<(PrivateKey, PresharedKey), Error> {
let wg_psk_privkey = PrivateKey::new_from_random();
let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await;
+ let kyber_keypair = kyber::keypair(&mut rand::thread_rng());
let mut client = new_client(service_address).await?;
let response = client
- .psk_exchange_experimental_v1(proto::PskRequestExperimentalV1 {
+ .psk_exchange_v1(proto::PskRequestV1 {
wg_pubkey: wg_pubkey.as_bytes().to_vec(),
wg_psk_pubkey: wg_psk_privkey.public_key().as_bytes().to_vec(),
- kem_pubkeys: vec![proto::KemPubkeyExperimentalV1 {
- algorithm_name: CLASSIC_MCELIECE_VARIANT.to_owned(),
- key_data: cme_kem_pubkey.as_array().to_vec(),
- }],
+ kem_pubkeys: vec![
+ proto::KemPubkeyV1 {
+ algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(),
+ key_data: cme_kem_pubkey.as_array().to_vec(),
+ },
+ proto::KemPubkeyV1 {
+ algorithm_name: kyber::ALGORITHM_NAME.to_owned(),
+ key_data: kyber_keypair.public.to_vec(),
+ },
+ ],
})
.await
.map_err(Error::GrpcError)?;
let ciphertexts = response.into_inner().ciphertexts;
+
// Unpack the ciphertexts into one per KEM without needing to access them by index.
- let [cme_ciphertext] = <&[Vec<u8>; 1]>::try_from(ciphertexts.as_slice()).map_err(|_| {
- Error::InvalidCiphertextCount {
+ let [cme_ciphertext, kyber_ciphertext] = <&[Vec<u8>; 2]>::try_from(ciphertexts.as_slice())
+ .map_err(|_| Error::InvalidCiphertextCount {
actual: ciphertexts.len(),
- }
- })?;
+ })?;
// Store the PSK data on the heap. So it can be passed around and then zeroized on drop without
// being stored in a bunch of places on the stack.
let mut psk_data = Box::new([0u8; 32]);
+
// Decapsulate Classic McEliece and mix into PSK
{
- let ciphertext_array =
- <[u8; classic_mceliece::CRYPTO_CIPHERTEXTBYTES]>::try_from(cme_ciphertext.as_slice())
- .map_err(|_| Error::InvalidCiphertextLength {
- actual: cme_ciphertext.len(),
- expected: classic_mceliece::CRYPTO_CIPHERTEXTBYTES,
- })?;
- let ciphertext = classic_mceliece::Ciphertext::from(ciphertext_array);
- let shared_secret = classic_mceliece::decapsulate(&cme_kem_secret, &ciphertext);
+ let mut shared_secret = classic_mceliece::decapsulate(&cme_kem_secret, cme_ciphertext)?;
xor_assign(&mut psk_data, shared_secret.as_array());
+
+ // This should happen automatically due to `SharedSecret` implementing ZeroizeOnDrop. But doing it explicitly
+ // provides a stronger guarantee that it's not accidentally removed.
+ shared_secret.zeroize();
+ }
+ // Decapsulate Kyber and mix into PSK
+ {
+ let mut shared_secret = kyber::decapsulate(kyber_keypair.secret, kyber_ciphertext)?;
+ xor_assign(&mut psk_data, &shared_secret);
+
+ // The shared secret is sadly stored in an array on the stack. So we can't get any
+ // guarantees that it's not copied around on the stack. The best we can do here
+ // is to zero out the version we have and hope the compiler optimizes out copies.
+ // https://github.com/Argyle-Software/kyber/issues/59
+ shared_secret.zeroize();
}
Ok((wg_psk_privkey, PresharedKey::from(psk_data)))