Feature/cred after handshake (#745)

* 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
This commit is contained in:
Bogdan-Ștefan Neacşu
2021-08-30 10:27:20 +03:00
committed by GitHub
parent 982ee0266c
commit 122f5d9f2e
21 changed files with 228 additions and 148 deletions
@@ -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.
+2 -2
View File
@@ -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;
@@ -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!")
}
+2 -2
View File
@@ -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;
@@ -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!")
}
+1 -1
View File
@@ -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]
+59 -16
View File
@@ -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<identity::KeyPair>,
@@ -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<SharedKeys, GatewayClientError> {
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<SharedKeys>,
) -> Result<bool, GatewayClientError> {
) -> 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<MixPacket>,
@@ -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()?;
@@ -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")
}
}
}
}
+1 -1
View File
@@ -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,
@@ -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;
@@ -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(),
@@ -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)
}
@@ -2,4 +2,3 @@
// SPDX-License-Identifier: Apache-2.0
pub mod encrypted_address;
pub mod iv;
@@ -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 = <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.
@@ -21,9 +21,9 @@ 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> {
@@ -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<u8> {
@@ -42,7 +42,7 @@ impl AuthenticationIV {
self.0.as_ref()
}
pub fn inner(&self) -> &IV<GatewayEncryptionAlgorithm> {
pub fn inner(&self) -> &CryptoIV<GatewayEncryptionAlgorithm> {
&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<AuthenticationIV> for String {
fn from(iv: AuthenticationIV) -> Self {
impl From<IV> for String {
fn from(iv: IV) -> Self {
iv.to_base58_string()
}
}
+3 -3
View File
@@ -1,19 +1,19 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// 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<GatewayIntegrityHmacAlgorithm>;
// TODO: could using `Mac` trait here for OutputSize backfire?
@@ -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 {
@@ -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]
@@ -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<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),
@@ -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);
@@ -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<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"))]
@@ -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
}
/*
@@ -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;
@@ -102,6 +103,41 @@ impl SharedKeys {
.collect()
}
pub fn decrypt_tagged(
&self,
enc_data: &[u8],
iv: Option<&IV<GatewayEncryptionAlgorithm>>,
) -> Result<Vec<u8>, 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::<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.to_vec()[mac_size..];
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
let iv = iv.unwrap_or(&zero_iv);
stream_cipher::decrypt_in_place::<GatewayEncryptionAlgorithm>(
self.encryption_key(),
iv,
&mut message_bytes_mut,
);
Ok(message_bytes_mut.to_vec())
}
pub fn encryption_key(&self) -> &Key<GatewayEncryptionAlgorithm> {
&self.encryption_key
}
@@ -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<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),
}
}
@@ -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()
}
@@ -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> {
@@ -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 {
@@ -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()
}
+40 -30
View File
@@ -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<u8> },
BandwidthCredential {
enc_credential: Vec<u8>,
iv: Vec<u8>,
},
}
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<Self> {
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<u8>,
shared_key: &SharedKeys,
iv: IV,
) -> Result<Credential, GatewayRequestsError> {
let credential = shared_key.decrypt_tagged(&enc_credential, Some(iv.inner()))?;
bincode::deserialize(&credential).map_err(|_| GatewayRequestsError::MalformedEncryption)
}
}
impl From<ClientControlRequest> for Message {
@@ -158,6 +191,8 @@ impl TryInto<String> 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<u8>,
raw_req: Vec<u8>,
shared_keys: &SharedKeys,
) -> Result<Self, GatewayRequestsError> {
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::<GatewayIntegrityHmacAlgorithm>(
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::<GatewayEncryptionAlgorithm>();
stream_cipher::decrypt_in_place::<GatewayEncryptionAlgorithm>(
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))
}
@@ -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,
) {
@@ -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<R, S> {
rng: R,
remote_address: Option<DestinationAddressBytes>,
shared_key: Option<SharedKeys>,
// 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<S>,
@@ -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<u8>) -> 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<u8>, iv: Vec<u8>) -> 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<Message> {
@@ -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
+2 -2
View File
@@ -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<bool, ClientLedgerError> {
match self.db.get(client_address.as_bytes_ref()) {
Err(e) => Err(ClientLedgerError::Read(e)),