Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/cred after handshake #745

Merged
merged 20 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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? {
neacsu marked this conversation as resolved.
Show resolved Hide resolved
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));
neacsu marked this conversation as resolved.
Show resolved Hide resolved
}
Ok(())
}

pub async fn authenticate(
async fn authenticate(
neacsu marked this conversation as resolved.
Show resolved Hide resolved
&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() {
neacsu marked this conversation as resolved.
Show resolved Hide resolved
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> {
neacsu marked this conversation as resolved.
Show resolved Hide resolved
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