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:
committed by
GitHub
parent
982ee0266c
commit
122f5d9f2e
@@ -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.
|
||||
|
||||
@@ -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!")
|
||||
}
|
||||
|
||||
@@ -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!")
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
+10
-10
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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,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)),
|
||||
|
||||
Reference in New Issue
Block a user