summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <faern@faern.net>2022-09-29 09:49:02 +0200
committerLinus Färnstrand <linus@mullvad.net>2023-01-30 13:57:09 +0100
commit0eb32c405c6539e63ac9ad75cf949b1ba32c6a3a (patch)
treefa7679b68c5467898d0b457598ed4b6bcfacde09
parent5d52ebc582d42fe51a28e3476b48b97059d2b8d2 (diff)
downloadmullvadvpn-0eb32c405c6539e63ac9ad75cf949b1ba32c6a3a.tar.xz
mullvadvpn-0eb32c405c6539e63ac9ad75cf949b1ba32c6a3a.zip
Add Kyber1024 into the PQ KEM mix
-rw-r--r--Cargo.lock30
-rw-r--r--talpid-tunnel-config-client/Cargo.toml1
-rw-r--r--talpid-tunnel-config-client/examples/tuncfg-server.rs6
-rw-r--r--talpid-tunnel-config-client/src/kyber.rs7
-rw-r--r--talpid-tunnel-config-client/src/lib.rs43
5 files changed, 68 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6bef7c336c..6dcabc7113 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,15 @@ 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",
+]
+
+[[package]]
name = "prettyplease"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2471,7 +2480,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
- "rand_core 0.6.3",
+ "rand_core 0.6.4",
]
[[package]]
@@ -2491,7 +2500,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 +2514,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 +2961,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 +3296,7 @@ version = "0.0.0"
dependencies = [
"classic-mceliece-rust",
"log",
+ "pqc_kyber",
"prost",
"rand 0.8.5",
"talpid-types",
@@ -4223,7 +4233,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/talpid-tunnel-config-client/Cargo.toml b/talpid-tunnel-config-client/Cargo.toml
index c6b2d1d655..4e78b7ad0c 100644
--- a/talpid-tunnel-config-client/Cargo.toml
+++ b/talpid-tunnel-config-client/Cargo.toml
@@ -16,6 +16,7 @@ prost = "0.11"
tower = "0.4"
tokio = "1"
classic-mceliece-rust = { version = "2.0.0", features = ["mceliece460896f"] }
+pqc_kyber = { version = "0.4.0", features = ["std", "kyber1024"] }
[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..ffd8cb505d 100644
--- a/talpid-tunnel-config-client/examples/tuncfg-server.rs
+++ b/talpid-tunnel-config-client/examples/tuncfg-server.rs
@@ -53,6 +53,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}"),
};
diff --git a/talpid-tunnel-config-client/src/kyber.rs b/talpid-tunnel-config-client/src/kyber.rs
new file mode 100644
index 0000000000..273bfd1225
--- /dev/null
+++ b/talpid-tunnel-config-client/src/kyber.rs
@@ -0,0 +1,7 @@
+use pqc_kyber::SecretKey;
+
+pub use pqc_kyber::{keypair, KYBER_CIPHERTEXTBYTES, KyberError};
+
+pub fn decapsulate(secret_key: SecretKey, ciphertext: [u8; KYBER_CIPHERTEXTBYTES]) -> Result<[u8; 32], KyberError> {
+ pqc_kyber::decapsulate(ciphertext.as_slice(), secret_key.as_slice())
+}
diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs
index 4cb7cd2c91..b8db6820f5 100644
--- a/talpid-tunnel-config-client/src/lib.rs
+++ b/talpid-tunnel-config-client/src/lib.rs
@@ -3,6 +3,7 @@ use talpid_types::net::wireguard::{PresharedKey, PrivateKey, PublicKey};
use tonic::transport::Channel;
mod classic_mceliece;
+mod kyber;
#[allow(clippy::derive_partial_eq_without_eq)]
mod proto {
@@ -15,6 +16,7 @@ pub enum Error {
GrpcError(tonic::Status),
InvalidCiphertextLength { actual: usize, expected: usize },
InvalidCiphertextCount { actual: usize },
+ FailedDecapsulateKyber(kyber::KyberError),
}
impl std::fmt::Display for Error {
@@ -28,8 +30,9 @@ impl std::fmt::Display for Error {
"Expected a ciphertext of length {expected}, 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 +41,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,
}
}
@@ -62,31 +66,39 @@ 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 {
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::KemPubkeyExperimentalV1 {
+ algorithm_name: CLASSIC_MCELIECE_VARIANT.to_owned(),
+ key_data: cme_kem_pubkey.as_array().to_vec(),
+ },
+ proto::KemPubkeyExperimentalV1 {
+ algorithm_name: "Kyber1024".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 =
@@ -99,6 +111,19 @@ pub async fn push_pq_key(
let shared_secret = classic_mceliece::decapsulate(&cme_kem_secret, &ciphertext);
xor_assign(&mut psk_data, shared_secret.as_array());
}
+ // Decapsulate Kyber and mix into PSK
+ {
+ let ciphertext_array = <[u8; kyber::KYBER_CIPHERTEXTBYTES]>::try_from(
+ kyber_ciphertext.as_slice(),
+ )
+ .map_err(|_| Error::InvalidCiphertextLength {
+ actual: kyber_ciphertext.len(),
+ expected: kyber::KYBER_CIPHERTEXTBYTES,
+ })?;
+ let shared_secret = kyber::decapsulate(kyber_keypair.secret, ciphertext_array)
+ .map_err(Error::FailedDecapsulateKyber)?;
+ xor_assign(&mut psk_data, &shared_secret);
+ }
Ok((wg_psk_privkey, PresharedKey::from(psk_data)))
}