Skip to content

Commit

Permalink
Feature/cred after handshake (#745)
Browse files Browse the repository at this point in the history
* Call perform_initial_authentication instead of register in clients

* Refactor the register/authenticate functions a bit

* Introduce Bandwidth request type

* Add encryption layer to cred

* Remove cred pass and check from handshake

* Replaced unreachable!  with error

* Changed decrypt_tagged signature to not take mutable ownership of data

* Put handle_bandwidth work inside a function

* Add check before unwrap

* Remove unnecessary async

* Decouple bandwidth credential from authentication

* Use new_error for ServerResponse:Error

* Send a fresh IV each time the BandwidthCredential request is sent

* Remove unwrap of bincode::serialize

* Add comment regarding Bandwidth response

* Remove _mut from naming

* Leave Debug trait alone, as the initial error doesn't reproduce anymore

* Pass iv as Vec<u8> instead of base58 string

* Renamed AuthenticationIV to IV, as it is now used for more the just authentication

* Did some IV refactorization
  • Loading branch information
neacsu authored Aug 30, 2021
1 parent 982ee02 commit 122f5d9
Show file tree
Hide file tree
Showing 21 changed files with 228 additions and 148 deletions.
4 changes: 2 additions & 2 deletions clients/client-core/src/client/key_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<SharedKeys>) {
self.gateway_shared_key = Some(gateway_shared_key)
}

/// Loads previously stored keys from the disk.
Expand Down
4 changes: 2 additions & 2 deletions clients/native/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
validator_urls: Vec<Url>,
) -> SharedKeys {
) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500);
let coconut_credential =
prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await;
Expand All @@ -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!")
}
Expand Down
4 changes: 2 additions & 2 deletions clients/socks5/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
validator_urls: Vec<Url>,
) -> SharedKeys {
) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500);
let coconut_credential =
prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await;
Expand All @@ -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!")
}
Expand Down
2 changes: 1 addition & 1 deletion clients/tauri-client/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
75 changes: 59 additions & 16 deletions common/client-libs/gateway-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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<identity::KeyPair>,
Expand Down Expand Up @@ -70,6 +72,7 @@ impl GatewayClient {
) -> Self {
GatewayClient {
authenticated: false,
has_bandwidth: false,
gateway_address,
gateway_identity,
local_identity,
Expand Down Expand Up @@ -114,6 +117,7 @@ impl GatewayClient {

GatewayClient {
authenticated: false,
has_bandwidth: false,
gateway_address,
gateway_identity,
local_identity,
Expand Down Expand Up @@ -362,7 +366,7 @@ impl GatewayClient {
}
}

pub async fn register(&mut self) -> Result<SharedKeys, GatewayClientError> {
async fn register(&mut self) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
Expand All @@ -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<SharedKeys>,
) -> Result<bool, GatewayClientError> {
) -> Result<(), GatewayClientError> {
if shared_key.is_none() && self.shared_key.is_none() {
return Err(GatewayClientError::NoSharedKeyAvailable);
}
Expand All @@ -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()
Expand All @@ -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
Expand All @@ -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
Expand All @@ -449,13 +456,42 @@ 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<MixPacket>,
) -> Result<(), GatewayClientError> {
if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated);
}
if !self.has_bandwidth {
return Err(GatewayClientError::NotEnoughBandwidth);
}
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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(());
}
Expand Down Expand Up @@ -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()?;
Expand Down
10 changes: 10 additions & 0 deletions common/client-libs/gateway-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ pub enum GatewayClientError {
NoSharedKeyAvailable,
ConnectionAbruptlyClosed,
MalformedResponse,
SerializeCredential,
NotAuthenticated,
NotEnoughBandwidth,
UnexpectedResponse,
ConnectionInInvalidState,
RegistrationFailure(HandshakeError),
AuthenticationFailure,
Expand Down Expand Up @@ -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")
}
}
}
}
2 changes: 1 addition & 1 deletion common/coconut-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
&params,
Expand Down
11 changes: 3 additions & 8 deletions gateway/gateway-requests/src/authentication/encrypted_address.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// 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;
Expand All @@ -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::<GatewayEncryptionAlgorithm>(
key.encryption_key(),
iv.inner(),
Expand All @@ -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)
}

Expand Down
1 change: 0 additions & 1 deletion gateway/gateway-requests/src/authentication/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@
// SPDX-License-Identifier: Apache-2.0

pub mod encrypted_address;
pub mod iv;
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
// 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};

type NonceSize = <GatewayEncryptionAlgorithm as NewStreamCipher>::NonceSize;

// I think 'IV' looks better than 'Iv', feel free to change that.
#[allow(clippy::upper_case_acronyms)]
pub struct AuthenticationIV(IV<GatewayEncryptionAlgorithm>);
pub struct IV(CryptoIV<GatewayEncryptionAlgorithm>);

#[derive(Debug)]
// I think 'IV' looks better than 'Iv', feel free to change that.
Expand All @@ -21,17 +21,17 @@ pub enum IVConversionError {
StringOfInvalidLengthError,
}

impl AuthenticationIV {
impl IV {
pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
AuthenticationIV(random_iv::<GatewayEncryptionAlgorithm, _>(rng))
IV(random_iv::<GatewayEncryptionAlgorithm, _>(rng))
}

pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, IVConversionError> {
if bytes.len() != NonceSize::to_usize() {
return Err(IVConversionError::BytesOfInvalidLengthError);
}

Ok(AuthenticationIV(GenericArray::clone_from_slice(bytes)))
Ok(IV(GenericArray::clone_from_slice(bytes)))
}

pub fn to_bytes(&self) -> Vec<u8> {
Expand All @@ -42,7 +42,7 @@ impl AuthenticationIV {
self.0.as_ref()
}

pub fn inner(&self) -> &IV<GatewayEncryptionAlgorithm> {
pub fn inner(&self) -> &CryptoIV<GatewayEncryptionAlgorithm> {
&self.0
}

Expand All @@ -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!")
))
}

Expand All @@ -66,8 +66,8 @@ impl AuthenticationIV {
}
}

impl From<AuthenticationIV> for String {
fn from(iv: AuthenticationIV) -> Self {
impl From<IV> for String {
fn from(iv: IV) -> Self {
iv.to_base58_string()
}
}
Loading

0 comments on commit 122f5d9

Please sign in to comment.