diff options
| author | Linus Färnstrand <faern@faern.net> | 2022-09-29 09:49:02 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2023-01-30 13:57:09 +0100 |
| commit | 0eb32c405c6539e63ac9ad75cf949b1ba32c6a3a (patch) | |
| tree | fa7679b68c5467898d0b457598ed4b6bcfacde09 | |
| parent | 5d52ebc582d42fe51a28e3476b48b97059d2b8d2 (diff) | |
| download | mullvadvpn-0eb32c405c6539e63ac9ad75cf949b1ba32c6a3a.tar.xz mullvadvpn-0eb32c405c6539e63ac9ad75cf949b1ba32c6a3a.zip | |
Add Kyber1024 into the PQ KEM mix
| -rw-r--r-- | Cargo.lock | 30 | ||||
| -rw-r--r-- | talpid-tunnel-config-client/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid-tunnel-config-client/examples/tuncfg-server.rs | 6 | ||||
| -rw-r--r-- | talpid-tunnel-config-client/src/kyber.rs | 7 | ||||
| -rw-r--r-- | talpid-tunnel-config-client/src/lib.rs | 43 |
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))) } |
