From 87567638759d81fb1ca4e5e24058776611111eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 13 Sep 2024 11:54:12 +0100 Subject: [PATCH] added support for aead in nym-crypto --- Cargo.lock | 17 ++++ Cargo.toml | 2 + common/bandwidth-controller/Cargo.toml | 2 +- .../client-core/gateways-storage/src/error.rs | 2 +- .../client-core/gateways-storage/src/types.rs | 20 +++-- .../base_client/storage/migration_helpers.rs | 8 +- .../client-core/src/client/key_manager/mod.rs | 4 +- common/client-core/src/init/types.rs | 4 +- .../gateway-client/src/client/mod.rs | 20 +++-- common/client-libs/gateway-client/src/lib.rs | 4 +- .../gateway-client/src/socket_state.rs | 8 +- common/crypto/Cargo.toml | 5 +- common/crypto/src/lib.rs | 11 +-- common/crypto/src/symmetric/aead.rs | 83 +++++++++++++++++ common/crypto/src/symmetric/mod.rs | 3 + common/crypto/src/symmetric/stream_cipher.rs | 3 +- common/gateway-requests/Cargo.toml | 2 +- .../src/authentication/encrypted_address.rs | 15 ++-- common/gateway-requests/src/iv.rs | 10 +-- common/gateway-requests/src/lib.rs | 4 +- .../src/registration/handshake/client.rs | 6 +- .../src/registration/handshake/gateway.rs | 6 +- .../src/registration/handshake/mod.rs | 20 +++-- .../{shared_key.rs => shared_key/legacy.rs} | 54 +++++------ .../registration/handshake/shared_key/mod.rs | 90 +++++++++++++++++++ .../src/registration/handshake/state.rs | 20 ++--- common/gateway-requests/src/types.rs | 28 +++--- common/gateway-storage/src/lib.rs | 6 +- common/nymsphinx/acknowledgements/Cargo.toml | 2 +- common/nymsphinx/anonymous-replies/Cargo.toml | 2 +- common/nymsphinx/params/Cargo.toml | 2 +- common/nymsphinx/params/src/lib.rs | 14 ++- .../websocket/connection_handler/fresh.rs | 19 ++-- .../websocket/connection_handler/mod.rs | 10 ++- 34 files changed, 366 insertions(+), 140 deletions(-) create mode 100644 common/crypto/src/symmetric/aead.rs rename common/gateway-requests/src/registration/handshake/{shared_key.rs => shared_key/legacy.rs} (73%) create mode 100644 common/gateway-requests/src/registration/handshake/shared_key/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 7d9dd13ca7..8395d243d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,21 @@ dependencies = [ "subtle 2.5.0", ] +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle 2.5.0", + "zeroize", +] + [[package]] name = "ahash" version = "0.7.8" @@ -4770,7 +4785,9 @@ dependencies = [ name = "nym-crypto" version = "0.4.0" dependencies = [ + "aead", "aes", + "aes-gcm-siv", "blake3", "bs58", "cipher", diff --git a/Cargo.toml b/Cargo.toml index 32a46b44a7..85fc7cb1e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,6 +169,8 @@ readme = "README.md" addr = "0.15.6" aes = "0.8.1" aes-gcm = "0.10.1" +aes-gcm-siv = "0.11.1" +aead = "0.5.2" anyhow = "1.0.89" argon2 = "0.5.0" async-trait = "0.1.82" diff --git a/common/bandwidth-controller/Cargo.toml b/common/bandwidth-controller/Cargo.toml index e56476afa6..386489ea7a 100644 --- a/common/bandwidth-controller/Cargo.toml +++ b/common/bandwidth-controller/Cargo.toml @@ -18,7 +18,7 @@ nym-ecash-time = { path = "../ecash-time" } nym-credential-storage = { path = "../credential-storage" } nym-credentials = { path = "../credentials" } nym-credentials-interface = { path = "../credentials-interface" } -nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] } +nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "stream_cipher", "aes", "hashing"] } nym-network-defaults = { path = "../network-defaults" } nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" } diff --git a/common/client-core/gateways-storage/src/error.rs b/common/client-core/gateways-storage/src/error.rs index f547164022..c867557108 100644 --- a/common/client-core/gateways-storage/src/error.rs +++ b/common/client-core/gateways-storage/src/error.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use nym_crypto::asymmetric::identity::Ed25519RecoveryError; -use nym_gateway_requests::registration::handshake::shared_key::SharedKeyConversionError; +use nym_gateway_requests::registration::handshake::SharedKeyConversionError; use thiserror::Error; #[derive(Debug, Error)] diff --git a/common/client-core/gateways-storage/src/types.rs b/common/client-core/gateways-storage/src/types.rs index 8daade3422..94888faf9c 100644 --- a/common/client-core/gateways-storage/src/types.rs +++ b/common/client-core/gateways-storage/src/types.rs @@ -4,7 +4,7 @@ use crate::BadGateway; use cosmrs::AccountId; use nym_crypto::asymmetric::identity; -use nym_gateway_requests::registration::handshake::SharedKeys; +use nym_gateway_requests::registration::handshake::LegacySharedKeys; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::str::FromStr; @@ -64,7 +64,7 @@ impl From for GatewayRegistration { impl GatewayDetails { pub fn new_remote( gateway_id: identity::PublicKey, - derived_aes128_ctr_blake3_hmac_keys: Arc, + derived_aes128_ctr_blake3_hmac_keys: Arc, gateway_owner_address: Option, gateway_listener: Url, ) -> Self { @@ -87,7 +87,7 @@ impl GatewayDetails { } } - pub fn shared_key(&self) -> Option<&SharedKeys> { + pub fn shared_key(&self) -> Option<&LegacySharedKeys> { match self { GatewayDetails::Remote(details) => Some(&details.derived_aes128_ctr_blake3_hmac_keys), GatewayDetails::Custom(_) => None, @@ -185,11 +185,13 @@ impl TryFrom for RemoteGatewayDetails { })?; let derived_aes128_ctr_blake3_hmac_keys = Arc::new( - SharedKeys::try_from_base58_string(&value.derived_aes128_ctr_blake3_hmac_keys_bs58) - .map_err(|source| BadGateway::MalformedSharedKeys { - gateway_id: value.gateway_id_bs58.clone(), - source, - })?, + LegacySharedKeys::try_from_base58_string( + &value.derived_aes128_ctr_blake3_hmac_keys_bs58, + ) + .map_err(|source| BadGateway::MalformedSharedKeys { + gateway_id: value.gateway_id_bs58.clone(), + source, + })?, ); let gateway_owner_address = value @@ -242,7 +244,7 @@ pub struct RemoteGatewayDetails { // note: `SharedKeys` implement ZeroizeOnDrop, meaning when `RemoteGatewayDetails` is dropped, // the keys will be zeroized - pub derived_aes128_ctr_blake3_hmac_keys: Arc, + pub derived_aes128_ctr_blake3_hmac_keys: Arc, pub gateway_owner_address: Option, diff --git a/common/client-core/src/client/base_client/storage/migration_helpers.rs b/common/client-core/src/client/base_client/storage/migration_helpers.rs index b03b6d13c7..91d7a0bf93 100644 --- a/common/client-core/src/client/base_client/storage/migration_helpers.rs +++ b/common/client-core/src/client/base_client/storage/migration_helpers.rs @@ -13,7 +13,7 @@ pub mod v1_1_33 { use nym_client_core_gateways_storage::{ CustomGatewayDetails, GatewayDetails, GatewayRegistration, RemoteGatewayDetails, }; - use nym_gateway_requests::registration::handshake::SharedKeys; + use nym_gateway_requests::registration::handshake::LegacySharedKeys; use serde::{Deserialize, Serialize}; use sha2::{digest::Digest, Sha256}; use std::ops::Deref; @@ -58,7 +58,7 @@ pub mod v1_1_33 { } impl PersistedGatewayConfig { - fn verify(&self, shared_key: &SharedKeys) -> bool { + fn verify(&self, shared_key: &LegacySharedKeys) -> bool { let key_bytes = Zeroizing::new(shared_key.to_bytes()); let mut key_hasher = Sha256::new(); @@ -74,7 +74,7 @@ pub mod v1_1_33 { gateway_id: String, } - fn load_shared_key>(path: P) -> Result { + fn load_shared_key>(path: P) -> Result { // the shared key was a simple pem file Ok(nym_pemstore::load_key(path)?) } @@ -83,7 +83,7 @@ pub mod v1_1_33 { gateway_id: String, gateway_owner: String, gateway_listener: String, - gateway_shared_key: SharedKeys, + gateway_shared_key: LegacySharedKeys, ) -> Result { Ok(GatewayDetails::Remote(RemoteGatewayDetails { gateway_id: gateway_id diff --git a/common/client-core/src/client/key_manager/mod.rs b/common/client-core/src/client/key_manager/mod.rs index 095980e753..01eaa88fc3 100644 --- a/common/client-core/src/client/key_manager/mod.rs +++ b/common/client-core/src/client/key_manager/mod.rs @@ -3,7 +3,7 @@ use crate::client::key_manager::persistence::KeyStore; use nym_crypto::asymmetric::{encryption, identity}; -use nym_gateway_requests::registration::handshake::SharedKeys; +use nym_gateway_requests::registration::handshake::LegacySharedKeys; use nym_sphinx::acknowledgements::AckKey; use rand::{CryptoRng, RngCore}; use std::sync::Arc; @@ -84,5 +84,5 @@ fn _assert_keys_zeroize_on_drop() { _assert_zeroize_on_drop::(); _assert_zeroize_on_drop::(); _assert_zeroize_on_drop::(); - _assert_zeroize_on_drop::(); + _assert_zeroize_on_drop::(); } diff --git a/common/client-core/src/init/types.rs b/common/client-core/src/init/types.rs index 8f1daa84b4..d0744abf62 100644 --- a/common/client-core/src/init/types.rs +++ b/common/client-core/src/init/types.rs @@ -11,7 +11,7 @@ use nym_client_core_gateways_storage::{ }; use nym_crypto::asymmetric::identity; use nym_gateway_client::client::InitGatewayClient; -use nym_gateway_requests::registration::handshake::SharedKeys; +use nym_gateway_requests::registration::handshake::LegacySharedKeys; use nym_sphinx::addressing::clients::Recipient; use nym_topology::gateway; use nym_validator_client::client::IdentityKey; @@ -104,7 +104,7 @@ impl SelectedGateway { /// - shared keys derived between ourselves and the node /// - an authenticated handle of an ephemeral handle created for the purposes of registration pub struct RegistrationResult { - pub shared_keys: Arc, + pub shared_keys: Arc, pub authenticated_ephemeral_client: InitGatewayClient, } diff --git a/common/client-libs/gateway-client/src/client/mod.rs b/common/client-libs/gateway-client/src/client/mod.rs index e6e907c307..6f29ea5b0b 100644 --- a/common/client-libs/gateway-client/src/client/mod.rs +++ b/common/client-libs/gateway-client/src/client/mod.rs @@ -19,7 +19,7 @@ use nym_credentials::CredentialSpendingData; use nym_crypto::asymmetric::identity; use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; use nym_gateway_requests::iv::IV; -use nym_gateway_requests::registration::handshake::{client_handshake, SharedKeys}; +use nym_gateway_requests::registration::handshake::{client_handshake, LegacySharedKeys}; use nym_gateway_requests::{ BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION, @@ -80,7 +80,7 @@ pub struct GatewayClient { gateway_address: String, gateway_identity: identity::PublicKey, local_identity: Arc, - shared_key: Option>, + shared_key: Option>, connection: SocketState, packet_router: PacketRouter, bandwidth_controller: Option>, @@ -98,7 +98,7 @@ impl GatewayClient { gateway_config: GatewayConfig, local_identity: Arc, // TODO: make it mandatory. if you don't want to pass it, use `new_init` - shared_key: Option>, + shared_key: Option>, packet_router: PacketRouter, bandwidth_controller: Option>, task_client: TaskClient, @@ -450,7 +450,7 @@ impl GatewayClient { async fn authenticate( &mut self, - shared_key: Option, + shared_key: Option, ) -> Result<(), GatewayClientError> { if shared_key.is_none() && self.shared_key.is_none() { return Err(GatewayClientError::NoSharedKeyAvailable); @@ -508,7 +508,13 @@ impl GatewayClient { /// Helper method to either call register or authenticate based on self.shared_key value pub async fn perform_initial_authentication( &mut self, - ) -> Result, GatewayClientError> { + ) -> Result, GatewayClientError> { + // 1. check gateway's protocol version + // 2. if error or new handshake unsupported => fallback to the old registration/authentication + // 3. otherwise continue with the updated key derivation + + // ?. if new protocol is supported and we have an old key, upgrade it to aes-gcm-siv + if self.authenticated { debug!("Already authenticated"); return if let Some(shared_key) = &self.shared_key { @@ -833,7 +839,9 @@ impl GatewayClient { Ok(()) } - pub async fn authenticate_and_start(&mut self) -> Result, GatewayClientError> + pub async fn authenticate_and_start( + &mut self, + ) -> Result, GatewayClientError> where C: DkgQueryClient + Send + Sync, St: CredentialStorage, diff --git a/common/client-libs/gateway-client/src/lib.rs b/common/client-libs/gateway-client/src/lib.rs index e37cb3e4a4..a07e03a258 100644 --- a/common/client-libs/gateway-client/src/lib.rs +++ b/common/client-libs/gateway-client/src/lib.rs @@ -7,7 +7,7 @@ use nym_gateway_requests::BinaryResponse; use tungstenite::{protocol::Message, Error as WsError}; pub use client::{config::GatewayClientConfig, GatewayClient, GatewayConfig}; -pub use nym_gateway_requests::registration::handshake::SharedKeys; +pub use nym_gateway_requests::registration::handshake::LegacySharedKeys; pub use packet_router::{ AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender, PacketRouter, @@ -45,7 +45,7 @@ pub(crate) fn cleanup_socket_messages( pub(crate) fn try_decrypt_binary_message( bin_msg: Vec, - shared_keys: &SharedKeys, + shared_keys: &LegacySharedKeys, ) -> Option> { match BinaryResponse::try_from_encrypted_tagged_bytes(bin_msg, shared_keys) { Ok(bin_response) => match bin_response { diff --git a/common/client-libs/gateway-client/src/socket_state.rs b/common/client-libs/gateway-client/src/socket_state.rs index d7754c5341..d8a7e7c747 100644 --- a/common/client-libs/gateway-client/src/socket_state.rs +++ b/common/client-libs/gateway-client/src/socket_state.rs @@ -10,7 +10,7 @@ use futures::channel::oneshot; use futures::stream::{SplitSink, SplitStream}; use futures::{SinkExt, StreamExt}; use log::*; -use nym_gateway_requests::registration::handshake::SharedKeys; +use nym_gateway_requests::registration::handshake::LegacySharedKeys; use nym_gateway_requests::{ServerResponse, SimpleGatewayRequestsError}; use nym_task::TaskClient; use std::os::raw::c_int as RawFd; @@ -62,7 +62,7 @@ pub(crate) struct PartiallyDelegatedHandle { struct PartiallyDelegatedRouter { packet_router: PacketRouter, - shared_key: Arc, + shared_key: Arc, client_bandwidth: ClientBandwidth, stream_return: SplitStreamSender, @@ -72,7 +72,7 @@ struct PartiallyDelegatedRouter { impl PartiallyDelegatedRouter { fn new( packet_router: PacketRouter, - shared_key: Arc, + shared_key: Arc, client_bandwidth: ClientBandwidth, stream_return: SplitStreamSender, stream_return_requester: oneshot::Receiver<()>, @@ -247,7 +247,7 @@ impl PartiallyDelegatedHandle { pub(crate) fn split_and_listen_for_mixnet_messages( conn: WsConn, packet_router: PacketRouter, - shared_key: Arc, + shared_key: Arc, client_bandwidth: ClientBandwidth, shutdown: TaskClient, ) -> Self { diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index 8e6da0b419..1dcb0b4957 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -8,7 +8,9 @@ license = { workspace = true } repository = { workspace = true } [dependencies] +aes-gcm-siv = { workspace = true, optional = true } aes = { workspace = true, optional = true } +aead = { workspace = true, optional = true } bs58 = { workspace = true } blake3 = { workspace = true, features = ["traits-preview"], optional = true } ctr = { workspace = true, optional = true } @@ -35,9 +37,10 @@ rand_chacha = { workspace = true } [features] default = ["sphinx"] +aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"] serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"] asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"] hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"] -symmetric = ["aes", "ctr", "cipher", "generic-array"] +stream_cipher = ["aes", "ctr", "cipher", "generic-array"] sphinx = ["nym-sphinx-types/sphinx"] outfox = ["nym-sphinx-types/outfox"] diff --git a/common/crypto/src/lib.rs b/common/crypto/src/lib.rs index 4ab59949f0..1dff7b82be 100644 --- a/common/crypto/src/lib.rs +++ b/common/crypto/src/lib.rs @@ -10,21 +10,22 @@ pub mod crypto_hash; pub mod hkdf; #[cfg(feature = "hashing")] pub mod hmac; -#[cfg(all(feature = "asymmetric", feature = "hashing", feature = "symmetric"))] +#[cfg(all(feature = "asymmetric", feature = "hashing", feature = "stream_cipher"))] pub mod shared_key; -#[cfg(feature = "symmetric")] pub mod symmetric; #[cfg(feature = "hashing")] pub use digest::{Digest, OutputSizeUser}; -#[cfg(any(feature = "hashing", feature = "symmetric"))] +#[cfg(any(feature = "hashing", feature = "stream_cipher", feature = "aead"))] pub use generic_array; // with the below my idea was to try to introduce having a single place of importing all hashing, encryption, // etc. algorithms and import them elsewhere as needed via common/crypto -#[cfg(feature = "symmetric")] +#[cfg(feature = "stream_cipher")] pub use aes; +#[cfg(feature = "aead")] +pub use aes_gcm_siv::{Aes128GcmSiv, Aes256GcmSiv}; #[cfg(feature = "hashing")] pub use blake3; -#[cfg(feature = "symmetric")] +#[cfg(feature = "stream_cipher")] pub use ctr; diff --git a/common/crypto/src/symmetric/aead.rs b/common/crypto/src/symmetric/aead.rs new file mode 100644 index 0000000000..6081d4c9c2 --- /dev/null +++ b/common/crypto/src/symmetric/aead.rs @@ -0,0 +1,83 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use aead::{Aead, AeadCore, AeadInPlace, Buffer, KeyInit, Payload}; + +#[cfg(feature = "rand")] +use rand::{CryptoRng, RngCore}; + +pub use aead::{Error as AeadError, Key as AeadKey, KeySizeUser, Nonce, Tag}; + +#[cfg(feature = "rand")] +pub fn generate_key(rng: &mut R) -> AeadKey +where + A: KeyInit, + R: RngCore + CryptoRng, +{ + let mut key = AeadKey::::default(); + rng.fill_bytes(&mut key); + key +} + +#[cfg(feature = "rand")] +pub fn random_nonce(rng: &mut R) -> Nonce +where + A: AeadCore, + R: RngCore + CryptoRng, +{ + ::generate_nonce(rng) +} + +#[inline] +pub fn encrypt<'msg, 'aad, A>( + key: &AeadKey, + nonce: &Nonce, + plaintext: impl Into>, +) -> Result, AeadError> +where + A: Aead + KeyInit, +{ + let cipher = A::new(key); + cipher.encrypt(nonce, plaintext) +} + +#[inline] +pub fn decrypt<'msg, 'aad, A>( + key: &AeadKey, + nonce: &Nonce, + ciphertext: impl Into>, +) -> Result, AeadError> +where + A: Aead + KeyInit, +{ + let cipher = A::new(key); + cipher.decrypt(nonce, ciphertext) +} + +#[inline] +pub fn encrypt_in_place( + key: &AeadKey, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut dyn Buffer, +) -> Result<(), AeadError> +where + A: AeadInPlace + KeyInit, +{ + let cipher = A::new(key); + cipher.encrypt_in_place(nonce, associated_data, buffer) +} + +#[inline] +pub fn decrypt_in_place( + key: &AeadKey, + nonce: &Nonce, + associated_data: &[u8], + buffer: &mut dyn Buffer, +) -> Result<(), AeadError> +where + A: AeadInPlace + KeyInit, +{ + let cipher = A::new(key); + cipher.decrypt_in_place(nonce, associated_data, buffer) +} diff --git a/common/crypto/src/symmetric/mod.rs b/common/crypto/src/symmetric/mod.rs index f61a2e5eb2..26e990b196 100644 --- a/common/crypto/src/symmetric/mod.rs +++ b/common/crypto/src/symmetric/mod.rs @@ -1,4 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "aead")] +pub mod aead; +#[cfg(feature = "stream_cipher")] pub mod stream_cipher; diff --git a/common/crypto/src/symmetric/stream_cipher.rs b/common/crypto/src/symmetric/stream_cipher.rs index 571ec67f08..c59843050b 100644 --- a/common/crypto/src/symmetric/stream_cipher.rs +++ b/common/crypto/src/symmetric/stream_cipher.rs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use cipher::{Iv, StreamCipher}; -pub use cipher::{IvSizeUser, KeyIvInit, KeySizeUser}; + #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; // re-export this for ease of use pub use cipher::Key as CipherKey; +pub use cipher::{IvSizeUser, KeyIvInit, KeySizeUser}; // SECURITY: // TODO: note that this is not the most secure approach here diff --git a/common/gateway-requests/Cargo.toml b/common/gateway-requests/Cargo.toml index 72a2bdfe9d..3517df78b2 100644 --- a/common/gateway-requests/Cargo.toml +++ b/common/gateway-requests/Cargo.toml @@ -21,7 +21,7 @@ thiserror = { workspace = true } tracing = { workspace = true, features = ["log"] } zeroize = { workspace = true } -nym-crypto = { path = "../crypto" } +nym-crypto = { path = "../crypto", features = ["aead"] } nym-pemstore = { path = "../pemstore" } nym-sphinx = { path = "../nymsphinx" } nym-task = { path = "../task" } diff --git a/common/gateway-requests/src/authentication/encrypted_address.rs b/common/gateway-requests/src/authentication/encrypted_address.rs index bd500932c7..f061f170a4 100644 --- a/common/gateway-requests/src/authentication/encrypted_address.rs +++ b/common/gateway-requests/src/authentication/encrypted_address.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::iv::IV; -use crate::registration::handshake::shared_key::SharedKeys; +use crate::registration::handshake::LegacySharedKeys; use nym_crypto::symmetric::stream_cipher; -use nym_sphinx::params::GatewayEncryptionAlgorithm; +use nym_sphinx::params::LegacyGatewayEncryptionAlgorithm; use nym_sphinx::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH}; use thiserror::Error; @@ -28,8 +28,8 @@ pub enum EncryptedAddressConversionError { } impl EncryptedAddressBytes { - pub fn new(address: &DestinationAddressBytes, key: &SharedKeys, iv: &IV) -> Self { - let ciphertext = stream_cipher::encrypt::( + pub fn new(address: &DestinationAddressBytes, key: &LegacySharedKeys, iv: &IV) -> Self { + let ciphertext = stream_cipher::encrypt::( key.encryption_key(), iv.inner(), address.as_bytes_ref(), @@ -40,7 +40,12 @@ impl EncryptedAddressBytes { EncryptedAddressBytes(enc_address) } - pub fn verify(&self, address: &DestinationAddressBytes, key: &SharedKeys, iv: &IV) -> bool { + pub fn verify( + &self, + address: &DestinationAddressBytes, + key: &LegacySharedKeys, + iv: &IV, + ) -> bool { self == &Self::new(address, key, iv) } diff --git a/common/gateway-requests/src/iv.rs b/common/gateway-requests/src/iv.rs index d7ab8d1c19..5db5b320bf 100644 --- a/common/gateway-requests/src/iv.rs +++ b/common/gateway-requests/src/iv.rs @@ -3,15 +3,15 @@ use nym_crypto::generic_array::{typenum::Unsigned, GenericArray}; use nym_crypto::symmetric::stream_cipher::{random_iv, IvSizeUser, IV as CryptoIV}; -use nym_sphinx::params::GatewayEncryptionAlgorithm; +use nym_sphinx::params::LegacyGatewayEncryptionAlgorithm; use rand::{CryptoRng, RngCore}; use thiserror::Error; -type NonceSize = ::IvSize; +type NonceSize = ::IvSize; // I think 'IV' looks better than 'Iv', feel free to change that. #[allow(clippy::upper_case_acronyms)] -pub struct IV(CryptoIV); +pub struct IV(CryptoIV); #[derive(Error, Debug)] // I think 'IV' looks better than 'Iv', feel free to change that. @@ -29,7 +29,7 @@ pub enum IVConversionError { impl IV { pub fn new_random(rng: &mut R) -> Self { - IV(random_iv::(rng)) + IV(random_iv::(rng)) } pub fn try_from_bytes(bytes: &[u8]) -> Result { @@ -48,7 +48,7 @@ impl IV { self.0.as_ref() } - pub fn inner(&self) -> &CryptoIV { + pub fn inner(&self) -> &CryptoIV { &self.0 } diff --git a/common/gateway-requests/src/lib.rs b/common/gateway-requests/src/lib.rs index 98feccc75b..47e30e2758 100644 --- a/common/gateway-requests/src/lib.rs +++ b/common/gateway-requests/src/lib.rs @@ -13,15 +13,17 @@ pub mod models; pub mod registration; pub mod types; -pub const CURRENT_PROTOCOL_VERSION: u8 = CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION; +pub const CURRENT_PROTOCOL_VERSION: u8 = AES_GCM_SIV_PROTOCOL_VERSION; /// Defines the current version of the communication protocol between gateway and clients. /// It has to be incremented for any breaking change. // history: // 1 - initial release // 2 - changes to client credentials structure +// 3 - change to AES-GCM-SIV and non-zero IVs pub const INITIAL_PROTOCOL_VERSION: u8 = 1; pub const CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION: u8 = 2; +pub const AES_GCM_SIV_PROTOCOL_VERSION: u8 = 3; pub type GatewayMac = HmacOutput; diff --git a/common/gateway-requests/src/registration/handshake/client.rs b/common/gateway-requests/src/registration/handshake/client.rs index 902a520bca..fe099e6178 100644 --- a/common/gateway-requests/src/registration/handshake/client.rs +++ b/common/gateway-requests/src/registration/handshake/client.rs @@ -1,8 +1,8 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::registration::handshake::shared_key::SharedKeys; use crate::registration::handshake::state::State; +use crate::registration::handshake::LegacySharedKeys; use crate::registration::handshake::{error::HandshakeError, WsItem}; use futures::future::BoxFuture; use futures::task::{Context, Poll}; @@ -15,7 +15,7 @@ use std::pin::Pin; use tungstenite::Message as WsMessage; pub(crate) struct ClientHandshake<'a> { - handshake_future: BoxFuture<'a, Result>, + handshake_future: BoxFuture<'a, Result>, } impl<'a> ClientHandshake<'a> { @@ -121,7 +121,7 @@ impl<'a> ClientHandshake<'a> { } impl<'a> Future for ClientHandshake<'a> { - type Output = Result; + type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.handshake_future).poll(cx) diff --git a/common/gateway-requests/src/registration/handshake/gateway.rs b/common/gateway-requests/src/registration/handshake/gateway.rs index a42851a320..52fe886263 100644 --- a/common/gateway-requests/src/registration/handshake/gateway.rs +++ b/common/gateway-requests/src/registration/handshake/gateway.rs @@ -1,8 +1,8 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::registration::handshake::shared_key::SharedKeys; use crate::registration::handshake::state::State; +use crate::registration::handshake::LegacySharedKeys; use crate::registration::handshake::{error::HandshakeError, WsItem}; use futures::future::BoxFuture; use futures::task::{Context, Poll}; @@ -14,7 +14,7 @@ use std::pin::Pin; use tungstenite::Message as WsMessage; pub(crate) struct GatewayHandshake<'a> { - handshake_future: BoxFuture<'a, Result>, + handshake_future: BoxFuture<'a, Result>, } impl<'a> GatewayHandshake<'a> { @@ -106,7 +106,7 @@ impl<'a> GatewayHandshake<'a> { } impl<'a> Future for GatewayHandshake<'a> { - type Output = Result; + type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.handshake_future).poll(cx) diff --git a/common/gateway-requests/src/registration/handshake/mod.rs b/common/gateway-requests/src/registration/handshake/mod.rs index d3faec7903..56804f8f55 100644 --- a/common/gateway-requests/src/registration/handshake/mod.rs +++ b/common/gateway-requests/src/registration/handshake/mod.rs @@ -3,25 +3,29 @@ use self::client::ClientHandshake; use self::error::HandshakeError; -#[cfg(not(target_arch = "wasm32"))] -use self::gateway::GatewayHandshake; -pub use self::shared_key::{SharedKeySize, SharedKeys}; use futures::{Sink, Stream}; use nym_crypto::asymmetric::identity; -#[cfg(not(target_arch = "wasm32"))] -use nym_task::TaskClient; use rand::{CryptoRng, RngCore}; use tungstenite::{Error as WsError, Message as WsMessage}; +#[cfg(not(target_arch = "wasm32"))] +use self::gateway::GatewayHandshake; + +#[cfg(not(target_arch = "wasm32"))] +use nym_task::TaskClient; + pub(crate) type WsItem = Result; mod client; pub mod error; #[cfg(not(target_arch = "wasm32"))] mod gateway; -pub mod shared_key; +mod shared_key; mod state; +pub use self::shared_key::legacy::{LegacySharedKeySize, LegacySharedKeys}; +pub use self::shared_key::{SharedKeyConversionError, SharedSymmetricKey}; + // Note: the handshake is built on top of WebSocket, but in principle it shouldn't be too difficult // to remove that restriction, by just changing Sink and Stream into // AsyncWrite and AsyncRead and slightly adjusting the implementation. But right now @@ -34,7 +38,7 @@ pub async fn client_handshake<'a, S>( gateway_pubkey: identity::PublicKey, expects_credential_usage: bool, #[cfg(not(target_arch = "wasm32"))] shutdown: TaskClient, -) -> Result +) -> Result where S: Stream + Sink + Unpin + Send + 'a, { @@ -57,7 +61,7 @@ pub async fn gateway_handshake<'a, S>( identity: &'a identity::KeyPair, received_init_payload: Vec, shutdown: TaskClient, -) -> Result +) -> Result where S: Stream + Sink + Unpin + Send + 'a, { diff --git a/common/gateway-requests/src/registration/handshake/shared_key.rs b/common/gateway-requests/src/registration/handshake/shared_key/legacy.rs similarity index 73% rename from common/gateway-requests/src/registration/handshake/shared_key.rs rename to common/gateway-requests/src/registration/handshake/shared_key/legacy.rs index 64975fe4cd..79c7382cff 100644 --- a/common/gateway-requests/src/registration/handshake/shared_key.rs +++ b/common/gateway-requests/src/registration/handshake/shared_key/legacy.rs @@ -1,6 +1,7 @@ // Copyright 2020-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::registration::handshake::shared_key::SharedKeyConversionError; use crate::{GatewayMacSize, GatewayRequestsError}; use nym_crypto::generic_array::{ typenum::{Sum, Unsigned, U16}, @@ -9,43 +10,32 @@ use nym_crypto::generic_array::{ use nym_crypto::hmac::{compute_keyed_hmac, recompute_keyed_hmac_and_verify_tag}; use nym_crypto::symmetric::stream_cipher::{self, CipherKey, KeySizeUser, IV}; use nym_pemstore::traits::PemStorableKey; -use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorithm}; +use nym_sphinx::params::{GatewayIntegrityHmacAlgorithm, LegacyGatewayEncryptionAlgorithm}; use serde::{Deserialize, Serialize}; -use thiserror::Error; use zeroize::{Zeroize, ZeroizeOnDrop}; // shared key is as long as the encryption key and the MAC key combined. -pub type SharedKeySize = Sum; +pub type LegacySharedKeySize = Sum; // we're using 16 byte long key in sphinx, so let's use the same one here type MacKeySize = U16; -type EncryptionKeySize = ::KeySize; +type EncryptionKeySize = ::KeySize; /// Shared key used when computing MAC for messages exchanged between client and its gateway. pub type MacKey = GenericArray; #[derive(Debug, PartialEq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] -pub struct SharedKeys { - encryption_key: CipherKey, +pub struct LegacySharedKeys { + encryption_key: CipherKey, mac_key: MacKey, } -#[derive(Debug, Clone, Copy, Error)] -pub enum SharedKeyConversionError { - #[error("the string representation of the shared keys was malformed - {0}")] - DecodeError(#[from] bs58::decode::Error), - #[error( - "the received shared keys had invalid size. Got: {received}, but expected: {expected}" - )] - InvalidSharedKeysSize { received: usize, expected: usize }, -} - -impl SharedKeys { +impl LegacySharedKeys { pub fn try_from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SharedKeySize::to_usize() { + if bytes.len() != LegacySharedKeySize::to_usize() { return Err(SharedKeyConversionError::InvalidSharedKeysSize { received: bytes.len(), - expected: SharedKeySize::to_usize(), + expected: LegacySharedKeySize::to_usize(), }); } @@ -53,7 +43,7 @@ impl SharedKeys { GenericArray::clone_from_slice(&bytes[..EncryptionKeySize::to_usize()]); let mac_key = GenericArray::clone_from_slice(&bytes[EncryptionKeySize::to_usize()..]); - Ok(SharedKeys { + Ok(LegacySharedKeys { encryption_key, mac_key, }) @@ -65,17 +55,17 @@ impl SharedKeys { pub fn encrypt_and_tag( &self, data: &[u8], - iv: Option<&IV>, + iv: Option<&IV>, ) -> Vec { let encrypted_data = match iv { - Some(iv) => stream_cipher::encrypt::( + Some(iv) => stream_cipher::encrypt::( self.encryption_key(), iv, data, ), None => { - let zero_iv = stream_cipher::zero_iv::(); - stream_cipher::encrypt::( + let zero_iv = stream_cipher::zero_iv::(); + stream_cipher::encrypt::( self.encryption_key(), &zero_iv, data, @@ -93,7 +83,7 @@ impl SharedKeys { pub fn decrypt_tagged( &self, enc_data: &[u8], - iv: Option<&IV>, + iv: Option<&IV>, ) -> Result, GatewayRequestsError> { let mac_size = GatewayMacSize::to_usize(); if enc_data.len() < mac_size { @@ -115,9 +105,9 @@ impl SharedKeys { // together with a mutable one let message_bytes_mut = &mut enc_data.to_vec()[mac_size..]; - let zero_iv = stream_cipher::zero_iv::(); + let zero_iv = stream_cipher::zero_iv::(); let iv = iv.unwrap_or(&zero_iv); - stream_cipher::decrypt_in_place::( + stream_cipher::decrypt_in_place::( self.encryption_key(), iv, message_bytes_mut, @@ -125,7 +115,7 @@ impl SharedKeys { Ok(message_bytes_mut.to_vec()) } - pub fn encryption_key(&self) -> &CipherKey { + pub fn encryption_key(&self) -> &CipherKey { &self.encryption_key } @@ -145,7 +135,7 @@ impl SharedKeys { val: S, ) -> Result { let decoded = bs58::decode(val.into()).into_vec()?; - SharedKeys::try_from_bytes(&decoded) + LegacySharedKeys::try_from_bytes(&decoded) } pub fn to_base58_string(&self) -> String { @@ -153,13 +143,13 @@ impl SharedKeys { } } -impl From for String { - fn from(keys: SharedKeys) -> Self { +impl From for String { + fn from(keys: LegacySharedKeys) -> Self { keys.to_base58_string() } } -impl PemStorableKey for SharedKeys { +impl PemStorableKey for LegacySharedKeys { type Error = SharedKeyConversionError; fn pem_type() -> &'static str { diff --git a/common/gateway-requests/src/registration/handshake/shared_key/mod.rs b/common/gateway-requests/src/registration/handshake/shared_key/mod.rs new file mode 100644 index 0000000000..0c7b13f18d --- /dev/null +++ b/common/gateway-requests/src/registration/handshake/shared_key/mod.rs @@ -0,0 +1,90 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::GatewayRequestsError; +use nym_crypto::generic_array::{typenum::Unsigned, GenericArray}; +use nym_crypto::symmetric::aead::{self, AeadKey, KeySizeUser, Nonce}; +use nym_pemstore::traits::PemStorableKey; +use nym_sphinx::params::GatewayEncryptionAlgorithm; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; + +pub mod legacy; + +#[derive(Debug, PartialEq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] +pub struct SharedSymmetricKey(AeadKey); + +type KeySize = ::KeySize; + +#[derive(Debug, Clone, Copy, Error)] +pub enum SharedKeyConversionError { + #[error("the string representation of the shared key was malformed: {0}")] + DecodeError(#[from] bs58::decode::Error), + #[error( + "the received shared keys had invalid size. Got: {received}, but expected: {expected}" + )] + InvalidSharedKeysSize { received: usize, expected: usize }, +} + +impl SharedSymmetricKey { + pub fn try_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != KeySize::to_usize() { + return Err(SharedKeyConversionError::InvalidSharedKeysSize { + received: bytes.len(), + expected: KeySize::to_usize(), + }); + } + + Ok(SharedSymmetricKey(GenericArray::clone_from_slice(bytes))) + } + + pub fn to_bytes(&self) -> Vec { + self.0.iter().copied().collect() + } + + pub fn try_from_base58_string>( + val: S, + ) -> Result { + let bs58_str = Zeroizing::new(val.into()); + let decoded = Zeroizing::new(bs58::decode(bs58_str).into_vec()?); + Self::try_from_bytes(&decoded) + } + + pub fn to_base58_string(&self) -> String { + let bytes = Zeroizing::new(self.to_bytes()); + bs58::encode(bytes).into_string() + } + + pub fn encrypt( + &self, + plaintext: &[u8], + nonce: &Nonce, + ) -> Result, GatewayRequestsError> { + aead::encrypt::(&self.0, &nonce, plaintext).map_err(Into::into) + } + + pub fn decrypt( + &self, + ciphertext: &[u8], + nonce: &Nonce, + ) -> Result, GatewayRequestsError> { + aead::decrypt::(&self.0, &nonce, ciphertext).map_err(Into::into) + } +} + +impl PemStorableKey for SharedSymmetricKey { + type Error = SharedKeyConversionError; + + fn pem_type() -> &'static str { + "AES-256-GCM-SIV GATEWAY SHARED KEY" + } + + fn to_bytes(&self) -> Vec { + self.to_bytes() + } + + fn from_bytes(bytes: &[u8]) -> Result { + Self::try_from_bytes(bytes) + } +} diff --git a/common/gateway-requests/src/registration/handshake/state.rs b/common/gateway-requests/src/registration/handshake/state.rs index 70cd48620c..8a7351d22f 100644 --- a/common/gateway-requests/src/registration/handshake/state.rs +++ b/common/gateway-requests/src/registration/handshake/state.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::registration::handshake::error::HandshakeError; -use crate::registration::handshake::shared_key::{SharedKeySize, SharedKeys}; use crate::registration::handshake::WsItem; +use crate::registration::handshake::{LegacySharedKeySize, LegacySharedKeys}; use crate::types; use futures::{Sink, SinkExt, Stream, StreamExt}; use nym_crypto::{ @@ -12,7 +12,7 @@ use nym_crypto::{ hkdf, symmetric::stream_cipher, }; -use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewaySharedKeyHkdfAlgorithm}; +use nym_sphinx::params::{GatewaySharedKeyHkdfAlgorithm, LegacyGatewayEncryptionAlgorithm}; #[cfg(not(target_arch = "wasm32"))] use nym_task::TaskClient; use rand::{CryptoRng, RngCore}; @@ -41,7 +41,7 @@ pub(crate) struct State<'a, S> { ephemeral_keypair: encryption::KeyPair, /// The derived shared key using the ephemeral keys of both parties. - derived_shared_keys: Option, + derived_shared_keys: Option, /// The known or received public identity key of the remote. /// Ideally it would always be known before the handshake was initiated. @@ -128,12 +128,12 @@ impl<'a, S> State<'a, S> { None, &dh_result, None, - SharedKeySize::to_usize(), + LegacySharedKeySize::to_usize(), ) .expect("somehow too long okm was provided"); let derived_shared_key = - SharedKeys::try_from_bytes(&okm).expect("okm was expanded to incorrect length!"); + LegacySharedKeys::try_from_bytes(&okm).expect("okm was expanded to incorrect length!"); self.derived_shared_keys = Some(derived_shared_key) } @@ -153,8 +153,8 @@ impl<'a, S> State<'a, S> { .collect(); let signature = self.identity.private_key().sign(message); - let zero_iv = stream_cipher::zero_iv::(); - stream_cipher::encrypt::( + let zero_iv = stream_cipher::zero_iv::(); + stream_cipher::encrypt::( self.derived_shared_keys.as_ref().unwrap().encryption_key(), &zero_iv, &signature.to_bytes(), @@ -178,8 +178,8 @@ impl<'a, S> State<'a, S> { .expect("shared key was not derived!"); // first decrypt received data - let zero_iv = stream_cipher::zero_iv::(); - let decrypted_signature = stream_cipher::decrypt::( + let zero_iv = stream_cipher::zero_iv::(); + let decrypted_signature = stream_cipher::decrypt::( derived_shared_key.encryption_key(), &zero_iv, remote_material, @@ -320,7 +320,7 @@ impl<'a, S> State<'a, S> { /// Finish the handshake, yielding the derived shared key and implicitly dropping all borrowed /// values. - pub(crate) fn finalize_handshake(self) -> SharedKeys { + pub(crate) fn finalize_handshake(self) -> LegacySharedKeys { self.derived_shared_keys.unwrap() } } diff --git a/common/gateway-requests/src/types.rs b/common/gateway-requests/src/types.rs index 07145dab8d..d55ca70b72 100644 --- a/common/gateway-requests/src/types.rs +++ b/common/gateway-requests/src/types.rs @@ -4,7 +4,7 @@ use crate::authentication::encrypted_address::EncryptedAddressBytes; use crate::iv::{IVConversionError, IV}; use crate::models::CredentialSpendingRequest; -use crate::registration::handshake::SharedKeys; +use crate::registration::handshake::LegacySharedKeys; use crate::{GatewayMacSize, CURRENT_PROTOCOL_VERSION, INITIAL_PROTOCOL_VERSION}; use nym_credentials::ecash::bandwidth::CredentialSpendingData; use nym_credentials_interface::CompactEcashError; @@ -14,11 +14,12 @@ use nym_crypto::symmetric::stream_cipher; use nym_sphinx::addressing::nodes::NymNodeRoutingAddressError; use nym_sphinx::forwarding::packet::{MixPacket, MixPacketFormattingError}; use nym_sphinx::params::packet_sizes::PacketSize; -use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorithm}; +use nym_sphinx::params::{GatewayIntegrityHmacAlgorithm, LegacyGatewayEncryptionAlgorithm}; use nym_sphinx::DestinationAddressBytes; use serde::{Deserialize, Serialize}; use tracing::log::error; +use nym_crypto::symmetric::aead::AeadError; use std::str::FromStr; use std::string::FromUtf8Error; use thiserror::Error; @@ -154,6 +155,9 @@ pub enum GatewayRequestsError { #[error("the provided [v1] credential has invalid number of parameters - {0}")] InvalidNumberOfEmbededParameters(u32), + #[error("failed to either encrypt or decrypt provided message")] + AeadFailure(#[from] AeadError), + // variant to catch legacy errors #[error("{0}")] Other(String), @@ -236,7 +240,7 @@ impl ClientControlRequest { pub fn new_enc_ecash_credential( credential: CredentialSpendingData, - shared_key: &SharedKeys, + shared_key: &LegacySharedKeys, iv: IV, ) -> Self { let cred = CredentialSpendingRequest::new(credential); @@ -251,7 +255,7 @@ impl ClientControlRequest { pub fn try_from_enc_ecash_credential( enc_credential: Vec, - shared_key: &SharedKeys, + shared_key: &LegacySharedKeys, iv: Vec, ) -> Result { let iv = IV::try_from_bytes(&iv)?; @@ -388,7 +392,7 @@ pub enum BinaryRequest { impl BinaryRequest { pub fn try_from_encrypted_tagged_bytes( raw_req: Vec, - shared_keys: &SharedKeys, + shared_keys: &LegacySharedKeys, ) -> Result { let message_bytes = &shared_keys.decrypt_tagged(&raw_req, None)?; @@ -398,7 +402,7 @@ impl BinaryRequest { Ok(BinaryRequest::ForwardSphinx(mix_packet)) } - pub fn into_encrypted_tagged_bytes(self, shared_key: &SharedKeys) -> Vec { + pub fn into_encrypted_tagged_bytes(self, shared_key: &LegacySharedKeys) -> Vec { match self { BinaryRequest::ForwardSphinx(mix_packet) => { let forwarding_data = match mix_packet.into_bytes() { @@ -421,7 +425,7 @@ impl BinaryRequest { BinaryRequest::ForwardSphinx(mix_packet) } - pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message { + pub fn into_ws_message(self, shared_key: &LegacySharedKeys) -> Message { Message::Binary(self.into_encrypted_tagged_bytes(shared_key)) } } @@ -434,7 +438,7 @@ pub enum BinaryResponse { impl BinaryResponse { pub fn try_from_encrypted_tagged_bytes( raw_req: Vec, - shared_keys: &SharedKeys, + shared_keys: &LegacySharedKeys, ) -> Result { let mac_size = GatewayMacSize::to_usize(); if raw_req.len() < mac_size { @@ -452,8 +456,8 @@ impl BinaryResponse { return Err(GatewayRequestsError::InvalidMac); } - let zero_iv = stream_cipher::zero_iv::(); - let plaintext = stream_cipher::decrypt::( + let zero_iv = stream_cipher::zero_iv::(); + let plaintext = stream_cipher::decrypt::( shared_keys.encryption_key(), &zero_iv, message_bytes, @@ -462,7 +466,7 @@ impl BinaryResponse { Ok(BinaryResponse::PushedMixMessage(plaintext)) } - pub fn into_encrypted_tagged_bytes(self, shared_key: &SharedKeys) -> Vec { + pub fn into_encrypted_tagged_bytes(self, shared_key: &LegacySharedKeys) -> Vec { match self { // TODO: it could be theoretically slightly more efficient if the data wasn't taken // by reference because then it makes a copy for encryption rather than do it in place @@ -474,7 +478,7 @@ impl BinaryResponse { BinaryResponse::PushedMixMessage(msg) } - pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message { + pub fn into_ws_message(self, shared_key: &LegacySharedKeys) -> Message { Message::Binary(self.into_encrypted_tagged_bytes(shared_key)) } } diff --git a/common/gateway-storage/src/lib.rs b/common/gateway-storage/src/lib.rs index 0cc49f5478..24fd0ee7b1 100644 --- a/common/gateway-storage/src/lib.rs +++ b/common/gateway-storage/src/lib.rs @@ -11,7 +11,7 @@ use models::{ VerifiedTicket, WireguardPeer, }; use nym_credentials_interface::ClientTicket; -use nym_gateway_requests::registration::handshake::SharedKeys; +use nym_gateway_requests::registration::handshake::LegacySharedKeys; use nym_sphinx::DestinationAddressBytes; use shared_keys::SharedKeysManager; use sqlx::ConnectOptions; @@ -46,7 +46,7 @@ pub trait Storage: Send + Sync { async fn insert_shared_keys( &self, client_address: DestinationAddressBytes, - shared_keys: &SharedKeys, + shared_keys: &LegacySharedKeys, ) -> Result; /// Tries to retrieve shared keys stored for the particular client. @@ -330,7 +330,7 @@ impl Storage for PersistentStorage { async fn insert_shared_keys( &self, client_address: DestinationAddressBytes, - shared_keys: &SharedKeys, + shared_keys: &LegacySharedKeys, ) -> Result { let client_id = self .client_manager diff --git a/common/nymsphinx/acknowledgements/Cargo.toml b/common/nymsphinx/acknowledgements/Cargo.toml index 8092813cea..117bc6b7d5 100644 --- a/common/nymsphinx/acknowledgements/Cargo.toml +++ b/common/nymsphinx/acknowledgements/Cargo.toml @@ -14,7 +14,7 @@ generic-array = { workspace = true, optional = true, features = ["serde"] } thiserror = { workspace = true } zeroize = { workspace = true } -nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] } +nym-crypto = { path = "../../crypto", features = ["stream_cipher", "rand"] } nym-pemstore = { path = "../../pemstore" } nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-params = { path = "../params" } diff --git a/common/nymsphinx/anonymous-replies/Cargo.toml b/common/nymsphinx/anonymous-replies/Cargo.toml index f8a1174d62..84c21d0bd9 100644 --- a/common/nymsphinx/anonymous-replies/Cargo.toml +++ b/common/nymsphinx/anonymous-replies/Cargo.toml @@ -13,7 +13,7 @@ bs58 = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } -nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] } +nym-crypto = { path = "../../crypto", features = ["stream_cipher", "rand"] } nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-params = { path = "../params" } nym-sphinx-routing = { path = "../routing" } diff --git a/common/nymsphinx/params/Cargo.toml b/common/nymsphinx/params/Cargo.toml index 7933943630..bf477580f4 100644 --- a/common/nymsphinx/params/Cargo.toml +++ b/common/nymsphinx/params/Cargo.toml @@ -11,7 +11,7 @@ repository = { workspace = true } thiserror = { workspace = true } serde = { workspace = true, features = ["derive"] } -nym-crypto = { path = "../../crypto", features = ["hashing", "symmetric"] } +nym-crypto = { path = "../../crypto", features = ["hashing", "stream_cipher", "aes-gcm-siv"] } nym-sphinx-types = { path = "../types" } [features] diff --git a/common/nymsphinx/params/src/lib.rs b/common/nymsphinx/params/src/lib.rs index 7ad247dfa5..9d899a426b 100644 --- a/common/nymsphinx/params/src/lib.rs +++ b/common/nymsphinx/params/src/lib.rs @@ -4,6 +4,7 @@ use nym_crypto::aes::Aes128; use nym_crypto::blake3; use nym_crypto::ctr; +use nym_crypto::Aes256GcmSiv; type Aes128Ctr = ctr::Ctr64BE; @@ -48,7 +49,7 @@ pub type GatewaySharedKeyHkdfAlgorithm = blake3::Hasher; pub type ReplySurbKeyDigestAlgorithm = blake3::Hasher; /// Hashing algorithm used when computing integrity (H)Mac for message exchanged between client and gateway. -// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\shared_key +// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\legacy_shared_key // needs updating! pub type GatewayIntegrityHmacAlgorithm = blake3::Hasher; @@ -59,11 +60,16 @@ pub type GatewayIntegrityHmacAlgorithm = blake3::Hasher; // - the pem type defined in nym\common\nymsphinx\acknowledgements\src\key needs updating! pub type AckEncryptionAlgorithm = Aes128Ctr; +/// Legacy encryption algorithm used for end-to-end encryption of messages exchanged between clients +/// and their gateways. +// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\legacy_shared_key +// needs updating! +pub type LegacyGatewayEncryptionAlgorithm = Aes128Ctr; + /// Encryption algorithm used for end-to-end encryption of messages exchanged between clients /// and their gateways. -// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\shared_key -// needs updating! -pub type GatewayEncryptionAlgorithm = Aes128Ctr; +// NOTE: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\shared_key +pub type GatewayEncryptionAlgorithm = Aes256GcmSiv; /// Encryption algorithm used for end-to-end encryption of messages exchanged between clients that are /// encapsulated inside sphinx packets. diff --git a/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs b/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs index a4beeb1cc6..a2337c8955 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/fresh.rs @@ -21,10 +21,11 @@ use nym_crypto::asymmetric::identity; use nym_gateway_requests::authentication::encrypted_address::{ EncryptedAddressBytes, EncryptedAddressConversionError, }; -use nym_gateway_requests::registration::handshake::shared_key::SharedKeyConversionError; use nym_gateway_requests::{ iv::{IVConversionError, IV}, - registration::handshake::{error::HandshakeError, gateway_handshake, SharedKeys}, + registration::handshake::{ + error::HandshakeError, gateway_handshake, LegacySharedKeys, SharedKeyConversionError, + }, types::{ClientControlRequest, ServerResponse}, BinaryResponse, CURRENT_PROTOCOL_VERSION, INITIAL_PROTOCOL_VERSION, }; @@ -177,7 +178,7 @@ where async fn perform_registration_handshake( &mut self, init_msg: Vec, - ) -> Result + ) -> Result where S: AsyncRead + AsyncWrite + Unpin + Send, { @@ -258,7 +259,7 @@ where /// * `packets`: unwrapped packets that are to be pushed back to the client. pub(crate) async fn push_packets_to_client( &mut self, - shared_keys: &SharedKeys, + shared_keys: &LegacySharedKeys, packets: Vec>, ) -> Result<(), WsError> where @@ -313,7 +314,7 @@ where async fn push_stored_messages_to_client( &mut self, client_address: DestinationAddressBytes, - shared_keys: &SharedKeys, + shared_keys: &LegacySharedKeys, ) -> Result<(), InitialAuthenticationError> where S: AsyncRead + AsyncWrite + Unpin, @@ -367,7 +368,7 @@ where client_address: DestinationAddressBytes, encrypted_address: EncryptedAddressBytes, iv: IV, - ) -> Result, InitialAuthenticationError> { + ) -> Result, InitialAuthenticationError> { let shared_keys = self .shared_state .storage @@ -378,7 +379,7 @@ where // this should never fail as we only ever construct persisted shared keys ourselves when inserting // data to the storage. The only way it could fail is if we somehow changed implementation without // performing proper migration - let keys = SharedKeys::try_from_base58_string( + let keys = LegacySharedKeys::try_from_base58_string( shared_keys.derived_aes128_ctr_blake3_hmac_keys_bs58, ) .map_err(|source| { @@ -451,7 +452,7 @@ where client_address: DestinationAddressBytes, encrypted_address: EncryptedAddressBytes, iv: IV, - ) -> Result, InitialAuthenticationError> + ) -> Result, InitialAuthenticationError> where S: AsyncRead + AsyncWrite + Unpin, { @@ -616,7 +617,7 @@ where async fn register_client( &mut self, client_address: DestinationAddressBytes, - client_shared_keys: &SharedKeys, + client_shared_keys: &LegacySharedKeys, ) -> Result where S: AsyncRead + AsyncWrite + Unpin, diff --git a/gateway/src/node/client_handling/websocket/connection_handler/mod.rs b/gateway/src/node/client_handling/websocket/connection_handler/mod.rs index 6251ab86bb..fa78d8fe8b 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/mod.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/mod.rs @@ -3,7 +3,7 @@ use crate::config::Config; use nym_credential_verification::BandwidthFlushingBehaviourConfig; -use nym_gateway_requests::registration::handshake::SharedKeys; +use nym_gateway_requests::registration::handshake::LegacySharedKeys; use nym_gateway_requests::ServerResponse; use nym_gateway_storage::Storage; use nym_sphinx::DestinationAddressBytes; @@ -46,11 +46,15 @@ pub(crate) struct ClientDetails { #[zeroize(skip)] pub(crate) address: DestinationAddressBytes, pub(crate) id: i64, - pub(crate) shared_keys: SharedKeys, + pub(crate) shared_keys: LegacySharedKeys, } impl ClientDetails { - pub(crate) fn new(id: i64, address: DestinationAddressBytes, shared_keys: SharedKeys) -> Self { + pub(crate) fn new( + id: i64, + address: DestinationAddressBytes, + shared_keys: LegacySharedKeys, + ) -> Self { ClientDetails { address, id,