From 90f0934c24e86be97899f232177d451e2520140f Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 7 Feb 2023 19:33:02 -0700 Subject: [PATCH] crypto_kx: use `curve25519-dalek`; MSRV 1.60 Upgrades to the latest `curve25519-dalek` v4.0.0-rc.1 release. The `x25519-dalek` crate is a wrapper whose functionality isn't really used, and we can use the `Scalar` and `MontgomeryPoint` types directly and eliminate the extra dependency. --- Cargo.lock | 116 ++++----------------------------- crypto_kx/Cargo.toml | 9 +-- crypto_kx/src/keypair.rs | 36 ++++------ crypto_kx/src/keys/public.rs | 23 +++---- crypto_kx/src/keys/secret.rs | 30 ++++----- crypto_kx/src/lib.rs | 6 +- crypto_kx/tests/lib.rs | 4 +- crypto_kx/tests/sodiumoxide.rs | 8 +-- 8 files changed, 56 insertions(+), 176 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97f7983..77c4613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" dependencies = [ - "digest 0.10.5", + "digest", ] [[package]] @@ -205,7 +205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "typenum", ] @@ -219,7 +219,7 @@ dependencies = [ "chacha20", "chacha20poly1305", "crypto_secretbox", - "curve25519-dalek 4.0.0-rc.1", + "curve25519-dalek", "rand", "rmp-serde", "salsa20", @@ -232,11 +232,11 @@ name = "crypto_kx" version = "0.2.0-pre" dependencies = [ "blake2", - "getrandom 0.2.8", - "rand_core 0.6.4", + "curve25519-dalek", + "getrandom", + "rand_core", "serdect", "sodiumoxide", - "x25519-dalek", ] [[package]] @@ -256,26 +256,13 @@ version = "0.2.0-pre" dependencies = [ "aead", "chacha20", - "getrandom 0.2.8", + "getrandom", "poly1305", - "rand_core 0.6.4", + "rand_core", "sodiumoxide", "subtle", ] -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - [[package]] name = "curve25519-dalek" version = "4.0.0-rc.1" @@ -290,15 +277,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.5" @@ -345,17 +323,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.8" @@ -365,7 +332,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -572,7 +539,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -582,16 +549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -600,7 +558,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom", ] [[package]] @@ -792,18 +750,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "typenum" version = "1.15.0" @@ -816,12 +762,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "universal-hash" version = "0.5.0" @@ -870,12 +810,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -967,34 +901,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "x25519-dalek" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" -dependencies = [ - "curve25519-dalek 3.2.0", - "rand_core 0.5.1", - "zeroize", -] - [[package]] name = "zeroize" version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] diff --git a/crypto_kx/Cargo.toml b/crypto_kx/Cargo.toml index 99567ad..657ac00 100644 --- a/crypto_kx/Cargo.toml +++ b/crypto_kx/Cargo.toml @@ -15,17 +15,12 @@ rust-version = "1.60" [dependencies] blake2 = { version = "0.10", default-features = false } +curve25519-dalek = { version = "4.0.0-rc.1", default-features = false, features = ["zeroize"] } rand_core = "0.6" # optional dependencies serdect = { version = "0.1", optional = true, default-features = false } -[target.'cfg(target_pointer_width = "32")'.dependencies] -x25519-dalek = { version = "1", default-features = false, features = ["u32_backend"] } - -[target.'cfg(target_pointer_width = "64")'.dependencies] -x25519-dalek = { version = "1", default-features = false, features = ["u64_backend"] } - [target.'cfg(target_family = "wasm")'.dependencies] getrandom = { version = "0.2", default-features = false, features = ["js"] } @@ -35,4 +30,4 @@ sodiumoxide = "0.2" [features] serde = ["serdect"] -std = ["blake2/std", "rand_core/std", "x25519-dalek/std"] +std = ["blake2/std", "rand_core/std"] diff --git a/crypto_kx/src/keypair.rs b/crypto_kx/src/keypair.rs index 9511670..d71a533 100644 --- a/crypto_kx/src/keypair.rs +++ b/crypto_kx/src/keypair.rs @@ -4,17 +4,15 @@ use rand_core::{CryptoRng, RngCore}; use crate::{ClientSessionKeys, PublicKey, SecretKey, ServerSessionKeys, SessionKey}; /// A [`SecretKey`] with its related [`PublicKey`]. -pub struct KeyPair { +pub struct Keypair { secret: SecretKey, public: PublicKey, } -impl KeyPair { - /// Generate a new random [`KeyPair`]. +impl Keypair { + /// Generate a new random [`Keypair`]. pub fn generate(csprng: impl RngCore + CryptoRng) -> Self { - let secret = SecretKey::generate(csprng); - - Self::from(secret) + SecretKey::generate(csprng).into() } /// Get the contained [`PublicKey`]. @@ -27,7 +25,7 @@ impl KeyPair { &self.secret } - /// Consume the [`KeyPair`] to extract the contained [`SecretKey`] & [`PublicKey`]. + /// Consume the [`Keypair`] to extract the contained [`SecretKey`] & [`PublicKey`]. pub fn split(self) -> (PublicKey, SecretKey) { (self.public, self.secret) } @@ -37,7 +35,6 @@ impl KeyPair { /// It's the implementation of libsodium's `crypto_kx_client_session_keys`. pub fn session_keys_to(&self, server_pk: &PublicKey) -> ClientSessionKeys { let (tx, rx) = self.gen_session_keys(server_pk, &self.public, server_pk); - ClientSessionKeys { tx, rx } } @@ -46,7 +43,6 @@ impl KeyPair { /// It's the implementation of libsodium's `crypto_kx_server_session_keys`. pub fn session_keys_from(&self, client_pk: &PublicKey) -> ServerSessionKeys { let (rx, tx) = self.gen_session_keys(client_pk, client_pk, &self.public); - ServerSessionKeys { tx, rx } } @@ -58,10 +54,8 @@ impl KeyPair { ) -> (SessionKey, SessionKey) { debug_assert!(other_pubkey == client_pk || other_pubkey == server_pk); - let shared_secret = self - .secret - .as_dalek() - .diffie_hellman(other_pubkey.as_dalek()); + // Elliptic Curve Diffie-Hellman + let shared_secret = self.secret.0 * other_pubkey.0; let mut hasher = Blake2b512::new(); @@ -75,11 +69,9 @@ impl KeyPair { } } -impl From for KeyPair { +impl From for Keypair { fn from(secret: SecretKey) -> Self { - let public_dalek = x25519_dalek::PublicKey::from(secret.as_dalek()); - let public = PublicKey::from(public_dalek.to_bytes()); - + let public = secret.public_key(); Self { secret, public } } } @@ -92,18 +84,16 @@ mod tests { #[test] fn from_secretkey_yield_same() { - let keypair = KeyPair::generate(&mut OsRng); - - let reconstructed_keypair = - KeyPair::from(SecretKey::from(keypair.secret().as_dalek().to_bytes())); + let keypair = Keypair::generate(&mut OsRng); + let reconstructed_keypair = Keypair::from(SecretKey::from(keypair.secret().to_bytes())); assert_eq!( keypair.public().as_ref(), reconstructed_keypair.public().as_ref(), ); assert_eq!( - keypair.secret().as_dalek().to_bytes(), - reconstructed_keypair.secret().as_dalek().to_bytes(), + keypair.secret().to_bytes(), + reconstructed_keypair.secret().to_bytes(), ); } } diff --git a/crypto_kx/src/keys/public.rs b/crypto_kx/src/keys/public.rs index 47da339..f961adc 100644 --- a/crypto_kx/src/keys/public.rs +++ b/crypto_kx/src/keys/public.rs @@ -1,21 +1,18 @@ //! Public key type. use crate::errors::InvalidLength; +use curve25519_dalek::MontgomeryPoint; #[cfg(feature = "serde")] use serdect::serde::{de, ser, Deserialize, Serialize}; /// [`PublicKey`] which can be freely shared. #[derive(Clone, Copy, Hash, PartialEq, Eq)] -pub struct PublicKey(x25519_dalek::PublicKey); +pub struct PublicKey(pub(crate) MontgomeryPoint); impl PublicKey { /// Size in bytes of the [`PublicKey`]. pub const BYTES: usize = 32; - - pub(crate) fn as_dalek(&self) -> &x25519_dalek::PublicKey { - &self.0 - } } impl AsRef<[u8; PublicKey::BYTES]> for PublicKey { @@ -26,7 +23,7 @@ impl AsRef<[u8; PublicKey::BYTES]> for PublicKey { impl From<[u8; PublicKey::BYTES]> for PublicKey { fn from(value: [u8; PublicKey::BYTES]) -> Self { - Self(value.into()) + Self(MontgomeryPoint(value)) } } @@ -34,14 +31,10 @@ impl TryFrom<&[u8]> for PublicKey { type Error = InvalidLength; fn try_from(slice: &[u8]) -> Result { - if slice.len() != Self::BYTES { - return Err(InvalidLength::new(Self::BYTES, slice.len())); - } - - let mut array = [0u8; PublicKey::BYTES]; - array.copy_from_slice(slice); - - Ok(Self::from(array)) + slice + .try_into() + .map(|bytes| Self(MontgomeryPoint(bytes))) + .map_err(|_| InvalidLength::new(Self::BYTES, slice.len())) } } @@ -63,6 +56,6 @@ impl<'de> Deserialize<'de> for PublicKey { { let mut bytes = [0u8; Self::BYTES]; serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?; - Self::try_from(&bytes[..]).map_err(de::Error::custom) + Ok(bytes.into()) } } diff --git a/crypto_kx/src/keys/secret.rs b/crypto_kx/src/keys/secret.rs index e32ba37..bf2b073 100644 --- a/crypto_kx/src/keys/secret.rs +++ b/crypto_kx/src/keys/secret.rs @@ -1,6 +1,7 @@ //! Secret key type. -use crate::errors::InvalidLength; +use crate::{errors::InvalidLength, PublicKey}; +use curve25519_dalek::{MontgomeryPoint, Scalar}; use rand_core::{CryptoRng, RngCore}; #[cfg(feature = "serde")] @@ -8,7 +9,7 @@ use serdect::serde::{de, ser, Deserialize, Serialize}; /// [`SecretKey`] that should be kept private. #[derive(Clone)] -pub struct SecretKey(x25519_dalek::StaticSecret); +pub struct SecretKey(pub(crate) Scalar); impl SecretKey { /// Size in bytes of the [`SecretKey`]. @@ -18,25 +19,23 @@ impl SecretKey { pub fn generate(mut csprng: impl RngCore + CryptoRng) -> Self { let mut bytes = [0u8; Self::BYTES]; csprng.fill_bytes(&mut bytes); + bytes.into() + } - let secret = x25519_dalek::StaticSecret::from(bytes); - - Self(secret) + /// Get the public key that corresponds to this [`SecretKey`]. + pub fn public_key(&self) -> PublicKey { + PublicKey(MontgomeryPoint::mul_base(&self.0)) } /// Get the bytes serialization of this [`SecretKey`]. pub fn to_bytes(&self) -> [u8; SecretKey::BYTES] { self.0.to_bytes() } - - pub(crate) fn as_dalek(&self) -> &x25519_dalek::StaticSecret { - &self.0 - } } impl From<[u8; SecretKey::BYTES]> for SecretKey { fn from(value: [u8; SecretKey::BYTES]) -> Self { - Self(value.into()) + Self(Scalar::from_bits_clamped(value)) } } @@ -44,14 +43,9 @@ impl TryFrom<&[u8]> for SecretKey { type Error = InvalidLength; fn try_from(slice: &[u8]) -> Result { - if slice.len() != Self::BYTES { - return Err(InvalidLength::new(Self::BYTES, slice.len())); - } - - let mut array = [0u8; SecretKey::BYTES]; - array.copy_from_slice(slice); - - Ok(Self::from(array)) + <[u8; SecretKey::BYTES]>::try_from(slice) + .map(Into::into) + .map_err(|_| InvalidLength::new(Self::BYTES, slice.len())) } } diff --git a/crypto_kx/src/lib.rs b/crypto_kx/src/lib.rs index 761799e..bd88967 100644 --- a/crypto_kx/src/lib.rs +++ b/crypto_kx/src/lib.rs @@ -12,8 +12,8 @@ //! use rand_core::OsRng; //! //! // Each generates a key on their machine. -//! let alice = KeyPair::generate(OsRng); -//! let betty = KeyPair::generate(OsRng); +//! let alice = Keypair::generate(OsRng); +//! let betty = Keypair::generate(OsRng); //! //! // Then Alice decides to send a message to Betty, so she computes the shared keys. //! let alice_keys = alice.session_keys_to(betty.public()); @@ -38,5 +38,5 @@ mod keys; pub mod errors; -pub use keypair::KeyPair; +pub use keypair::Keypair; pub use keys::*; diff --git a/crypto_kx/tests/lib.rs b/crypto_kx/tests/lib.rs index 7210cdb..9faa479 100644 --- a/crypto_kx/tests/lib.rs +++ b/crypto_kx/tests/lib.rs @@ -3,8 +3,8 @@ use rand_core::OsRng; #[test] fn client_keys_is_reverse_from_server_keys() { - let client = KeyPair::generate(OsRng); - let server = KeyPair::generate(OsRng); + let client = Keypair::generate(OsRng); + let server = Keypair::generate(OsRng); let client_keys = client.session_keys_to(server.public()); let server_keys = server.session_keys_from(client.public()); diff --git a/crypto_kx/tests/sodiumoxide.rs b/crypto_kx/tests/sodiumoxide.rs index 32134b9..735abce 100644 --- a/crypto_kx/tests/sodiumoxide.rs +++ b/crypto_kx/tests/sodiumoxide.rs @@ -8,7 +8,7 @@ use sodiumoxide::crypto::kx as reference; fn same_publickey_constructed_from_secretkey() { let keypair = reference::gen_keypair(); - let reconstructed_keypair = KeyPair::from( + let reconstructed_keypair = Keypair::from( SecretKey::try_from(keypair.1.as_ref()).expect("parse reference's secret key"), ); @@ -18,7 +18,7 @@ fn same_publickey_constructed_from_secretkey() { #[test] fn same_client_keys() { let (client_pk, client_sk) = reference::gen_keypair(); - let server = KeyPair::generate(OsRng); + let server = Keypair::generate(OsRng); let server_pk = server.public(); let reference_keys = reference::client_session_keys( @@ -28,7 +28,7 @@ fn same_client_keys() { ) .expect("generate reference's session's keys"); - let client = KeyPair::from(SecretKey::from(client_sk.0)); + let client = Keypair::from(SecretKey::from(client_sk.0)); let keys = client.session_keys_to(server_pk); assert_eq!(reference_keys.0.as_ref(), &keys.tx.as_ref()[..]); @@ -37,7 +37,7 @@ fn same_client_keys() { #[test] fn same_server_keys() { - let server = KeyPair::generate(OsRng); + let server = Keypair::generate(OsRng); let (client_pk, _) = reference::gen_keypair(); let reference_keys = reference::server_session_keys(