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 5 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
48 changes: 35 additions & 13 deletions common/client-libs/gateway-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,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 +379,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)),
_ => unreachable!(),
neacsu marked this conversation as resolved.
Show resolved Hide resolved
}?;
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 @@ -420,15 +425,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)
}
}

/// Helper method to either call register or authenticate based on self.shared_key value
Expand All @@ -438,8 +442,26 @@ 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?;
}
// Authenticate with the bandwidth credential as well
if self.authenticated {
// Shared key should be set here, or else we wouldn't be authenticated
let msg = ClientControlRequest::new_enc_bandwidth_credential(
self.coconut_credential.clone(),
self.shared_key.as_ref().unwrap(),
)
.into();
self.authenticated = match self.send_websocket_message(msg).await? {
neacsu marked this conversation as resolved.
Show resolved Hide resolved
ServerResponse::Bandwidth { status } => Ok(status),
ServerResponse::Register { status: _ } => Err(GatewayClientError::NotAuthenticated),
ServerResponse::Error { message } => {
self.authenticated = false;
self.shared_key = None;
Err(GatewayClientError::GatewayError(message))
}
_ => unreachable!(),
neacsu marked this conversation as resolved.
Show resolved Hide resolved
}?;
}
if self.authenticated {
// if we are authenticated it means we MUST have an associated shared_key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = WsItem> + Sink<WsMessage> + 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 {
Expand Down
2 changes: 0 additions & 2 deletions gateway/gateway-requests/src/registration/handshake/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
20 changes: 2 additions & 18 deletions gateway/gateway-requests/src/registration/handshake/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -23,12 +22,11 @@ impl<'a> GatewayHandshake<'a> {
ws_stream: &'a mut S,
identity: &'a crypto::asymmetric::identity::KeyPair,
received_init_payload: Vec<u8>,
verification_key: &'a VerificationKey,
) -> Self
where
S: Stream<Item = WsItem> + Sink<WsMessage> + 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),
Expand All @@ -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::<S>::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);
Expand Down
15 changes: 2 additions & 13 deletions gateway/gateway-requests/src/registration/handshake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<SharedKeys, HandshakeError>
where
S: Stream<Item = WsItem> + Sink<WsMessage> + 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"))]
Expand All @@ -46,19 +43,11 @@ pub async fn gateway_handshake<'a, S>(
ws_stream: &'a mut S,
identity: &'a identity::KeyPair,
received_init_payload: Vec<u8>,
verification_key: &VerificationKey,
) -> Result<SharedKeys, HandshakeError>
where
S: Stream<Item = WsItem> + Sink<WsMessage> + 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
}

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// 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;
Expand Down Expand Up @@ -102,6 +103,36 @@ impl SharedKeys {
.collect()
}

pub fn decrypt_tagged(&self, mut enc_data: Vec<u8>) -> Result<Vec<u8>, GatewayRequestsError> {
neacsu marked this conversation as resolved.
Show resolved Hide resolved
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::<GatewayIntegrityHmacAlgorithm>(
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[mac_size..];

let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
stream_cipher::decrypt_in_place::<GatewayEncryptionAlgorithm>(
self.encryption_key(),
&zero_iv,
&mut message_bytes_mut,
);
Ok(message_bytes_mut.to_vec())
}

pub fn encryption_key(&self) -> &Key<GatewayEncryptionAlgorithm> {
&self.encryption_key
}
Expand Down
18 changes: 1 addition & 17 deletions gateway/gateway-requests/src/registration/handshake/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<Credential>,
}

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),
}
}

Expand All @@ -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<u8> {
bincode::serialize(self).unwrap()
neacsu marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down Expand Up @@ -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<identity::PublicKey>,
coconut_credential: Option<Credential>,
}

impl<'a, S> State<'a, S> {
Expand All @@ -94,7 +81,6 @@ impl<'a, S> State<'a, S> {
ws_stream: &'a mut S,
identity: &'a identity::KeyPair,
remote_pubkey: Option<identity::PublicKey>,
credential: Option<Credential>,
) -> Self {
let ephemeral_keypair = encryption::KeyPair::new(rng);
State {
Expand All @@ -103,7 +89,6 @@ impl<'a, S> State<'a, S> {
identity,
remote_pubkey,
derived_shared_keys: None,
coconut_credential: credential,
}
}

Expand All @@ -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()
}
Expand Down
Loading