diff --git a/clients/client-core/src/client/key_manager.rs b/clients/client-core/src/client/key_manager.rs index cad8f726d4c..f852af38069 100644 --- a/clients/client-core/src/client/key_manager.rs +++ b/clients/client-core/src/client/key_manager.rs @@ -61,8 +61,8 @@ impl KeyManager { // I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute #[allow(dead_code)] /// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`]. - pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: SharedKeys) { - self.gateway_shared_key = Some(Arc::new(gateway_shared_key)) + pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: Arc) { + self.gateway_shared_key = Some(gateway_shared_key) } /// Loads previously stored keys from the disk. diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 2035daf78ca..cb37d96b27e 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -79,7 +79,7 @@ async fn register_with_gateway( gateway: &gateway::Node, our_identity: Arc, validator_urls: Vec, -) -> SharedKeys { +) -> Arc { let timeout = Duration::from_millis(1500); let coconut_credential = prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await; @@ -95,7 +95,7 @@ async fn register_with_gateway( .await .expect("failed to establish connection with the gateway!"); gateway_client - .register() + .perform_initial_authentication() .await .expect("failed to register with the gateway!") } diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 99e8eff7163..58916dd45ce 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -79,7 +79,7 @@ async fn register_with_gateway( gateway: &gateway::Node, our_identity: Arc, validator_urls: Vec, -) -> SharedKeys { +) -> Arc { let timeout = Duration::from_millis(1500); let coconut_credential = prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await; @@ -95,7 +95,7 @@ async fn register_with_gateway( .await .expect("failed to establish connection with the gateway!"); gateway_client - .register() + .perform_initial_authentication() .await .expect("failed to register with the gateway!") } diff --git a/clients/tauri-client/src-tauri/src/main.rs b/clients/tauri-client/src-tauri/src/main.rs index 7c5bb06df79..bef090400f0 100644 --- a/clients/tauri-client/src-tauri/src/main.rs +++ b/clients/tauri-client/src-tauri/src/main.rs @@ -144,7 +144,7 @@ async fn verify_credential( .ok_or("Got invalid signature idx")?, ); - Ok(credential.verify(&verification_key).await) + Ok(credential.verify(&verification_key)) } #[tauri::command] diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index 52bdc40a35a..d250bc720d9 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -12,7 +12,7 @@ use coconut_interface::Credential; use crypto::asymmetric::identity; use futures::{FutureExt, SinkExt, StreamExt}; use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; -use gateway_requests::authentication::iv::AuthenticationIV; +use gateway_requests::iv::IV; use gateway_requests::registration::handshake::{client_handshake, SharedKeys}; use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse}; use log::*; @@ -36,6 +36,8 @@ const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5); pub struct GatewayClient { authenticated: bool, + // TODO: This should be replaced by an actual bandwidth value, with 0 meaning no bandwidth + has_bandwidth: bool, gateway_address: String, gateway_identity: identity::PublicKey, local_identity: Arc, @@ -70,6 +72,7 @@ impl GatewayClient { ) -> Self { GatewayClient { authenticated: false, + has_bandwidth: false, gateway_address, gateway_identity, local_identity, @@ -114,6 +117,7 @@ impl GatewayClient { GatewayClient { authenticated: false, + has_bandwidth: false, gateway_address, gateway_identity, local_identity, @@ -362,7 +366,7 @@ impl GatewayClient { } } - pub async fn register(&mut self) -> Result { + async fn register(&mut self) -> Result<(), GatewayClientError> { if !self.connection.is_established() { return Err(GatewayClientError::ConnectionNotEstablished); } @@ -379,21 +383,26 @@ impl GatewayClient { ws_stream, self.local_identity.as_ref(), self.gateway_identity, - self.coconut_credential.clone(), ) .await .map_err(GatewayClientError::RegistrationFailure), _ => unreachable!(), }?; - - self.authenticated = true; - Ok(shared_key) + self.authenticated = match self.read_control_response().await? { + ServerResponse::Register { status } => Ok(status), + ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), + _ => Err(GatewayClientError::UnexpectedResponse), + }?; + if self.authenticated { + self.shared_key = Some(Arc::new(shared_key)); + } + Ok(()) } - pub async fn authenticate( + async fn authenticate( &mut self, shared_key: Option, - ) -> Result { + ) -> Result<(), GatewayClientError> { if shared_key.is_none() && self.shared_key.is_none() { return Err(GatewayClientError::NoSharedKeyAvailable); } @@ -409,7 +418,7 @@ impl GatewayClient { let shared_key = shared_key .as_ref() .unwrap_or_else(|| self.shared_key.as_ref().unwrap()); - let iv = AuthenticationIV::new_random(&mut rng); + let iv = IV::new_random(&mut rng); let self_address = self .local_identity .as_ref() @@ -420,15 +429,14 @@ impl GatewayClient { let msg = ClientControlRequest::new_authenticate(self_address, encrypted_address, iv).into(); - let authenticated = match self.send_websocket_message(msg).await? { + match self.send_websocket_message(msg).await? { ServerResponse::Authenticate { status } => { self.authenticated = status; - Ok(status) + Ok(()) } ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), - _ => unreachable!(), - }?; - Ok(authenticated) + _ => Err(GatewayClientError::UnexpectedResponse), + } } /// Helper method to either call register or authenticate based on self.shared_key value @@ -438,8 +446,7 @@ impl GatewayClient { if self.shared_key.is_some() { self.authenticate(None).await?; } else { - let shared_key = self.register().await?; - self.shared_key = Some(Arc::new(shared_key)); + self.register().await?; } if self.authenticated { // if we are authenticated it means we MUST have an associated shared_key @@ -449,6 +456,32 @@ impl GatewayClient { } } + pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError> { + if !self.authenticated { + return Err(GatewayClientError::NotAuthenticated); + } + if self.shared_key.is_none() { + return Err(GatewayClientError::NoSharedKeyAvailable); + } + + let mut rng = OsRng; + let iv = IV::new_random(&mut rng); + + let msg = ClientControlRequest::new_enc_bandwidth_credential( + &self.coconut_credential, + self.shared_key.as_ref().unwrap(), + iv, + ) + .ok_or(GatewayClientError::SerializeCredential)? + .into(); + self.has_bandwidth = match self.send_websocket_message(msg).await? { + ServerResponse::Bandwidth { status } => Ok(status), + ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), + _ => Err(GatewayClientError::UnexpectedResponse), + }?; + Ok(()) + } + pub async fn batch_send_mix_packets( &mut self, packets: Vec, @@ -456,6 +489,9 @@ impl GatewayClient { if !self.authenticated { return Err(GatewayClientError::NotAuthenticated); } + if !self.has_bandwidth { + return Err(GatewayClientError::NotEnoughBandwidth); + } if !self.connection.is_established() { return Err(GatewayClientError::ConnectionNotEstablished); } @@ -514,6 +550,9 @@ impl GatewayClient { if !self.authenticated { return Err(GatewayClientError::NotAuthenticated); } + if !self.has_bandwidth { + return Err(GatewayClientError::NotEnoughBandwidth); + } if !self.connection.is_established() { return Err(GatewayClientError::ConnectionNotEstablished); } @@ -559,6 +598,9 @@ impl GatewayClient { if !self.authenticated { return Err(GatewayClientError::NotAuthenticated); } + if !self.has_bandwidth { + return Err(GatewayClientError::NotEnoughBandwidth); + } if self.connection.is_partially_delegated() { return Ok(()); } @@ -591,6 +633,7 @@ impl GatewayClient { self.establish_connection().await?; } let shared_key = self.perform_initial_authentication().await?; + self.claim_bandwidth().await?; // this call is NON-blocking self.start_listening_for_mixnet_messages()?; diff --git a/common/client-libs/gateway-client/src/error.rs b/common/client-libs/gateway-client/src/error.rs index 2f9938b57a8..170314e4847 100644 --- a/common/client-libs/gateway-client/src/error.rs +++ b/common/client-libs/gateway-client/src/error.rs @@ -21,7 +21,10 @@ pub enum GatewayClientError { NoSharedKeyAvailable, ConnectionAbruptlyClosed, MalformedResponse, + SerializeCredential, NotAuthenticated, + NotEnoughBandwidth, + UnexpectedResponse, ConnectionInInvalidState, RegistrationFailure(HandshakeError), AuthenticationFailure, @@ -98,6 +101,13 @@ impl fmt::Display for GatewayClientError { GatewayClientError::GatewayError(err) => { write!(f, "gateway returned an error response - {}", err) } + GatewayClientError::UnexpectedResponse => write!(f, "received an unexpected response"), + GatewayClientError::NotEnoughBandwidth => { + write!(f, "client does not have enough bandwidth") + } + GatewayClientError::SerializeCredential => { + write!(f, "credential could not be serialized") + } } } } diff --git a/common/coconut-interface/src/lib.rs b/common/coconut-interface/src/lib.rs index 823dfb562d0..b068e7525c1 100644 --- a/common/coconut-interface/src/lib.rs +++ b/common/coconut-interface/src/lib.rs @@ -45,7 +45,7 @@ impl Credential { .collect() } - pub async fn verify(&self, verification_key: &VerificationKey) -> bool { + pub fn verify(&self, verification_key: &VerificationKey) -> bool { let params = Parameters::new(self.n_params).unwrap(); coconut_rs::verify_credential( ¶ms, diff --git a/gateway/gateway-requests/src/authentication/encrypted_address.rs b/gateway/gateway-requests/src/authentication/encrypted_address.rs index 73ae885dfdf..36c5f969edf 100644 --- a/gateway/gateway-requests/src/authentication/encrypted_address.rs +++ b/gateway/gateway-requests/src/authentication/encrypted_address.rs @@ -1,7 +1,7 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::authentication::iv::AuthenticationIV; +use crate::iv::IV; use crate::registration::handshake::shared_key::SharedKeys; use crypto::symmetric::stream_cipher; use nymsphinx::params::GatewayEncryptionAlgorithm; @@ -23,7 +23,7 @@ pub enum EncryptedAddressConversionError { } impl EncryptedAddressBytes { - pub fn new(address: &DestinationAddressBytes, key: &SharedKeys, iv: &AuthenticationIV) -> Self { + pub fn new(address: &DestinationAddressBytes, key: &SharedKeys, iv: &IV) -> Self { let ciphertext = stream_cipher::encrypt::( key.encryption_key(), iv.inner(), @@ -35,12 +35,7 @@ impl EncryptedAddressBytes { EncryptedAddressBytes(enc_address) } - pub fn verify( - &self, - address: &DestinationAddressBytes, - key: &SharedKeys, - iv: &AuthenticationIV, - ) -> bool { + pub fn verify(&self, address: &DestinationAddressBytes, key: &SharedKeys, iv: &IV) -> bool { self == &Self::new(address, key, iv) } diff --git a/gateway/gateway-requests/src/authentication/mod.rs b/gateway/gateway-requests/src/authentication/mod.rs index cf1ba5aab34..3e2a4c4d5a7 100644 --- a/gateway/gateway-requests/src/authentication/mod.rs +++ b/gateway/gateway-requests/src/authentication/mod.rs @@ -2,4 +2,3 @@ // SPDX-License-Identifier: Apache-2.0 pub mod encrypted_address; -pub mod iv; diff --git a/gateway/gateway-requests/src/authentication/iv.rs b/gateway/gateway-requests/src/iv.rs similarity index 81% rename from gateway/gateway-requests/src/authentication/iv.rs rename to gateway/gateway-requests/src/iv.rs index a0964c96bf6..95fae40095e 100644 --- a/gateway/gateway-requests/src/authentication/iv.rs +++ b/gateway/gateway-requests/src/iv.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crypto::generic_array::{typenum::Unsigned, GenericArray}; -use crypto::symmetric::stream_cipher::{random_iv, NewStreamCipher, IV}; +use crypto::symmetric::stream_cipher::{random_iv, NewStreamCipher, IV as CryptoIV}; use nymsphinx::params::GatewayEncryptionAlgorithm; use rand::{CryptoRng, RngCore}; @@ -10,7 +10,7 @@ type NonceSize = ::NonceSize; // I think 'IV' looks better than 'Iv', feel free to change that. #[allow(clippy::upper_case_acronyms)] -pub struct AuthenticationIV(IV); +pub struct IV(CryptoIV); #[derive(Debug)] // I think 'IV' looks better than 'Iv', feel free to change that. @@ -21,9 +21,9 @@ pub enum IVConversionError { StringOfInvalidLengthError, } -impl AuthenticationIV { +impl IV { pub fn new_random(rng: &mut R) -> Self { - AuthenticationIV(random_iv::(rng)) + IV(random_iv::(rng)) } pub fn try_from_bytes(bytes: &[u8]) -> Result { @@ -31,7 +31,7 @@ impl AuthenticationIV { return Err(IVConversionError::BytesOfInvalidLengthError); } - Ok(AuthenticationIV(GenericArray::clone_from_slice(bytes))) + Ok(IV(GenericArray::clone_from_slice(bytes))) } pub fn to_bytes(&self) -> Vec { @@ -42,7 +42,7 @@ impl AuthenticationIV { self.0.as_ref() } - pub fn inner(&self) -> &IV { + pub fn inner(&self) -> &CryptoIV { &self.0 } @@ -56,8 +56,8 @@ impl AuthenticationIV { return Err(IVConversionError::StringOfInvalidLengthError); } - Ok(AuthenticationIV( - GenericArray::from_exact_iter(decoded).expect("Invalid vector length!"), + Ok(IV( + GenericArray::from_exact_iter(decoded).expect("Invalid vector length!") )) } @@ -66,8 +66,8 @@ impl AuthenticationIV { } } -impl From for String { - fn from(iv: AuthenticationIV) -> Self { +impl From for String { + fn from(iv: IV) -> Self { iv.to_base58_string() } } diff --git a/gateway/gateway-requests/src/lib.rs b/gateway/gateway-requests/src/lib.rs index bbfd8abd5fe..24018ba2196 100644 --- a/gateway/gateway-requests/src/lib.rs +++ b/gateway/gateway-requests/src/lib.rs @@ -1,19 +1,19 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +pub use crypto::generic_array; use crypto::hmac::{hmac::Mac, HmacOutput}; use nymsphinx::params::GatewayIntegrityHmacAlgorithm; +pub use types::*; pub mod authentication; +pub mod iv; pub mod registration; pub mod types; pub const DUMMY_MESSAGE_CONTENT: &[u8] = b"[DUMMY MESSAGE] Wanting something does not give you the right to have it."; -pub use crypto::generic_array; -pub use types::*; - pub type GatewayMac = HmacOutput; // TODO: could using `Mac` trait here for OutputSize backfire? diff --git a/gateway/gateway-requests/src/registration/handshake/client.rs b/gateway/gateway-requests/src/registration/handshake/client.rs index 9848c621c34..784364ebe20 100644 --- a/gateway/gateway-requests/src/registration/handshake/client.rs +++ b/gateway/gateway-requests/src/registration/handshake/client.rs @@ -24,18 +24,11 @@ impl<'a> ClientHandshake<'a> { ws_stream: &'a mut S, identity: &'a crypto::asymmetric::identity::KeyPair, gateway_pubkey: identity::PublicKey, - coconut_credential: coconut_interface::Credential, ) -> Self where S: Stream + Sink + Unpin + Send + 'a, { - let mut state = State::new( - rng, - ws_stream, - identity, - Some(gateway_pubkey), - Some(coconut_credential), - ); + let mut state = State::new(rng, ws_stream, identity, Some(gateway_pubkey)); ClientHandshake { handshake_future: Box::pin(async move { diff --git a/gateway/gateway-requests/src/registration/handshake/error.rs b/gateway/gateway-requests/src/registration/handshake/error.rs index dbb083d0b92..0bf672d2b5b 100644 --- a/gateway/gateway-requests/src/registration/handshake/error.rs +++ b/gateway/gateway-requests/src/registration/handshake/error.rs @@ -25,8 +25,6 @@ pub enum HandshakeError { MalformedRequest, #[error("sent request was malformed")] HandshakeFailure, - #[error("could not verify Coconut Credential")] - InvalidCoconutCredential, #[error("could not deserialize from slice: {source}")] DeserializationError { #[from] diff --git a/gateway/gateway-requests/src/registration/handshake/gateway.rs b/gateway/gateway-requests/src/registration/handshake/gateway.rs index bf786f687e8..8663f9af5fd 100644 --- a/gateway/gateway-requests/src/registration/handshake/gateway.rs +++ b/gateway/gateway-requests/src/registration/handshake/gateway.rs @@ -4,7 +4,6 @@ use crate::registration::handshake::shared_key::SharedKeys; use crate::registration::handshake::state::State; use crate::registration::handshake::{error::HandshakeError, WsItem}; -use coconut_interface::VerificationKey; use crypto::asymmetric::encryption; use futures::future::BoxFuture; use futures::task::{Context, Poll}; @@ -23,12 +22,11 @@ impl<'a> GatewayHandshake<'a> { ws_stream: &'a mut S, identity: &'a crypto::asymmetric::identity::KeyPair, received_init_payload: Vec, - verification_key: &'a VerificationKey, ) -> Self where S: Stream + Sink + Unpin + Send + 'a, { - let mut state = State::new(rng, ws_stream, identity, None, None); + let mut state = State::new(rng, ws_stream, identity, None); GatewayHandshake { handshake_future: Box::pin(async move { // If any step along the way failed (that are non-network related), @@ -50,27 +48,13 @@ impl<'a> GatewayHandshake<'a> { } } - // init: <- pub_key || g^x || credential + // init: <- pub_key || g^x let init_message = check_processing_error( State::::parse_init_message(received_init_payload), &mut state, ) .await?; - let credential = init_message.credential(); - - check_processing_error( - { - if !credential.verify(verification_key).await { - Err(HandshakeError::InvalidCoconutCredential) - } else { - Ok(()) - } - }, - &mut state, - ) - .await?; - let remote_identity = init_message.local_id_pubkey(); let remote_ephemeral_key = init_message.ephemeral_key(); state.update_remote_identity(remote_identity); diff --git a/gateway/gateway-requests/src/registration/handshake/mod.rs b/gateway/gateway-requests/src/registration/handshake/mod.rs index 715b0dedba3..0b3b95bf7f2 100644 --- a/gateway/gateway-requests/src/registration/handshake/mod.rs +++ b/gateway/gateway-requests/src/registration/handshake/mod.rs @@ -6,8 +6,6 @@ use self::error::HandshakeError; #[cfg(not(target_arch = "wasm32"))] use self::gateway::GatewayHandshake; pub use self::shared_key::{SharedKeySize, SharedKeys}; -#[cfg(not(target_arch = "wasm32"))] -use coconut_interface::VerificationKey; use crypto::asymmetric::identity; use futures::{Sink, Stream}; use rand::{CryptoRng, RngCore}; @@ -32,12 +30,11 @@ pub async fn client_handshake<'a, S>( ws_stream: &'a mut S, identity: &'a identity::KeyPair, gateway_pubkey: identity::PublicKey, - coconut_credential: coconut_interface::Credential, ) -> Result where S: Stream + Sink + Unpin + Send + 'a, { - ClientHandshake::new(rng, ws_stream, identity, gateway_pubkey, coconut_credential).await + ClientHandshake::new(rng, ws_stream, identity, gateway_pubkey).await } #[cfg(not(target_arch = "wasm32"))] @@ -46,19 +43,11 @@ pub async fn gateway_handshake<'a, S>( ws_stream: &'a mut S, identity: &'a identity::KeyPair, received_init_payload: Vec, - verification_key: &VerificationKey, ) -> Result where S: Stream + Sink + Unpin + Send + 'a, { - GatewayHandshake::new( - rng, - ws_stream, - identity, - received_init_payload, - verification_key, - ) - .await + GatewayHandshake::new(rng, ws_stream, identity, received_init_payload).await } /* diff --git a/gateway/gateway-requests/src/registration/handshake/shared_key.rs b/gateway/gateway-requests/src/registration/handshake/shared_key.rs index 7fc655ee19f..b07c6827d71 100644 --- a/gateway/gateway-requests/src/registration/handshake/shared_key.rs +++ b/gateway/gateway-requests/src/registration/handshake/shared_key.rs @@ -1,11 +1,12 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::{GatewayMacSize, GatewayRequestsError}; use crypto::generic_array::{ typenum::{Sum, Unsigned, U16}, GenericArray, }; -use crypto::hmac::compute_keyed_hmac; +use crypto::hmac::{compute_keyed_hmac, recompute_keyed_hmac_and_verify_tag}; use crypto::symmetric::stream_cipher::{self, Key, NewStreamCipher, IV}; use nymsphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorithm}; use pemstore::traits::PemStorableKey; @@ -102,6 +103,41 @@ impl SharedKeys { .collect() } + pub fn decrypt_tagged( + &self, + enc_data: &[u8], + iv: Option<&IV>, + ) -> Result, GatewayRequestsError> { + let mac_size = GatewayMacSize::to_usize(); + if enc_data.len() < mac_size { + return Err(GatewayRequestsError::TooShortRequest); + } + + let mac_tag = &enc_data[..mac_size]; + let message_bytes = &enc_data[mac_size..]; + + if !recompute_keyed_hmac_and_verify_tag::( + self.mac_key(), + message_bytes, + mac_tag, + ) { + return Err(GatewayRequestsError::InvalidMac); + } + + // couldn't have made the first borrow mutable as you can't have an immutable borrow + // together with a mutable one + let mut message_bytes_mut = &mut enc_data.to_vec()[mac_size..]; + + let zero_iv = stream_cipher::zero_iv::(); + let iv = iv.unwrap_or(&zero_iv); + stream_cipher::decrypt_in_place::( + self.encryption_key(), + iv, + &mut message_bytes_mut, + ); + Ok(message_bytes_mut.to_vec()) + } + pub fn encryption_key(&self) -> &Key { &self.encryption_key } diff --git a/gateway/gateway-requests/src/registration/handshake/state.rs b/gateway/gateway-requests/src/registration/handshake/state.rs index 97e2ae56e6a..9b45b89292d 100644 --- a/gateway/gateway-requests/src/registration/handshake/state.rs +++ b/gateway/gateway-requests/src/registration/handshake/state.rs @@ -5,7 +5,6 @@ use crate::registration::handshake::error::HandshakeError; use crate::registration::handshake::shared_key::{SharedKeySize, SharedKeys}; use crate::registration::handshake::WsItem; use crate::types; -use coconut_interface::Credential; use crypto::{ asymmetric::{encryption, identity}, generic_array::typenum::Unsigned, @@ -24,19 +23,13 @@ use tungstenite::Message as WsMessage; pub struct InitMessage { local_id_pubkey: [u8; identity::PUBLIC_KEY_LENGTH], ephemeral_key: [u8; identity::PUBLIC_KEY_LENGTH], - credential: Option, } impl InitMessage { - fn new( - local_id_pubkey: &identity::PublicKey, - ephemeral_key: &encryption::PublicKey, - credential: Credential, - ) -> Self { + fn new(local_id_pubkey: &identity::PublicKey, ephemeral_key: &encryption::PublicKey) -> Self { InitMessage { local_id_pubkey: local_id_pubkey.to_bytes(), ephemeral_key: ephemeral_key.to_bytes(), - credential: Some(credential), } } @@ -50,11 +43,6 @@ impl InitMessage { encryption::PublicKey::from_bytes(&self.ephemeral_key).unwrap() } - #[cfg(not(target_arch = "wasm32"))] - pub fn credential(&self) -> Credential { - self.credential.clone().unwrap() - } - fn to_bytes(&self) -> Vec { bincode::serialize(self).unwrap() } @@ -85,7 +73,6 @@ pub(crate) struct State<'a, S> { /// The known or received public identity key of the remote. /// Ideally it would always be known before the handshake was initiated. remote_pubkey: Option, - coconut_credential: Option, } impl<'a, S> State<'a, S> { @@ -94,7 +81,6 @@ impl<'a, S> State<'a, S> { ws_stream: &'a mut S, identity: &'a identity::KeyPair, remote_pubkey: Option, - credential: Option, ) -> Self { let ephemeral_keypair = encryption::KeyPair::new(rng); State { @@ -103,7 +89,6 @@ impl<'a, S> State<'a, S> { identity, remote_pubkey, derived_shared_keys: None, - coconut_credential: credential, } } @@ -119,7 +104,6 @@ impl<'a, S> State<'a, S> { InitMessage::new( self.identity.public_key(), self.ephemeral_keypair.public_key(), - self.coconut_credential.clone().unwrap(), ) .to_bytes() } diff --git a/gateway/gateway-requests/src/types.rs b/gateway/gateway-requests/src/types.rs index 1c751783e96..3ada1325385 100644 --- a/gateway/gateway-requests/src/types.rs +++ b/gateway/gateway-requests/src/types.rs @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::authentication::encrypted_address::EncryptedAddressBytes; -use crate::authentication::iv::AuthenticationIV; +use crate::iv::IV; use crate::registration::handshake::SharedKeys; use crate::GatewayMacSize; +use coconut_interface::Credential; use crypto::generic_array::typenum::Unsigned; use crypto::hmac::recompute_keyed_hmac_and_verify_tag; use crypto::symmetric::stream_cipher; @@ -112,13 +113,17 @@ pub enum ClientControlRequest { }, #[serde(alias = "handshakePayload")] RegisterHandshakeInitRequest { data: Vec }, + BandwidthCredential { + enc_credential: Vec, + iv: Vec, + }, } impl ClientControlRequest { pub fn new_authenticate( address: DestinationAddressBytes, enc_address: EncryptedAddressBytes, - iv: AuthenticationIV, + iv: IV, ) -> Self { ClientControlRequest::Authenticate { address: address.as_base58_string(), @@ -126,6 +131,34 @@ impl ClientControlRequest { iv: iv.to_base58_string(), } } + + pub fn new_enc_bandwidth_credential( + credential: &Credential, + shared_key: &SharedKeys, + iv: IV, + ) -> Option { + match bincode::serialize(credential) { + Ok(serialized_credential) => { + let enc_credential = + shared_key.encrypt_and_tag(&serialized_credential, Some(iv.inner())); + + Some(ClientControlRequest::BandwidthCredential { + enc_credential, + iv: iv.to_bytes(), + }) + } + _ => None, + } + } + + pub fn try_from_enc_bandwidth_credential( + enc_credential: Vec, + shared_key: &SharedKeys, + iv: IV, + ) -> Result { + let credential = shared_key.decrypt_tagged(&enc_credential, Some(iv.inner()))?; + bincode::deserialize(&credential).map_err(|_| GatewayRequestsError::MalformedEncryption) + } } impl From for Message { @@ -158,6 +191,8 @@ impl TryInto for ClientControlRequest { pub enum ServerResponse { Authenticate { status: bool }, Register { status: bool }, + // Maybe we could return the remaining bandwidth? + Bandwidth { status: bool }, Send { status: bool }, Error { message: String }, } @@ -210,39 +245,14 @@ pub enum BinaryRequest { // would work there. impl BinaryRequest { pub fn try_from_encrypted_tagged_bytes( - mut raw_req: Vec, + raw_req: Vec, shared_keys: &SharedKeys, ) -> Result { - let mac_size = GatewayMacSize::to_usize(); - if raw_req.len() < mac_size { - return Err(GatewayRequestsError::TooShortRequest); - } - - let mac_tag = &raw_req[..mac_size]; - let message_bytes = &raw_req[mac_size..]; - - if !recompute_keyed_hmac_and_verify_tag::( - shared_keys.mac_key(), - message_bytes, - mac_tag, - ) { - return Err(GatewayRequestsError::InvalidMac); - } - - // couldn't have made the first borrow mutable as you can't have an immutable borrow - // together with a mutable one - let mut message_bytes_mut = &mut raw_req[mac_size..]; - - let zero_iv = stream_cipher::zero_iv::(); - stream_cipher::decrypt_in_place::( - shared_keys.encryption_key(), - &zero_iv, - &mut message_bytes_mut, - ); + let message_bytes = &shared_keys.decrypt_tagged(&raw_req, None)?; // right now there's only a single option possible which significantly simplifies the logic // if we decided to allow for more 'binary' messages, the API wouldn't need to change. - let mix_packet = MixPacket::try_from_bytes(message_bytes_mut)?; + let mix_packet = MixPacket::try_from_bytes(message_bytes)?; Ok(BinaryRequest::ForwardSphinx(mix_packet)) } diff --git a/gateway/src/node/client_handling/clients_handler.rs b/gateway/src/node/client_handling/clients_handler.rs index 110f056f308..2051d9b501f 100644 --- a/gateway/src/node/client_handling/clients_handler.rs +++ b/gateway/src/node/client_handling/clients_handler.rs @@ -10,7 +10,7 @@ use futures::{ StreamExt, }; use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; -use gateway_requests::authentication::iv::AuthenticationIV; +use gateway_requests::iv::IV; use gateway_requests::registration::handshake::SharedKeys; use log::*; use nymsphinx::DestinationAddressBytes; @@ -34,7 +34,7 @@ pub(crate) enum ClientsHandlerRequest { Authenticate( DestinationAddressBytes, EncryptedAddressBytes, - AuthenticationIV, + IV, MixMessageSender, ClientsHandlerResponseSender, ), @@ -193,7 +193,7 @@ impl ClientsHandler { &mut self, address: DestinationAddressBytes, encrypted_address: EncryptedAddressBytes, - iv: AuthenticationIV, + iv: IV, comm_channel: MixMessageSender, res_channel: ClientsHandlerResponseSender, ) { diff --git a/gateway/src/node/client_handling/websocket/connection_handler.rs b/gateway/src/node/client_handling/websocket/connection_handler.rs index 82c459fe4af..9cf55b0e313 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler.rs @@ -14,7 +14,7 @@ use futures::{ SinkExt, StreamExt, }; use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; -use gateway_requests::authentication::iv::AuthenticationIV; +use gateway_requests::iv::IV; use gateway_requests::registration::handshake::error::HandshakeError; use gateway_requests::registration::handshake::{gateway_handshake, SharedKeys}; use gateway_requests::types::{BinaryRequest, ClientControlRequest, ServerResponse}; @@ -53,6 +53,8 @@ pub(crate) struct Handle { rng: R, remote_address: Option, shared_key: Option, + // TODO: This should be replaced by an actual bandwidth value, with 0 meaning no bandwidth + has_bandwidth: bool, clients_handler_sender: ClientsHandlerRequestSender, outbound_mix_sender: MixForwardingSender, socket_connection: SocketStream, @@ -79,6 +81,7 @@ where rng, remote_address: None, shared_key: None, + has_bandwidth: false, clients_handler_sender, outbound_mix_sender, socket_connection: SocketStream::RawTcp(conn), @@ -119,7 +122,6 @@ where ws_stream, self.local_identity.as_ref(), init_msg, - &self.aggregated_verification_key, ) .await } @@ -192,6 +194,9 @@ where } async fn handle_binary(&self, bin_msg: Vec) -> Message { + if !self.has_bandwidth { + return ServerResponse::new_error("Not enough bandwidth").into(); + } trace!("Handling binary message (presumably sphinx packet)"); // this function decrypts the request and checks the MAC @@ -236,7 +241,7 @@ where } }; - let iv = match AuthenticationIV::try_from_base58_string(iv) { + let iv = match IV::try_from_base58_string(iv) { Ok(iv) => iv, Err(e) => { trace!("failed to parse received IV {:?}", e); @@ -342,12 +347,45 @@ where } } - // currently there are no valid control messages you can send after authentication - async fn handle_text(&mut self, _: String) -> Message { - trace!("Handling text message (presumably control message)"); + async fn handle_bandwidth(&mut self, enc_credential: Vec, iv: Vec) -> ServerResponse { + if self.shared_key.is_none() { + return ServerResponse::new_error("No shared key has been exchanged with the gateway"); + } + let iv = match IV::try_from_bytes(&iv) { + Ok(iv) => iv, + Err(e) => { + trace!("failed to parse received IV {:?}", e); + return ServerResponse::new_error("malformed iv"); + } + }; + let credential = match ClientControlRequest::try_from_enc_bandwidth_credential( + enc_credential, + self.shared_key.as_ref().unwrap(), + iv, + ) { + Ok(c) => c, + Err(e) => { + return ServerResponse::new_error(e.to_string()); + } + }; + let status = credential.verify(&self.aggregated_verification_key); + self.has_bandwidth = status; + ServerResponse::Bandwidth { status } + } - error!("Currently there are no text messages besides 'Authenticate' and 'Register' and they were already dealt with!"); - ServerResponse::new_error("invalid request").into() + // currently the bandwidth credential request is the only one we can receive after + // authentication + async fn handle_text(&mut self, raw_request: String) -> Message { + if let Ok(request) = ClientControlRequest::try_from(raw_request) { + match request { + ClientControlRequest::BandwidthCredential { enc_credential, iv } => { + self.handle_bandwidth(enc_credential, iv).await.into() + } + _ => ServerResponse::new_error("invalid request").into(), + } + } else { + ServerResponse::new_error("malformed request").into() + } } async fn handle_request(&mut self, raw_request: Message) -> Option { @@ -384,6 +422,7 @@ where ClientControlRequest::RegisterHandshakeInitRequest { data } => { self.handle_register(data, mix_sender).await } + _ => ServerResponse::new_error("invalid request"), } } else { // TODO: is this a malformed request or rather a network error and diff --git a/gateway/src/node/storage/ledger.rs b/gateway/src/node/storage/ledger.rs index e5d6c5aec24..ba698358867 100644 --- a/gateway/src/node/storage/ledger.rs +++ b/gateway/src/node/storage/ledger.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; -use gateway_requests::authentication::iv::AuthenticationIV; use gateway_requests::generic_array::typenum::Unsigned; +use gateway_requests::iv::IV; use gateway_requests::registration::handshake::{SharedKeySize, SharedKeys}; use log::*; use nymsphinx::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH}; @@ -79,7 +79,7 @@ impl ClientLedger { &self, client_address: &DestinationAddressBytes, encrypted_address: &EncryptedAddressBytes, - iv: &AuthenticationIV, + iv: &IV, ) -> Result { match self.db.get(client_address.as_bytes_ref()) { Err(e) => Err(ClientLedgerError::Read(e)),