Feature/client core (#303)

* Adding a client-core crate.

* Moved config into client_core

* Socks5 client now depends on client-core/config

* Native client now mostly using client-core internals

* Socks client now uses client-core internals.
This commit is contained in:
Dave Hrycyszyn
2020-08-13 11:21:59 +01:00
committed by GitHub
parent 1546088904
commit bdfe91676c
60 changed files with 213 additions and 3152 deletions
Generated
+27
View File
@@ -423,6 +423,31 @@ dependencies = [
"cc",
]
[[package]]
name = "client-core"
version = "0.8.0-dev"
dependencies = [
"built",
"config",
"crypto",
"directory-client",
"dirs",
"dotenv",
"futures 0.3.4",
"gateway-client",
"gateway-requests",
"log 0.4.8",
"nymsphinx",
"pemstore",
"rand 0.7.3",
"serde",
"sled 0.33.0",
"tempfile",
"tokio 0.2.16",
"topology",
"url 2.1.1",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
@@ -1838,6 +1863,7 @@ dependencies = [
"bs58",
"built",
"clap",
"client-core",
"config",
"crypto",
"directory-client",
@@ -3098,6 +3124,7 @@ dependencies = [
"bs58",
"built",
"clap",
"client-core",
"config",
"crypto",
"curve25519-dalek",
+1
View File
@@ -8,6 +8,7 @@ members = [
"clients/native",
"clients/socks5",
"clients/webassembly",
"clients/client-core",
"common/client-libs/directory-client",
"common/client-libs/directory-client/models",
"common/client-libs/gateway-client",
+35
View File
@@ -0,0 +1,35 @@
[package]
build = "build.rs"
name = "client-core"
version = "0.8.0-dev"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dirs = "2.0.2"
dotenv = "0.15.0"
futures = "0.3.1"
log = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0.104", features = ["derive"] }
sled = "0.33"
tokio = { version = "0.2", features = ["full"] }
url = "2.1"
# internal
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" }
directory-client = { path = "../../common/client-libs/directory-client" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
nymsphinx = { path = "../../common/nymsphinx" }
pemstore = { path = "../../common/pemstore" }
topology = { path = "../../common/topology" }
[build-dependencies]
built = "0.3.2"
[dev-dependencies]
tempfile = "3.1.0"
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use built;
fn main() {
built::write_built_file().expect("Failed to acquire build-time information");
}
@@ -12,4 +12,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod key_pathfinder;
// The file has been placed there by the build script.
include!(concat!(env!("OUT_DIR"), "/built.rs"));
@@ -28,7 +28,7 @@ use tokio::runtime::Handle;
use tokio::task::JoinHandle;
use tokio::time;
pub(crate) struct LoopCoverTrafficStream<R>
pub struct LoopCoverTrafficStream<R>
where
R: CryptoRng + Rng,
{
@@ -96,7 +96,7 @@ where
// obviously when we finally make shared rng that is on 'higher' level, this should become
// generic `R`
impl LoopCoverTrafficStream<OsRng> {
pub(crate) fn new(
pub fn new(
ack_key: Arc<AckKey>,
average_ack_delay: time::Duration,
average_packet_delay: time::Duration,
@@ -178,7 +178,7 @@ impl LoopCoverTrafficStream<OsRng> {
}
}
pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> {
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
self.run().await;
})
@@ -2,11 +2,11 @@ use futures::channel::mpsc;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::anonymous_replies::ReplySURB;
pub(crate) type InputMessageSender = mpsc::UnboundedSender<InputMessage>;
pub(crate) type InputMessageReceiver = mpsc::UnboundedReceiver<InputMessage>;
pub type InputMessageSender = mpsc::UnboundedSender<InputMessage>;
pub type InputMessageReceiver = mpsc::UnboundedReceiver<InputMessage>;
#[derive(Debug)]
pub(crate) enum InputMessage {
pub enum InputMessage {
Fresh {
recipient: Recipient,
data: Vec<u8>,
@@ -19,7 +19,7 @@ pub(crate) enum InputMessage {
}
impl InputMessage {
pub(crate) fn new_fresh(recipient: Recipient, data: Vec<u8>, with_reply_surb: bool) -> Self {
pub fn new_fresh(recipient: Recipient, data: Vec<u8>, with_reply_surb: bool) -> Self {
InputMessage::Fresh {
recipient,
data,
@@ -27,7 +27,7 @@ impl InputMessage {
}
}
pub(crate) fn new_reply(reply_surb: ReplySURB, data: Vec<u8>) -> Self {
pub fn new_reply(reply_surb: ReplySURB, data: Vec<u8>) -> Self {
InputMessage::Reply { reply_surb, data }
}
}
@@ -28,7 +28,7 @@ use std::sync::Arc;
// use the old key after new one was issued.
// Remember that Arc<T> has Deref implementation for T
pub(crate) struct KeyManager {
pub struct KeyManager {
/// identity key associated with the client instance.
identity_keypair: Arc<identity::KeyPair>,
@@ -56,7 +56,7 @@ impl KeyManager {
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
#[allow(dead_code)]
/// Creates new instance of a [`KeyManager`]
pub(crate) fn new<R>(rng: &mut R) -> Self
pub fn new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
@@ -72,12 +72,12 @@ 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(crate) fn insert_gateway_shared_key(&mut self, gateway_shared_key: SharedKeys) {
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: SharedKeys) {
self.gateway_shared_key = Some(Arc::new(gateway_shared_key))
}
/// Loads previously stored keys from the disk.
pub(crate) fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
let identity_keypair: identity::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
client_pathfinder.private_identity_key().to_owned(),
@@ -111,7 +111,7 @@ impl KeyManager {
// While perhaps there is no much point in storing the `AckKey` on the disk,
// it is done so for the consistency sake so that you wouldn't require an rng instance
// during `load_keys` to generate the said key.
pub(crate) fn store_keys(&self, client_pathfinder: &ClientKeyPathfinder) -> io::Result<()> {
pub fn store_keys(&self, client_pathfinder: &ClientKeyPathfinder) -> io::Result<()> {
pemstore::store_keypair(
self.identity_keypair.as_ref(),
&pemstore::KeyPairPath::new(
@@ -140,19 +140,19 @@ impl KeyManager {
}
/// Gets an atomically reference counted pointer to [`identity::KeyPair`].
pub(crate) fn identity_keypair(&self) -> Arc<identity::KeyPair> {
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
Arc::clone(&self.identity_keypair)
}
/// Gets an atomically reference counted pointer to [`encryption::KeyPair`].
pub(crate) fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
Arc::clone(&self.encryption_keypair)
}
/// Gets an atomically reference counted pointer to [`SharedKey`].
// since this function is not fully public, it is not expected to be used externally and
// hence it's up to us to ensure it's called in correct context
pub(crate) fn gateway_shared_key(&self) -> Arc<SharedKeys> {
pub fn gateway_shared_key(&self) -> Arc<SharedKeys> {
Arc::clone(
&self
.gateway_shared_key
@@ -162,7 +162,7 @@ impl KeyManager {
}
/// Gets an atomically reference counted pointer to [`AckKey`].
pub(crate) fn ack_key(&self) -> Arc<AckKey> {
pub fn ack_key(&self) -> Arc<AckKey> {
Arc::clone(&self.ack_key)
}
}
@@ -20,19 +20,19 @@ use nymsphinx::{addressing::nodes::NymNodeRoutingAddress, SphinxPacket};
use tokio::runtime::Handle;
use tokio::task::JoinHandle;
pub(crate) struct MixMessage(NymNodeRoutingAddress, SphinxPacket);
pub(crate) type MixMessageSender = mpsc::UnboundedSender<MixMessage>;
pub(crate) type MixMessageReceiver = mpsc::UnboundedReceiver<MixMessage>;
pub struct MixMessage(NymNodeRoutingAddress, SphinxPacket);
pub type MixMessageSender = mpsc::UnboundedSender<MixMessage>;
pub type MixMessageReceiver = mpsc::UnboundedReceiver<MixMessage>;
impl MixMessage {
pub(crate) fn new(address: NymNodeRoutingAddress, packet: SphinxPacket) -> Self {
pub fn new(address: NymNodeRoutingAddress, packet: SphinxPacket) -> Self {
MixMessage(address, packet)
}
}
const MAX_FAILURE_COUNT: usize = 100;
pub(crate) struct MixTrafficController<'a> {
pub struct MixTrafficController<'a> {
// TODO: most likely to be replaced by some higher level construct as
// later on gateway_client will need to be accessible by other entities
gateway_client: GatewayClient<'a, url::Url>,
@@ -44,7 +44,7 @@ pub(crate) struct MixTrafficController<'a> {
}
impl<'a> MixTrafficController<'static> {
pub(crate) fn new(
pub fn new(
mix_rx: MixMessageReceiver,
gateway_client: GatewayClient<'a, url::Url>,
) -> MixTrafficController<'a> {
@@ -77,13 +77,13 @@ impl<'a> MixTrafficController<'static> {
}
}
pub(crate) async fn run(&mut self) {
pub async fn run(&mut self) {
while let Some(mix_message) = self.mix_rx.next().await {
self.on_message(mix_message).await;
}
}
pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> {
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
self.run().await;
})
+8
View File
@@ -0,0 +1,8 @@
pub mod cover_traffic_stream;
pub mod inbound_messages;
pub mod key_manager;
pub mod mix_traffic;
pub mod real_messages_control;
pub mod received_buffer;
pub mod reply_key_storage;
pub mod topology_control;
@@ -39,7 +39,7 @@ use tokio::task::JoinHandle;
mod acknowlegement_control;
mod real_traffic_stream;
pub(crate) struct Config {
pub struct Config {
ack_key: Arc<AckKey>,
ack_wait_multiplier: f64,
ack_wait_addition: Duration,
@@ -50,7 +50,7 @@ pub(crate) struct Config {
}
impl Config {
pub(crate) fn new(
pub fn new(
ack_key: Arc<AckKey>,
ack_wait_multiplier: f64,
ack_wait_addition: Duration,
@@ -71,7 +71,7 @@ impl Config {
}
}
pub(crate) struct RealMessagesController<R>
pub struct RealMessagesController<R>
where
R: CryptoRng + Rng,
{
@@ -82,7 +82,7 @@ where
// obviously when we finally make shared rng that is on 'higher' level, this should become
// generic `R`
impl RealMessagesController<OsRng> {
pub(crate) fn new(
pub fn new(
config: Config,
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
@@ -161,7 +161,7 @@ impl RealMessagesController<OsRng> {
// &Handle is only passed for consistency sake with other client modules, but I think
// when we get to refactoring, we should apply gateway approach and make it implicit
pub(super) fn start(mut self, handle: &Handle) -> JoinHandle<Self> {
pub fn start(mut self, handle: &Handle) -> JoinHandle<Self> {
handle.spawn(async move {
self.run().await;
self
@@ -31,12 +31,12 @@ use tokio::task::JoinHandle;
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
// or to say "hey, I'm going offline, don't send anything more to me. Just buffer them instead"
pub(crate) type ReceivedBufferRequestSender = mpsc::UnboundedSender<ReceivedBufferMessage>;
pub(crate) type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver<ReceivedBufferMessage>;
pub type ReceivedBufferRequestSender = mpsc::UnboundedSender<ReceivedBufferMessage>;
pub type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver<ReceivedBufferMessage>;
// The channel set for the above
pub(crate) type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
pub(crate) type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
pub type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
pub type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
struct ReceivedMessagesBufferInner {
messages: Vec<ReconstructedMessage>,
@@ -277,7 +277,7 @@ impl ReceivedMessagesBuffer {
}
}
pub(crate) enum ReceivedBufferMessage {
pub enum ReceivedBufferMessage {
// Signals a websocket connection (or a native implementation) was established and we should stop buffering messages,
// and instead send them directly to the received channel
ReceiverAnnounce(ReconstructedMessagesSender),
@@ -342,13 +342,13 @@ impl FragmentedMessageReceiver {
}
}
pub(crate) struct ReceivedMessagesBufferController {
pub struct ReceivedMessagesBufferController {
fragmented_message_receiver: FragmentedMessageReceiver,
request_receiver: RequestReceiver,
}
impl ReceivedMessagesBufferController {
pub(crate) fn new(
pub fn new(
local_encryption_keypair: Arc<encryption::KeyPair>,
query_receiver: ReceivedBufferRequestReceiver,
mixnet_packet_receiver: MixnetMessageReceiver,
@@ -366,7 +366,7 @@ impl ReceivedMessagesBufferController {
}
}
pub(crate) fn start(self, handle: &Handle) {
pub fn start(self, handle: &Handle) {
// TODO: should we do anything with JoinHandle(s) returned by start methods?
self.fragmented_message_receiver.start(handle);
self.request_receiver.start(handle);
@@ -20,7 +20,7 @@ use nymsphinx::anonymous_replies::{
use std::path::Path;
#[derive(Debug)]
pub(crate) enum ReplyKeyStorageError {
pub enum ReplyKeyStorageError {
DbReadError(sled::Error),
DbWriteError(sled::Error),
DbOpenError(sled::Error),
@@ -40,7 +40,7 @@ pub struct ReplyKeyStorage {
}
impl ReplyKeyStorage {
pub(crate) fn load<P: AsRef<Path>>(path: P) -> Result<Self, ReplyKeyStorageError> {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, ReplyKeyStorageError> {
let db = match sled::open(path) {
Err(e) => return Err(ReplyKeyStorageError::DbOpenError(e)),
Ok(db) => db,
@@ -64,7 +64,7 @@ impl ReplyKeyStorage {
}
// TOOD: perhaps we could also store some part of original message here too?
pub(crate) fn insert_encryption_key(
pub fn insert_encryption_key(
&mut self,
encryption_key: SURBEncryptionKey,
) -> Result<(), ReplyKeyStorageError> {
@@ -86,7 +86,7 @@ impl ReplyKeyStorage {
}
// Once we use key once, we do not expect to use it again
pub(crate) fn get_and_remove_encryption_key(
pub fn get_and_remove_encryption_key(
&self,
key_digest: EncryptionKeyDigest,
) -> Result<Option<SURBEncryptionKey>, ReplyKeyStorageError> {
@@ -29,7 +29,7 @@ use topology::NymTopology;
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
#[derive(Debug)]
pub(super) struct TopologyAccessorInner(Option<NymTopology>);
pub struct TopologyAccessorInner(Option<NymTopology>);
impl AsRef<Option<NymTopology>> for TopologyAccessorInner {
fn as_ref(&self) -> &Option<NymTopology> {
@@ -47,7 +47,7 @@ impl TopologyAccessorInner {
}
}
pub(super) struct TopologyReadPermit<'a> {
pub struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, TopologyAccessorInner>,
}
@@ -99,7 +99,7 @@ impl<'a> From<RwLockReadGuard<'a, TopologyAccessorInner>> for TopologyReadPermit
}
#[derive(Clone, Debug)]
pub(crate) struct TopologyAccessor {
pub struct TopologyAccessor {
// `RwLock` *seems to* be the better approach for this as write access is only requested every
// few seconds, while reads are needed every single packet generated.
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
@@ -108,13 +108,13 @@ pub(crate) struct TopologyAccessor {
}
impl TopologyAccessor {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
TopologyAccessor {
inner: Arc::new(RwLock::new(TopologyAccessorInner::new())),
}
}
pub(super) async fn get_read_permit(&self) -> TopologyReadPermit<'_> {
pub async fn get_read_permit(&self) -> TopologyReadPermit<'_> {
self.inner.read().await.into()
}
@@ -124,7 +124,7 @@ impl TopologyAccessor {
// only used by the client at startup to get a slightly more reasonable error message
// (currently displays as unused because health checker is disabled due to required changes)
pub(crate) async fn is_routable(&self) -> bool {
pub async fn is_routable(&self) -> bool {
match &self.inner.read().await.0 {
None => false,
Some(ref topology) => topology.can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
@@ -132,13 +132,13 @@ impl TopologyAccessor {
}
}
pub(crate) struct TopologyRefresherConfig {
pub struct TopologyRefresherConfig {
directory_server: String,
refresh_rate: time::Duration,
}
impl TopologyRefresherConfig {
pub(crate) fn new(directory_server: String, refresh_rate: time::Duration) -> Self {
pub fn new(directory_server: String, refresh_rate: time::Duration) -> Self {
TopologyRefresherConfig {
directory_server,
refresh_rate,
@@ -146,14 +146,14 @@ impl TopologyRefresherConfig {
}
}
pub(crate) struct TopologyRefresher {
pub struct TopologyRefresher {
directory_client: directory_client::Client,
topology_accessor: TopologyAccessor,
refresh_rate: Duration,
}
impl TopologyRefresher {
pub(crate) fn new_directory_client(
pub fn new_directory_client(
cfg: TopologyRefresherConfig,
topology_accessor: TopologyAccessor,
) -> Self {
@@ -180,7 +180,7 @@ impl TopologyRefresher {
}
}
pub(crate) async fn refresh(&mut self) {
pub async fn refresh(&mut self) {
trace!("Refreshing the topology");
let new_topology = self.get_current_compatible_topology().await;
@@ -189,11 +189,11 @@ impl TopologyRefresher {
.await;
}
pub(crate) async fn is_topology_routable(&self) -> bool {
pub async fn is_topology_routable(&self) -> bool {
self.topology_accessor.is_routable().await
}
pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> {
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
loop {
tokio::time::delay_for(self.refresh_rate).await;
+3
View File
@@ -0,0 +1,3 @@
pub mod built_info;
pub mod client;
pub mod config;
+6 -5
View File
@@ -20,7 +20,7 @@ futures = "0.3.1"
log = "0.4"
pem = "0.7.0"
pretty_env_logger = "0.3"
rand = {version = "0.7.3", features = ["wasm-bindgen"]}
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
reqwest = "0.9.22"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.44"
@@ -30,15 +30,16 @@ tokio-tungstenite = "0.10.1"
url = "2.1"
## internal
config = {path = "../../common/config"}
crypto = {path = "../../common/crypto"}
client-core = { path = "../client-core" }
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" }
directory-client = { path = "../../common/client-libs/directory-client" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
mixnet-client = { path = "../../common/client-libs/mixnet-client" }
nymsphinx = { path = "../../common/nymsphinx" }
pemstore = {path = "../../common/pemstore"}
topology = {path = "../../common/topology" }
pemstore = { path = "../../common/pemstore" }
topology = { path = "../../common/topology" }
[build-dependencies]
built = "0.3.2"
@@ -1,33 +0,0 @@
use futures::channel::mpsc;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::anonymous_replies::ReplySURB;
pub(crate) type InputMessageSender = mpsc::UnboundedSender<InputMessage>;
pub(crate) type InputMessageReceiver = mpsc::UnboundedReceiver<InputMessage>;
#[derive(Debug)]
pub(crate) enum InputMessage {
Fresh {
recipient: Recipient,
data: Vec<u8>,
with_reply_surb: bool,
},
Reply {
reply_surb: ReplySURB,
data: Vec<u8>,
},
}
impl InputMessage {
pub(crate) fn new_fresh(recipient: Recipient, data: Vec<u8>, with_reply_surb: bool) -> Self {
InputMessage::Fresh {
recipient,
data,
with_reply_surb,
}
}
pub(crate) fn new_reply(reply_surb: ReplySURB, data: Vec<u8>) -> Self {
InputMessage::Reply { reply_surb, data }
}
}
-168
View File
@@ -1,168 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
use crypto::asymmetric::{encryption, identity};
use gateway_requests::registration::handshake::SharedKeys;
use log::*;
use nymsphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore};
use std::io;
use std::sync::Arc;
// Note: to support key rotation in the future, all keys will require adding an extra smart pointer,
// most likely an AtomicCell, or if it doesn't work as I think it does, a Mutex. Although I think
// AtomicCell includes a Mutex implicitly if the underlying type does not work atomically.
// And I guess there will need to be some mechanism for a grace period when you can still
// use the old key after new one was issued.
// Remember that Arc<T> has Deref implementation for T
pub(crate) struct KeyManager {
/// identity key associated with the client instance.
identity_keypair: Arc<identity::KeyPair>,
/// encryption key associated with the client instance.
encryption_keypair: Arc<encryption::KeyPair>,
/// shared key derived with the gateway during "registration handshake"
gateway_shared_key: Option<Arc<SharedKeys>>,
/// key used for producing and processing acknowledgement packets.
ack_key: Arc<AckKey>,
}
// The expected flow of a KeyManager "lifetime" is as follows:
/*
1. ::new() is called during client-init
2. after gateway registration is completed [in init] ::insert_gateway_shared_key() is called
3. ::store_keys() is called before init finishes execution.
4. ::load_keys() is called at the beginning of each subsequent client-run
5. [not implemented] ::rotate_keys() is called periodically during client-run I presume?
*/
impl KeyManager {
// this is actually **NOT** dead code
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
#[allow(dead_code)]
/// Creates new instance of a [`KeyManager`]
pub(crate) fn new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
KeyManager {
identity_keypair: Arc::new(identity::KeyPair::new_with_rng(rng)),
encryption_keypair: Arc::new(encryption::KeyPair::new_with_rng(rng)),
gateway_shared_key: None,
ack_key: Arc::new(AckKey::new(rng)),
}
}
// this is actually **NOT** dead code
// 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(crate) fn insert_gateway_shared_key(&mut self, gateway_shared_key: SharedKeys) {
self.gateway_shared_key = Some(Arc::new(gateway_shared_key))
}
/// Loads previously stored keys from the disk.
pub(crate) fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
let identity_keypair: identity::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
client_pathfinder.private_identity_key().to_owned(),
client_pathfinder.public_identity_key().to_owned(),
))?;
let encryption_keypair: encryption::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
client_pathfinder.private_encryption_key().to_owned(),
client_pathfinder.public_encryption_key().to_owned(),
))?;
let gateway_shared_key: SharedKeys =
pemstore::load_key(&client_pathfinder.gateway_shared_key().to_owned())?;
let ack_key: AckKey = pemstore::load_key(&client_pathfinder.ack_key().to_owned())?;
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
// after all for consistency sake?
Ok(KeyManager {
identity_keypair: Arc::new(identity_keypair),
encryption_keypair: Arc::new(encryption_keypair),
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
ack_key: Arc::new(ack_key),
})
}
// this is actually **NOT** dead code
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
#[allow(dead_code)]
/// Stores all available keys on the disk.
// While perhaps there is no much point in storing the `AckKey` on the disk,
// it is done so for the consistency sake so that you wouldn't require an rng instance
// during `load_keys` to generate the said key.
pub(crate) fn store_keys(&self, client_pathfinder: &ClientKeyPathfinder) -> io::Result<()> {
pemstore::store_keypair(
self.identity_keypair.as_ref(),
&pemstore::KeyPairPath::new(
client_pathfinder.private_identity_key().to_owned(),
client_pathfinder.public_identity_key().to_owned(),
),
)?;
pemstore::store_keypair(
self.encryption_keypair.as_ref(),
&pemstore::KeyPairPath::new(
client_pathfinder.private_encryption_key().to_owned(),
client_pathfinder.public_encryption_key().to_owned(),
),
)?;
pemstore::store_key(self.ack_key.as_ref(), &client_pathfinder.ack_key())?;
match self.gateway_shared_key.as_ref() {
None => warn!("No gateway shared key available to store!"),
Some(gate_key) => {
pemstore::store_key(gate_key.as_ref(), &client_pathfinder.gateway_shared_key())?
}
}
Ok(())
}
/// Gets an atomically reference counted pointer to [`identity::KeyPair`].
pub(crate) fn identity_keypair(&self) -> Arc<identity::KeyPair> {
Arc::clone(&self.identity_keypair)
}
/// Gets an atomically reference counted pointer to [`encryption::KeyPair`].
pub(crate) fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
Arc::clone(&self.encryption_keypair)
}
/// Gets an atomically reference counted pointer to [`SharedKey`].
// since this function is not fully public, it is not expected to be used externally and
// hence it's up to us to ensure it's called in correct context
pub(crate) fn gateway_shared_key(&self) -> Arc<SharedKeys> {
Arc::clone(
&self
.gateway_shared_key
.as_ref()
.expect("tried to unwrap empty gateway key!"),
)
}
/// Gets an atomically reference counted pointer to [`AckKey`].
pub(crate) fn ack_key(&self) -> Arc<AckKey> {
Arc::clone(&self.ack_key)
}
}
-91
View File
@@ -1,91 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use futures::channel::mpsc;
use futures::StreamExt;
use gateway_client::GatewayClient;
use log::*;
use nymsphinx::{addressing::nodes::NymNodeRoutingAddress, SphinxPacket};
use tokio::runtime::Handle;
use tokio::task::JoinHandle;
pub(crate) struct MixMessage(NymNodeRoutingAddress, SphinxPacket);
pub(crate) type MixMessageSender = mpsc::UnboundedSender<MixMessage>;
pub(crate) type MixMessageReceiver = mpsc::UnboundedReceiver<MixMessage>;
impl MixMessage {
pub(crate) fn new(address: NymNodeRoutingAddress, packet: SphinxPacket) -> Self {
MixMessage(address, packet)
}
}
const MAX_FAILURE_COUNT: usize = 100;
pub(crate) struct MixTrafficController<'a> {
// TODO: most likely to be replaced by some higher level construct as
// later on gateway_client will need to be accessible by other entities
gateway_client: GatewayClient<'a, url::Url>,
mix_rx: MixMessageReceiver,
// TODO: this is temporary work-around.
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
consecutive_gateway_failure_count: usize,
}
impl<'a> MixTrafficController<'static> {
pub(crate) fn new(
mix_rx: MixMessageReceiver,
gateway_client: GatewayClient<'a, url::Url>,
) -> MixTrafficController<'a> {
MixTrafficController {
gateway_client,
mix_rx,
consecutive_gateway_failure_count: 0,
}
}
async fn on_message(&mut self, mix_message: MixMessage) {
debug!("Got a mix_message for {:?}", mix_message.0);
match self
.gateway_client
.send_sphinx_packet(mix_message.0, mix_message.1)
.await
{
Err(e) => {
error!("Failed to send sphinx packet to the gateway! - {:?}", e);
self.consecutive_gateway_failure_count += 1;
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// todo: in the future this should initiate a 'graceful' shutdown
panic!("failed to send sphinx packet to the gateway {} times in a row - assuming the gateway is dead. Can't do anything about it yet :(", MAX_FAILURE_COUNT)
}
}
Ok(_) => {
trace!("We *might* have managed to forward sphinx packet to the gateway!");
self.consecutive_gateway_failure_count = 0;
}
}
}
pub(crate) async fn run(&mut self) {
while let Some(mix_message) = self.mix_rx.next().await {
self.on_message(mix_message).await;
}
}
pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
self.run().await;
})
}
}
+18 -22
View File
@@ -12,21 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::KeyManager;
use crate::client::mix_traffic::{MixMessageReceiver, MixMessageSender, MixTrafficController};
use crate::client::real_messages_control::RealMessagesController;
use crate::client::received_buffer::{
ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController,
use crate::websocket;
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender,
};
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::topology_control::{
use client_core::client::key_manager::KeyManager;
use client_core::client::mix_traffic::{
MixMessageReceiver, MixMessageSender, MixTrafficController,
};
use client_core::client::real_messages_control;
use client_core::client::real_messages_control::RealMessagesController;
use client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestReceiver, ReceivedBufferRequestSender,
ReceivedMessagesBufferController, ReconstructedMessagesReceiver,
};
use client_core::client::reply_key_storage::ReplyKeyStorage;
use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
use crate::config::{Config, SocketType};
use crate::websocket;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::config::{Config, SocketType};
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::{
@@ -38,18 +44,8 @@ use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::anonymous_replies::ReplySURB;
use nymsphinx::receiver::ReconstructedMessage;
use received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
use tokio::runtime::Runtime;
mod cover_traffic_stream;
pub(crate) mod inbound_messages;
pub(crate) mod key_manager;
mod mix_traffic;
pub(crate) mod real_messages_control;
pub(crate) mod received_buffer;
mod reply_key_storage;
pub(crate) mod topology_control;
pub struct NymClient {
/// Client configuration options, including, among other things, packet sending rates,
/// key filepaths, etc.
@@ -1,170 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// INPUT: InputMessage from user
// INPUT2: Acks from mix
// OUTPUT: MixMessage to mix traffic
use self::{
acknowlegement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
};
use crate::client::real_messages_control::acknowlegement_control::AcknowledgementControllerConnectors;
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::{
inbound_messages::InputMessageReceiver, mix_traffic::MixMessageSender,
topology_control::TopologyAccessor,
};
use futures::channel::mpsc;
use gateway_client::AcknowledgementReceiver;
use log::*;
use nymsphinx::acknowledgements::AckKey;
use nymsphinx::addressing::clients::Recipient;
use rand::{rngs::OsRng, CryptoRng, Rng};
use std::sync::Arc;
use std::time::Duration;
use tokio::runtime::Handle;
use tokio::task::JoinHandle;
mod acknowlegement_control;
mod real_traffic_stream;
pub(crate) struct Config {
ack_key: Arc<AckKey>,
ack_wait_multiplier: f64,
ack_wait_addition: Duration,
self_recipient: Recipient,
average_packet_delay_duration: Duration,
average_ack_delay_duration: Duration,
average_message_sending_delay: Duration,
}
impl Config {
pub(crate) fn new(
ack_key: Arc<AckKey>,
ack_wait_multiplier: f64,
ack_wait_addition: Duration,
average_ack_delay_duration: Duration,
average_message_sending_delay: Duration,
average_packet_delay_duration: Duration,
self_recipient: Recipient,
) -> Self {
Config {
ack_key,
self_recipient,
average_packet_delay_duration,
average_ack_delay_duration,
average_message_sending_delay,
ack_wait_multiplier,
ack_wait_addition,
}
}
}
pub(crate) struct RealMessagesController<R>
where
R: CryptoRng + Rng,
{
out_queue_control: Option<OutQueueControl<R>>,
ack_control: Option<AcknowledgementController<R>>,
}
// obviously when we finally make shared rng that is on 'higher' level, this should become
// generic `R`
impl RealMessagesController<OsRng> {
pub(crate) fn new(
config: Config,
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
mix_sender: MixMessageSender,
topology_access: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
) -> Self {
let rng = OsRng;
let (real_message_sender, real_message_receiver) = mpsc::unbounded();
let (sent_notifier_tx, sent_notifier_rx) = mpsc::unbounded();
let ack_controller_connectors = AcknowledgementControllerConnectors::new(
real_message_sender,
input_receiver,
sent_notifier_rx,
ack_receiver,
);
let ack_control = AcknowledgementController::new(
rng,
topology_access.clone(),
Arc::clone(&config.ack_key),
config.self_recipient.clone(),
reply_key_storage,
config.average_packet_delay_duration,
config.average_ack_delay_duration,
config.ack_wait_multiplier,
config.ack_wait_addition,
ack_controller_connectors,
);
let out_queue_control = OutQueueControl::new(
Arc::clone(&config.ack_key),
config.average_ack_delay_duration,
config.average_packet_delay_duration,
config.average_message_sending_delay,
sent_notifier_tx,
mix_sender,
real_message_receiver,
rng,
config.self_recipient,
topology_access,
);
RealMessagesController {
out_queue_control: Some(out_queue_control),
ack_control: Some(ack_control),
}
}
pub(super) async fn run(&mut self) {
let mut out_queue_control = self.out_queue_control.take().unwrap();
let mut ack_control = self.ack_control.take().unwrap();
// the below are log messages are errors as at the current stage we do not expect any of
// the task to ever finish. This will of course change once we introduce
// graceful shutdowns.
let out_queue_control_fut = tokio::spawn(async move {
out_queue_control.run_out_queue_control().await;
error!("The out queue controller has finished execution!");
out_queue_control
});
let ack_control_fut = tokio::spawn(async move {
ack_control.run().await;
error!("The acknowledgement controller has finished execution!");
ack_control
});
// technically we don't have to bring `RealMessagesController` back to a valid state
// but we can do it, so why not? Perhaps it might be useful if we wanted to allow
// for restarts of certain modules without killing the entire process.
self.out_queue_control = Some(out_queue_control_fut.await.unwrap());
self.ack_control = Some(ack_control_fut.await.unwrap());
}
// &Handle is only passed for consistency sake with other client modules, but I think
// when we get to refactoring, we should apply gateway approach and make it implicit
pub(super) fn start(mut self, handle: &Handle) -> JoinHandle<Self> {
handle.spawn(async move {
self.run().await;
self
})
}
}
@@ -1,105 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use log::*;
use nymsphinx::anonymous_replies::{
encryption_key::EncryptionKeyDigest, encryption_key::Unsigned, SURBEncryptionKey,
SURBEncryptionKeySize,
};
use std::path::Path;
#[derive(Debug)]
pub(crate) enum ReplyKeyStorageError {
DbReadError(sled::Error),
DbWriteError(sled::Error),
DbOpenError(sled::Error),
}
/// Permanent storage for keys in all sent [`ReplySURB`]
///
/// Each sent out [`ReplySURB`] has a new key associated with it that is going to be used for
/// payload encryption. In order to decrypt whatever reply we receive, we need to know which
/// key to use for that purpose. We do it based on received `H(t)` which has to be included
/// with each reply.
/// Moreover, there is no restriction when the [`ReplySURB`] might get used so we need to
/// have a permanent storage for all the keys that we might ever see in the future.
#[derive(Debug, Clone)]
pub struct ReplyKeyStorage {
db: sled::Db,
}
impl ReplyKeyStorage {
pub(crate) fn load<P: AsRef<Path>>(path: P) -> Result<Self, ReplyKeyStorageError> {
let db = match sled::open(path) {
Err(e) => return Err(ReplyKeyStorageError::DbOpenError(e)),
Ok(db) => db,
};
Ok(ReplyKeyStorage { db })
}
fn read_encryption_key(&self, raw_key: sled::IVec) -> SURBEncryptionKey {
let key_bytes_ref = raw_key.as_ref();
// if this fails it means we have some database corruption and we
// absolutely can't continue
if key_bytes_ref.len() != SURBEncryptionKeySize::to_usize() {
error!("REPLY KEY STORAGE DATA CORRUPTION - ENCRYPTION KEY HAS INVALID LENGTH");
panic!("REPLY KEY STORAGE DATA CORRUPTION - ENCRYPTION KEY HAS INVALID LENGTH");
}
// this can only fail if the bytes have invalid length but we already asserted it
SURBEncryptionKey::try_from_bytes(key_bytes_ref).unwrap()
}
// TOOD: perhaps we could also store some part of original message here too?
pub(crate) fn insert_encryption_key(
&mut self,
encryption_key: SURBEncryptionKey,
) -> Result<(), ReplyKeyStorageError> {
let digest = encryption_key.compute_digest();
let insertion_result = match self.db.insert(digest.to_vec(), encryption_key.to_bytes()) {
Err(e) => Err(ReplyKeyStorageError::DbWriteError(e)),
Ok(existing_key) => {
if existing_key.is_some() {
panic!("HASH COLLISION DETECTED")
};
Ok(())
}
};
// TODO: perhaps we could implement some batching mechanism to avoid frequent flushes?
self.db.flush().unwrap();
insertion_result
}
// Once we use key once, we do not expect to use it again
pub(crate) fn get_and_remove_encryption_key(
&self,
key_digest: EncryptionKeyDigest,
) -> Result<Option<SURBEncryptionKey>, ReplyKeyStorageError> {
let removal_result = match self.db.remove(&key_digest.to_vec()) {
Err(e) => Err(ReplyKeyStorageError::DbReadError(e)),
Ok(existing_key) => {
Ok(existing_key.map(|existing_key| self.read_encryption_key(existing_key)))
}
};
// TODO: not sure how to feel about flushing it every single time here...
// same with insertion
self.db.flush().unwrap();
removal_result
}
}
+3 -3
View File
@@ -13,10 +13,10 @@
// limitations under the License.
use crate::built_info;
use crate::client::key_manager::KeyManager;
use crate::commands::override_config;
use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use config::NymConfig;
use crypto::asymmetric::identity;
use directory_client::DirectoryClient;
@@ -149,7 +149,7 @@ pub fn execute(matches: &ArgMatches) {
println!("Initialising client...");
let id = matches.value_of("id").unwrap(); // required for now
let mut config = crate::config::Config::new(id);
let mut config = client_core::config::Config::new(id);
let mut rng = OsRng;
config = override_config(config, matches);
+1 -1
View File
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::{Config, SocketType};
use clap::ArgMatches;
use client_core::config::{Config, SocketType};
pub mod init;
pub mod run;
+1 -1
View File
@@ -14,8 +14,8 @@
use crate::client::NymClient;
use crate::commands::override_config;
use crate::config::Config;
use clap::{App, Arg, ArgMatches};
use client_core::config::Config;
use config::NymConfig;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
-1
View File
@@ -14,5 +14,4 @@
pub mod built_info;
pub mod client;
pub mod config;
pub mod websocket;
+1 -2
View File
@@ -16,8 +16,7 @@ use clap::{App, ArgMatches};
pub mod built_info;
pub mod client;
mod commands;
pub mod config;
pub mod commands;
pub mod websocket;
fn main() {
+2 -2
View File
@@ -13,13 +13,13 @@
// limitations under the License.
use super::types::{BinaryClientRequest, ClientTextRequest, ServerTextResponse};
use crate::client::{
use crate::websocket::types::ReceivedTextMessage;
use client_core::client::{
inbound_messages::{InputMessage, InputMessageSender},
received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
},
};
use crate::websocket::types::ReceivedTextMessage;
use futures::channel::mpsc;
use futures::{SinkExt, StreamExt};
use log::*;
+1
View File
@@ -29,6 +29,7 @@ tokio-tungstenite = "0.10.1"
url = "2.1"
## internal
client-core = { path = "../client-core" }
config = {path = "../../common/config"}
crypto = {path = "../../common/crypto"}
directory-client = { path = "../../common/client-libs/directory-client" }
@@ -1,186 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::mix_traffic::{MixMessage, MixMessageSender};
use crate::client::topology_control::TopologyAccessor;
use futures::task::{Context, Poll};
use futures::{Future, Stream, StreamExt};
use log::*;
use nymsphinx::acknowledgements::AckKey;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::cover::generate_loop_cover_packet;
use nymsphinx::utils::sample_poisson_duration;
use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin;
use std::sync::Arc;
use tokio::runtime::Handle;
use tokio::task::JoinHandle;
use tokio::time;
pub(crate) struct LoopCoverTrafficStream<R>
where
R: CryptoRng + Rng,
{
/// Key used to encrypt and decrypt content of an ACK packet.
ack_key: Arc<AckKey>,
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
average_ack_delay: time::Duration,
/// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: time::Duration,
/// Average delay between sending subsequent cover packets.
average_cover_message_sending_delay: time::Duration,
/// Internal state, determined by `average_message_sending_delay`,
/// used to keep track of when a next packet should be sent out.
next_delay: time::Delay,
/// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them
/// out to the network without any further delays.
mix_tx: MixMessageSender,
/// Represents full address of this client.
our_full_destination: Recipient,
/// Instance of a cryptographically secure random number generator.
rng: R,
/// Accessor to the common instance of network topology.
topology_access: TopologyAccessor,
}
impl<R> Stream for LoopCoverTrafficStream<R>
where
R: CryptoRng + Rng + Unpin,
{
// Item is only used to indicate we should create a new message rather than actual cover message
// reason being to not introduce unnecessary complexity by having to keep state of topology
// mutex when trying to acquire it. So right now the Stream trait serves as a glorified timer.
// Perhaps this should be changed in the future.
type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// it is not yet time to return a message
if Pin::new(&mut self.next_delay).poll(cx).is_pending() {
return Poll::Pending;
};
// we know it's time to send a message, so let's prepare delay for the next one
// Get the `now` by looking at the current `delay` deadline
let avg_delay = self.average_cover_message_sending_delay;
let now = self.next_delay.deadline();
let next_poisson_delay = sample_poisson_duration(&mut self.rng, avg_delay);
// The next interval value is `next_poisson_delay` after the one that just
// yielded.
let next = now + next_poisson_delay;
self.next_delay.reset(next);
Poll::Ready(Some(()))
}
}
// obviously when we finally make shared rng that is on 'higher' level, this should become
// generic `R`
impl LoopCoverTrafficStream<OsRng> {
pub(crate) fn new(
ack_key: Arc<AckKey>,
average_ack_delay: time::Duration,
average_packet_delay: time::Duration,
average_cover_message_sending_delay: time::Duration,
mix_tx: MixMessageSender,
our_full_destination: Recipient,
topology_access: TopologyAccessor,
) -> Self {
let rng = OsRng;
LoopCoverTrafficStream {
ack_key,
average_ack_delay,
average_packet_delay,
average_cover_message_sending_delay,
next_delay: time::delay_for(Default::default()),
mix_tx,
our_full_destination,
rng,
topology_access,
}
}
async fn on_new_message(&mut self) {
trace!("next cover message!");
// TODO for way down the line: in very rare cases (during topology update) we might have
// to wait a really tiny bit before actually obtaining the permit hence messing with our
// poisson delay, but is it really a problem?
let topology_permit = self.topology_access.get_read_permit().await;
// the ack is sent back to ourselves (and then ignored)
let topology_ref_option = topology_permit.try_get_valid_topology_ref(
&self.our_full_destination,
Some(&self.our_full_destination),
);
if topology_ref_option.is_none() {
warn!("No valid topology detected - won't send any loop cover message this time");
return;
}
let topology_ref = topology_ref_option.unwrap();
let cover_message = generate_loop_cover_packet(
&mut self.rng,
topology_ref,
&*self.ack_key,
&self.our_full_destination,
self.average_ack_delay,
self.average_packet_delay,
)
.expect("Somehow failed to generate a loop cover message with a valid topology");
// if this one fails, there's no retrying because it means that either:
// - we run out of memory
// - the receiver channel is closed
// in either case there's no recovery and we can only panic
self.mix_tx
.unbounded_send(MixMessage::new(cover_message.0, cover_message.1))
.unwrap();
// TODO: I'm not entirely sure whether this is really required, because I'm not 100%
// sure how `yield_now()` works - whether it just notifies the scheduler or whether it
// properly blocks. So to play it on the safe side, just explicitly drop the read permit
drop(topology_permit);
// JS: due to identical logical structure to OutQueueControl::on_message(), this is also
// presumably required to prevent bugs in the future. Exact reason is still unknown to me.
tokio::task::yield_now().await;
}
async fn run(&mut self) {
// we should set initial delay only when we actually start the stream
self.next_delay = time::delay_for(sample_poisson_duration(
&mut self.rng,
self.average_cover_message_sending_delay,
));
while let Some(_) = self.next().await {
self.on_new_message().await;
}
}
pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
self.run().await;
})
}
}
+20 -25
View File
@@ -12,24 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::KeyManager;
use crate::client::mix_traffic::{MixMessageReceiver, MixMessageSender, MixTrafficController};
use crate::client::real_messages_control::RealMessagesController;
use crate::client::received_buffer::{
ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController,
};
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
use crate::config::{Config, SocketType};
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
server::SphinxSocksServer,
};
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender,
};
use client_core::client::key_manager::KeyManager;
use client_core::client::mix_traffic::{
MixMessageReceiver, MixMessageSender, MixTrafficController,
};
use client_core::client::real_messages_control::RealMessagesController;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
use client_core::client::received_buffer::{
ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController,
};
use client_core::client::reply_key_storage::ReplyKeyStorage;
use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::config::{Config, SocketType};
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::{
@@ -41,18 +46,8 @@ use nymsphinx::addressing::clients::{ClientEncryptionKey, ClientIdentity, Recipi
use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::anonymous_replies::ReplySURB;
use nymsphinx::receiver::ReconstructedMessage;
use received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
use tokio::runtime::Runtime;
mod cover_traffic_stream;
pub(crate) mod inbound_messages;
pub(crate) mod key_manager;
mod mix_traffic;
pub(crate) mod real_messages_control;
pub(crate) mod received_buffer;
mod reply_key_storage;
pub(crate) mod topology_control;
pub struct NymClient {
/// Client configuration options, including, among other things, packet sending rates,
/// key filepaths, etc.
@@ -133,7 +128,7 @@ impl NymClient {
input_receiver: InputMessageReceiver,
mix_sender: MixMessageSender,
) {
let controller_config = real_messages_control::Config::new(
let controller_config = client_core::client::real_messages_control::Config::new(
self.key_manager.ack_key(),
self.config.get_ack_wait_multiplier(),
self.config.get_ack_wait_addition(),
@@ -1,92 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::PendingAcksMap;
use futures::StreamExt;
use gateway_client::AcknowledgementReceiver;
use log::*;
use nymsphinx::{
acknowledgements::{identifier::recover_identifier, AckKey},
chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID},
};
use std::sync::Arc;
// responsible for cancelling retransmission timers and removed entries from the map
pub(super) struct AcknowledgementListener {
ack_key: Arc<AckKey>,
ack_receiver: AcknowledgementReceiver,
pending_acks: PendingAcksMap,
}
impl AcknowledgementListener {
pub(super) fn new(
ack_key: Arc<AckKey>,
ack_receiver: AcknowledgementReceiver,
pending_acks: PendingAcksMap,
) -> Self {
AcknowledgementListener {
ack_key,
ack_receiver,
pending_acks,
}
}
async fn on_ack(&mut self, ack_content: Vec<u8>) {
debug!("Received an ack");
let frag_id = match recover_identifier(&self.ack_key, &ack_content) {
None => {
warn!("Received invalid ACK!"); // should we do anything else about that?
return;
}
Some(frag_id_bytes) => match FragmentIdentifier::try_from_bytes(frag_id_bytes) {
Ok(frag_id) => frag_id,
Err(err) => {
warn!("Received invalid ACK! - {:?}", err); // should we do anything else about that?
return;
}
},
};
if frag_id == COVER_FRAG_ID {
trace!("Received an ack for a cover message - no need to do anything");
return;
} else if frag_id.is_reply() {
debug!("Received an ack for a reply message - no need to do anything!");
// TODO: probably there will need to be some extra procedure here, something to notify
// user that his reply reached the recipient (since we got an ack)
info!("We received an ack for one of the replies we sent!");
return;
}
if let Some(pending_ack) = self.pending_acks.write().await.remove(&frag_id) {
// cancel the retransmission future
pending_ack.retransmission_cancel.notify();
} else {
warn!("received ACK for packet we haven't stored! - {:?}", frag_id);
}
}
pub(super) async fn run(&mut self) {
debug!("Started AcknowledgementListener");
while let Some(acks) = self.ack_receiver.next().await {
// realistically we would only be getting one ack at the time, but if we managed to
// introduce batching in gateway client, this call should be improved to not re-acquire
// write permit on the map every loop iteration
for ack in acks {
self.on_ack(ack).await;
}
}
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -1,205 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{PendingAcknowledgement, PendingAcksMap};
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::{
inbound_messages::{InputMessage, InputMessageReceiver},
real_messages_control::real_traffic_stream::{RealMessage, RealMessageSender},
topology_control::TopologyAccessor,
};
use futures::StreamExt;
use log::*;
use nymsphinx::anonymous_replies::ReplySURB;
use nymsphinx::preparer::MessagePreparer;
use nymsphinx::{acknowledgements::AckKey, addressing::clients::Recipient};
use rand::{CryptoRng, Rng};
use std::sync::Arc;
// responsible for splitting received message and initial sending attempt
pub(super) struct InputMessageListener<R>
where
R: CryptoRng + Rng,
{
ack_key: Arc<AckKey>,
ack_recipient: Recipient,
input_receiver: InputMessageReceiver,
message_preparer: MessagePreparer<R>,
pending_acks: PendingAcksMap,
real_message_sender: RealMessageSender,
topology_access: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
}
impl<R> InputMessageListener<R>
where
R: CryptoRng + Rng,
{
pub(super) fn new(
ack_key: Arc<AckKey>,
ack_recipient: Recipient,
input_receiver: InputMessageReceiver,
message_preparer: MessagePreparer<R>,
pending_acks: PendingAcksMap,
real_message_sender: RealMessageSender,
topology_access: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
) -> Self {
InputMessageListener {
ack_key,
ack_recipient,
input_receiver,
message_preparer,
pending_acks,
real_message_sender,
topology_access,
reply_key_storage,
}
}
async fn handle_reply(&mut self, reply_surb: ReplySURB, data: Vec<u8>) -> Option<RealMessage> {
let topology_permit = self.topology_access.get_read_permit().await;
let topology_ref_option =
topology_permit.try_get_valid_topology_ref(&self.ack_recipient, None);
if topology_ref_option.is_none() {
warn!("Could not process the message - the network topology is invalid");
return None;
}
let topology = topology_ref_option.unwrap();
match self
.message_preparer
.prepare_reply_for_use(data, reply_surb, topology, &self.ack_key)
{
Ok((reply_id, sphinx_packet, first_hop)) => {
// TODO: later probably write pending ack here
// and deal with them....
// ... somehow
Some(RealMessage::new(first_hop, sphinx_packet, reply_id))
}
Err(err) => {
// TODO: should we have some mechanism to indicate to the user that the `reply_surb`
// could be reused since technically it wasn't used up here?
warn!("failed to deal with received reply surb - {:?}", err);
None
}
}
}
async fn handle_fresh_message(
&mut self,
recipient: Recipient,
content: Vec<u8>,
with_reply_surb: bool,
) -> Vec<RealMessage> {
let topology_permit = self.topology_access.get_read_permit().await;
let topology_ref_option =
topology_permit.try_get_valid_topology_ref(&self.ack_recipient, Some(&recipient));
if topology_ref_option.is_none() {
warn!("Could not process the message - the network topology is invalid");
return Vec::new();
}
let topology = topology_ref_option.unwrap();
// split the message, attach optional reply surb
let (split_message, reply_key) = self
.message_preparer
.prepare_and_split_message(content, with_reply_surb, topology)
.expect("somehow the topology was invalid after all!");
if let Some(reply_key) = reply_key {
self.reply_key_storage
.insert_encryption_key(reply_key)
.expect("Failed to insert surb reply key to the store!")
}
// encrypt chunks, put them inside sphinx packets and generate acks
let mut pending_acks = Vec::with_capacity(split_message.len());
let mut real_messages = Vec::with_capacity(split_message.len());
for message_chunk in split_message {
// since the paths can be constructed, this CAN'T fail, if it does, there's a bug somewhere
let frag_id = message_chunk.fragment_identifier();
// we need to clone it because we need to keep it in memory in case we had to retransmit
// it. And then we'd need to recreate entire ACK again.
let chunk_clone = message_chunk.clone();
let prepared_fragment = self
.message_preparer
.prepare_chunk_for_sending(chunk_clone, topology, &self.ack_key, &recipient)
.unwrap();
real_messages.push(RealMessage::new(
prepared_fragment.first_hop_address,
prepared_fragment.sphinx_packet,
frag_id,
));
let pending_ack = PendingAcknowledgement::new(
message_chunk,
prepared_fragment.total_delay,
recipient.clone(),
);
pending_acks.push((frag_id, pending_ack));
}
// first insert pending_acks only then request fragments to be sent, otherwise you might get
// some very nasty (and time-consuming to figure out...) race condition.
let mut pending_acks_map_write_guard = self.pending_acks.write().await;
for (frag_id, pending_ack) in pending_acks.into_iter() {
if pending_acks_map_write_guard
.insert(frag_id, pending_ack)
.is_some()
{
panic!("Tried to insert duplicate pending ack")
}
}
real_messages
}
async fn on_input_message(&mut self, msg: InputMessage) {
let real_messages = match msg {
InputMessage::Fresh {
recipient,
data,
with_reply_surb,
} => {
self.handle_fresh_message(recipient, data, with_reply_surb)
.await
}
InputMessage::Reply { reply_surb, data } => {
if let Some(real_message) = self.handle_reply(reply_surb, data).await {
vec![real_message]
} else {
return;
}
}
};
for real_message in real_messages {
self.real_message_sender
.unbounded_send(real_message)
.unwrap();
}
}
pub(super) async fn run(&mut self) {
debug!("Started InputMessageListener");
while let Some(input_msg) = self.input_receiver.next().await {
self.on_input_message(input_msg).await;
}
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -1,230 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use self::{
acknowledgement_listener::AcknowledgementListener,
input_message_listener::InputMessageListener,
retransmission_request_listener::RetransmissionRequestListener,
sent_notification_listener::SentNotificationListener,
};
use super::real_traffic_stream::RealMessageSender;
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::{inbound_messages::InputMessageReceiver, topology_control::TopologyAccessor};
use futures::channel::mpsc;
use gateway_client::AcknowledgementReceiver;
use log::*;
use nymsphinx::preparer::MessagePreparer;
use nymsphinx::{
acknowledgements::AckKey,
addressing::clients::Recipient,
chunking::fragment::{Fragment, FragmentIdentifier},
Delay,
};
use rand::{CryptoRng, Rng};
use std::{collections::HashMap, sync::Arc, time::Duration};
use tokio::{
sync::{Notify, RwLock},
task::JoinHandle,
};
mod acknowledgement_listener;
mod input_message_listener;
mod retransmission_request_listener;
mod sent_notification_listener;
type RetransmissionRequestSender = mpsc::UnboundedSender<FragmentIdentifier>;
type RetransmissionRequestReceiver = mpsc::UnboundedReceiver<FragmentIdentifier>;
pub(super) type SentPacketNotificationSender = mpsc::UnboundedSender<FragmentIdentifier>;
type SentPacketNotificationReceiver = mpsc::UnboundedReceiver<FragmentIdentifier>;
type PendingAcksMap = Arc<RwLock<HashMap<FragmentIdentifier, PendingAcknowledgement>>>;
struct PendingAcknowledgement {
message_chunk: Fragment,
delay: Delay,
recipient: Recipient,
retransmission_cancel: Arc<Notify>,
}
impl PendingAcknowledgement {
fn new(message_chunk: Fragment, delay: Delay, recipient: Recipient) -> Self {
PendingAcknowledgement {
message_chunk,
delay,
retransmission_cancel: Arc::new(Notify::new()),
recipient,
}
}
fn update_delay(&mut self, new_delay: Delay) {
self.delay = new_delay;
}
}
pub(super) struct AcknowledgementControllerConnectors {
real_message_sender: RealMessageSender,
input_receiver: InputMessageReceiver,
sent_notifier: SentPacketNotificationReceiver,
ack_receiver: AcknowledgementReceiver,
}
impl AcknowledgementControllerConnectors {
pub(super) fn new(
real_message_sender: RealMessageSender,
input_receiver: InputMessageReceiver,
sent_notifier: SentPacketNotificationReceiver,
ack_receiver: AcknowledgementReceiver,
) -> Self {
AcknowledgementControllerConnectors {
real_message_sender,
input_receiver,
sent_notifier,
ack_receiver,
}
}
}
pub(super) struct AcknowledgementController<R>
where
R: CryptoRng + Rng,
{
acknowledgement_listener: Option<AcknowledgementListener>,
input_message_listener: Option<InputMessageListener<R>>,
retransmission_request_listener: Option<RetransmissionRequestListener<R>>,
sent_notification_listener: Option<SentNotificationListener>,
}
impl<R> AcknowledgementController<R>
where
R: 'static + CryptoRng + Rng + Clone + Send,
{
pub(super) fn new(
rng: R,
topology_access: TopologyAccessor,
ack_key: Arc<AckKey>,
ack_recipient: Recipient,
reply_key_storage: ReplyKeyStorage,
average_packet_delay: Duration,
average_ack_delay: Duration,
ack_wait_multiplier: f64,
ack_wait_addition: Duration,
connectors: AcknowledgementControllerConnectors,
) -> Self {
let pending_acks = Arc::new(RwLock::new(HashMap::new()));
let message_preparer = MessagePreparer::new(
rng,
ack_recipient.clone(),
average_packet_delay,
average_ack_delay,
);
let acknowledgement_listener = AcknowledgementListener::new(
Arc::clone(&ack_key),
connectors.ack_receiver,
Arc::clone(&pending_acks),
);
let input_message_listener = InputMessageListener::new(
Arc::clone(&ack_key),
ack_recipient.clone(),
connectors.input_receiver,
message_preparer.clone(),
Arc::clone(&pending_acks),
connectors.real_message_sender.clone(),
topology_access.clone(),
reply_key_storage,
);
let (retransmission_tx, retransmission_rx) = mpsc::unbounded();
let retransmission_request_listener = RetransmissionRequestListener::new(
Arc::clone(&ack_key),
ack_recipient,
message_preparer,
Arc::clone(&pending_acks),
connectors.real_message_sender,
retransmission_rx,
topology_access,
);
let sent_notification_listener = SentNotificationListener::new(
ack_wait_multiplier,
ack_wait_addition,
connectors.sent_notifier,
pending_acks,
retransmission_tx,
);
AcknowledgementController {
acknowledgement_listener: Some(acknowledgement_listener),
input_message_listener: Some(input_message_listener),
retransmission_request_listener: Some(retransmission_request_listener),
sent_notification_listener: Some(sent_notification_listener),
}
}
pub(super) async fn run(&mut self) {
let mut acknowledgement_listener = self.acknowledgement_listener.take().unwrap();
let mut input_message_listener = self.input_message_listener.take().unwrap();
let mut retransmission_request_listener =
self.retransmission_request_listener.take().unwrap();
let mut sent_notification_listener = self.sent_notification_listener.take().unwrap();
// TODO: perhaps an extra 'DEBUG' task that would periodically check for stale entries in
// pending acks map?
// It would only be 'DEBUG' as I don't expect any stale entries to exist there to begin with,
// but when can bugs be expected to begin with?
// the below are log messages are errors as at the current stage we do not expect any of
// the task to ever finish. This will of course change once we introduce
// graceful shutdowns.
let ack_listener_fut = tokio::spawn(async move {
acknowledgement_listener.run().await;
error!("The acknowledgement listener has finished execution!");
acknowledgement_listener
});
let input_listener_fut = tokio::spawn(async move {
input_message_listener.run().await;
error!("The input listener has finished execution!");
input_message_listener
});
let retransmission_req_fut = tokio::spawn(async move {
retransmission_request_listener.run().await;
error!("The retransmission request listener has finished execution!");
retransmission_request_listener
});
let sent_notification_fut = tokio::spawn(async move {
sent_notification_listener.run().await;
error!("The sent notification listener has finished execution!");
sent_notification_listener
});
// technically we don't have to bring `AcknowledgementController` back to a valid state
// but we can do it, so why not? Perhaps it might be useful if we wanted to allow
// for restarts of certain modules without killing the entire process.
self.acknowledgement_listener = Some(ack_listener_fut.await.unwrap());
self.input_message_listener = Some(input_listener_fut.await.unwrap());
self.retransmission_request_listener = Some(retransmission_req_fut.await.unwrap());
self.sent_notification_listener = Some(sent_notification_fut.await.unwrap());
}
#[allow(dead_code)]
pub(super) fn start(mut self) -> JoinHandle<Self> {
tokio::spawn(async move {
self.run().await;
self
})
}
}
@@ -1,129 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{PendingAcksMap, RetransmissionRequestReceiver};
use crate::client::{
real_messages_control::real_traffic_stream::{RealMessage, RealMessageSender},
topology_control::TopologyAccessor,
};
use futures::StreamExt;
use log::*;
use nymsphinx::preparer::MessagePreparer;
use nymsphinx::{
acknowledgements::AckKey, addressing::clients::Recipient,
chunking::fragment::FragmentIdentifier,
};
use rand::{CryptoRng, Rng};
use std::sync::Arc;
// responsible for packet retransmission upon fired timer
pub(super) struct RetransmissionRequestListener<R>
where
R: CryptoRng + Rng,
{
ack_key: Arc<AckKey>,
ack_recipient: Recipient,
message_preparer: MessagePreparer<R>,
pending_acks: PendingAcksMap,
real_message_sender: RealMessageSender,
request_receiver: RetransmissionRequestReceiver,
topology_access: TopologyAccessor,
}
impl<R> RetransmissionRequestListener<R>
where
R: CryptoRng + Rng,
{
pub(super) fn new(
ack_key: Arc<AckKey>,
ack_recipient: Recipient,
message_preparer: MessagePreparer<R>,
pending_acks: PendingAcksMap,
real_message_sender: RealMessageSender,
request_receiver: RetransmissionRequestReceiver,
topology_access: TopologyAccessor,
) -> Self {
RetransmissionRequestListener {
ack_key,
ack_recipient,
message_preparer,
pending_acks,
real_message_sender,
request_receiver,
topology_access,
}
}
async fn on_retransmission_request(&mut self, frag_id: FragmentIdentifier) {
let pending_acks_map_read_guard = self.pending_acks.read().await;
// if the unwrap failed here, we have some weird bug somewhere - honestly, I'm not sure
// if it's even possible for it to happen
let unreceived_ack_fragment = pending_acks_map_read_guard
.get(&frag_id)
.expect("wanted to retransmit ack'd fragment");
let packet_recipient = unreceived_ack_fragment.recipient.clone();
let chunk_clone = unreceived_ack_fragment.message_chunk.clone();
let frag_id = unreceived_ack_fragment.message_chunk.fragment_identifier();
// TODO: we need some proper benchmarking here to determine whether it could
// be more efficient to just get write lock and keep it while doing sphinx computation,
// but my gut feeling tells me we should re-acquire it.
drop(pending_acks_map_read_guard);
let topology_permit = self.topology_access.get_read_permit().await;
let topology_ref_option = topology_permit
.try_get_valid_topology_ref(&self.ack_recipient, Some(&packet_recipient));
if topology_ref_option.is_none() {
warn!("Could not retransmit the packet - the network topology is invalid");
// TODO: perhaps put back into pending acks and reset the timer?
return;
}
let topology_ref = topology_ref_option.unwrap();
let prepared_fragment = self
.message_preparer
.prepare_chunk_for_sending(chunk_clone, topology_ref, &self.ack_key, &packet_recipient)
.unwrap();
// minor optimization to not hold the permit while we no longer need it and might have to block
// waiting for the write lock on `pending_acks`
drop(topology_permit);
self.pending_acks
.write()
.await
.get_mut(&frag_id)
.expect(
"on_retransmission_request: somehow we already received an ack for this packet?",
)
.update_delay(prepared_fragment.total_delay);
self.real_message_sender
.unbounded_send(RealMessage::new(
prepared_fragment.first_hop_address,
prepared_fragment.sphinx_packet,
frag_id,
))
.unwrap();
}
pub(super) async fn run(&mut self) {
debug!("Started RetransmissionRequestListener");
while let Some(frag_id) = self.request_receiver.next().await {
self.on_retransmission_request(frag_id).await;
}
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -1,109 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{PendingAcksMap, RetransmissionRequestSender, SentPacketNotificationReceiver};
use futures::StreamExt;
use log::*;
use nymsphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID};
use std::sync::Arc;
use std::time::Duration;
// responsible for starting and controlling retransmission timers
// it is required because when we send our packet to the `real traffic stream` controlled
// with poisson timer, there's no guarantee the message will be sent immediately, so we might
// accidentally fire retransmission way quicker than we would have wanted.
pub(super) struct SentNotificationListener {
ack_wait_multiplier: f64,
ack_wait_addition: Duration,
sent_notifier: SentPacketNotificationReceiver,
pending_acks: PendingAcksMap,
retransmission_sender: RetransmissionRequestSender,
}
impl SentNotificationListener {
pub(super) fn new(
ack_wait_multiplier: f64,
ack_wait_addition: Duration,
sent_notifier: SentPacketNotificationReceiver,
pending_acks: PendingAcksMap,
retransmission_sender: RetransmissionRequestSender,
) -> Self {
SentNotificationListener {
ack_wait_multiplier,
ack_wait_addition,
sent_notifier,
pending_acks,
retransmission_sender,
}
}
async fn on_sent_message(&mut self, frag_id: FragmentIdentifier) {
if frag_id == COVER_FRAG_ID {
trace!("sent off a cover message - no need to start retransmission timer!");
return;
} else if frag_id.is_reply() {
debug!("sent off a reply message - no need to start retransmission timer!");
// TODO: probably there will need to be some extra procedure here, like it would
// be nice to know that our reply actually reached the recipient (i.e. we got the ack)
return;
}
let pending_acks_map_read_guard = self.pending_acks.read().await;
// if the unwrap failed here, we have some weird bug somewhere
// although when I think about it, it *theoretically* could happen under extremely heavy client
// load that `on_sent_message()` is not called (and we do not receive the read permit)
// until we already received and processed an ack for the packet
// but this seems extremely unrealistic, but perhaps we should guard against that?
let pending_ack_data = pending_acks_map_read_guard
.get(&frag_id)
.expect("on_sent_message: somehow we already received an ack for this packet?");
// if this assertion ever fails, we have some bug due to some unintended leak.
// the only reason I see it could happen if the `tokio::select` in the spawned
// task below somehow did not drop it
debug_assert_eq!(
Arc::strong_count(&pending_ack_data.retransmission_cancel),
1
);
// TODO: read more about Arc::downgrade. it could be useful here
let retransmission_cancel = Arc::clone(&pending_ack_data.retransmission_cancel);
let retransmission_timeout = tokio::time::delay_for(
(pending_ack_data.delay.clone() * self.ack_wait_multiplier).to_duration()
+ self.ack_wait_addition,
);
let retransmission_sender = self.retransmission_sender.clone();
tokio::spawn(async move {
tokio::select! {
_ = retransmission_cancel.notified() => {
trace!("received ack for the fragment. Cancelling retransmission future");
}
_ = retransmission_timeout => {
trace!("did not receive an ack - will retransmit the packet");
retransmission_sender.unbounded_send(frag_id).unwrap();
}
}
});
}
pub(super) async fn run(&mut self) {
debug!("Started SentNotificationListener");
while let Some(frag_id) = self.sent_notifier.next().await {
self.on_sent_message(frag_id).await;
}
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -1,245 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::mix_traffic::{MixMessage, MixMessageSender};
use crate::client::real_messages_control::acknowlegement_control::SentPacketNotificationSender;
use crate::client::topology_control::TopologyAccessor;
use futures::channel::mpsc;
use futures::task::{Context, Poll};
use futures::{Future, Stream, StreamExt};
use log::*;
use nymsphinx::acknowledgements::AckKey;
use nymsphinx::addressing::{clients::Recipient, nodes::NymNodeRoutingAddress};
use nymsphinx::chunking::fragment::FragmentIdentifier;
use nymsphinx::cover::generate_loop_cover_packet;
use nymsphinx::utils::sample_poisson_duration;
use nymsphinx::SphinxPacket;
use rand::{CryptoRng, Rng};
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use tokio::time;
pub(crate) struct OutQueueControl<R>
where
R: CryptoRng + Rng,
{
/// Key used to encrypt and decrypt content of an ACK packet.
ack_key: Arc<AckKey>,
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
average_ack_delay: Duration,
/// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: Duration,
/// Average delay between sending subsequent packets.
average_message_sending_delay: Duration,
/// Channel used for notifying of a real packet being sent out. Used to start up retransmission timer.
sent_notifier: SentPacketNotificationSender,
/// Internal state, determined by `average_message_sending_delay`,
/// used to keep track of when a next packet should be sent out.
next_delay: time::Delay,
/// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them
/// out to the network without any further delays.
mix_tx: MixMessageSender,
/// Channel used for receiving real, prepared, messages that must be first sufficiently delayed
/// before being sent out into the network.
real_receiver: RealMessageReceiver,
/// Represents full address of this client.
our_full_destination: Recipient,
/// Instance of a cryptographically secure random number generator.
rng: R,
/// Accessor to the common instance of network topology.
topology_access: TopologyAccessor,
}
pub(crate) struct RealMessage {
first_hop_address: NymNodeRoutingAddress,
packet: SphinxPacket,
fragment_id: FragmentIdentifier,
}
impl RealMessage {
pub(crate) fn new(
first_hop_address: NymNodeRoutingAddress,
packet: SphinxPacket,
fragment_id: FragmentIdentifier,
) -> Self {
RealMessage {
first_hop_address,
packet,
fragment_id,
}
}
}
// messages are already prepared, etc. the real point of it is to forward it to mix_traffic
// after sufficient delay
pub(crate) type RealMessageSender = mpsc::UnboundedSender<RealMessage>;
type RealMessageReceiver = mpsc::UnboundedReceiver<RealMessage>;
pub(crate) enum StreamMessage {
Cover,
Real(RealMessage),
}
impl<R> Stream for OutQueueControl<R>
where
R: CryptoRng + Rng + Unpin,
{
type Item = StreamMessage;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// it is not yet time to return a message
if Pin::new(&mut self.next_delay).poll(cx).is_pending() {
return Poll::Pending;
};
// we know it's time to send a message, so let's prepare delay for the next one
// Get the `now` by looking at the current `delay` deadline
let avg_delay = self.average_message_sending_delay;
let now = self.next_delay.deadline();
let next_poisson_delay = sample_poisson_duration(&mut self.rng, avg_delay);
// The next interval value is `next_poisson_delay` after the one that just
// yielded.
let next = now + next_poisson_delay;
self.next_delay.reset(next);
// decide what kind of message to send
match Pin::new(&mut self.real_receiver).poll_next(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
// (and whoever is using the stream should panic)
Poll::Ready(None) => Poll::Ready(None),
// if there's an actual message - return it
Poll::Ready(Some(real_message)) => Poll::Ready(Some(StreamMessage::Real(real_message))),
// otherwise construct a dummy one
Poll::Pending => Poll::Ready(Some(StreamMessage::Cover)),
}
}
}
impl<R> OutQueueControl<R>
where
R: CryptoRng + Rng + Unpin,
{
pub(crate) fn new(
ack_key: Arc<AckKey>,
average_ack_delay: Duration,
average_packet_delay: Duration,
average_message_sending_delay: Duration,
sent_notifier: SentPacketNotificationSender,
mix_tx: MixMessageSender,
real_receiver: RealMessageReceiver,
rng: R,
our_full_destination: Recipient,
topology_access: TopologyAccessor,
) -> Self {
OutQueueControl {
ack_key,
average_ack_delay,
average_packet_delay,
average_message_sending_delay,
sent_notifier,
next_delay: time::delay_for(Default::default()),
mix_tx,
real_receiver,
our_full_destination,
rng,
topology_access,
}
}
async fn on_message(&mut self, next_message: StreamMessage) {
trace!("created new message");
let next_message = match next_message {
StreamMessage::Cover => {
// TODO for way down the line: in very rare cases (during topology update) we might have
// to wait a really tiny bit before actually obtaining the permit hence messing with our
// poisson delay, but is it really a problem?
let topology_permit = self.topology_access.get_read_permit().await;
// the ack is sent back to ourselves (and then ignored)
let topology_ref_option = topology_permit.try_get_valid_topology_ref(
&self.our_full_destination,
Some(&self.our_full_destination),
);
if topology_ref_option.is_none() {
warn!(
"No valid topology detected - won't send any loop cover message this time"
);
return;
}
let topology_ref = topology_ref_option.unwrap();
let cover_message = generate_loop_cover_packet(
&mut self.rng,
topology_ref,
&*self.ack_key,
&self.our_full_destination,
self.average_ack_delay,
self.average_packet_delay,
)
.expect("Somehow failed to generate a loop cover message with a valid topology");
MixMessage::new(cover_message.0, cover_message.1)
}
StreamMessage::Real(real_message) => {
// well technically the message was not sent just yet, but now it's up to internal
// queues and client load rather than the required delay. So realistically we can treat
// whatever is about to happen as negligible additional delay.
self.sent_notifier
.unbounded_send(real_message.fragment_id)
.unwrap();
MixMessage::new(real_message.first_hop_address, real_message.packet)
}
};
// if this one fails, there's no retrying because it means that either:
// - we run out of memory
// - the receiver channel is closed
// in either case there's no recovery and we can only panic
self.mix_tx.unbounded_send(next_message).unwrap();
// JS: Not entirely sure why or how it fixes stuff, but without the yield call,
// the UnboundedReceiver [of mix_rx] will not get a chance to read anything
// JS2: Basically it was the case that with high enough rate, the stream had already a next value
// ready and hence was immediately re-scheduled causing other tasks to be starved;
// yield makes it go back the scheduling queue regardless of its value availability
tokio::task::yield_now().await;
}
pub(crate) async fn run_out_queue_control(&mut self) {
// we should set initial delay only when we actually start the stream
self.next_delay = time::delay_for(sample_poisson_duration(
&mut self.rng,
self.average_message_sending_delay,
));
info!("Starting out queue controller...");
while let Some(next_message) = self.next().await {
self.on_message(next_message).await;
}
}
}
@@ -1,374 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::reply_key_storage::ReplyKeyStorage;
use crypto::asymmetric::encryption;
use crypto::symmetric::stream_cipher;
use crypto::Digest;
use futures::channel::mpsc;
use futures::lock::Mutex;
use futures::StreamExt;
use gateway_client::MixnetMessageReceiver;
use log::*;
use nymsphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SURBEncryptionKey};
use nymsphinx::params::{ReplySURBEncryptionAlgorithm, ReplySURBKeyDigestAlgorithm};
use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
use std::collections::HashSet;
use std::sync::Arc;
use tokio::runtime::Handle;
use tokio::task::JoinHandle;
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
// or to say "hey, I'm going offline, don't send anything more to me. Just buffer them instead"
pub(crate) type ReceivedBufferRequestSender = mpsc::UnboundedSender<ReceivedBufferMessage>;
pub(crate) type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver<ReceivedBufferMessage>;
// The channel set for the above
pub(crate) type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
pub(crate) type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
struct ReceivedMessagesBufferInner {
messages: Vec<ReconstructedMessage>,
local_encryption_keypair: Arc<encryption::KeyPair>,
// TODO: looking how it 'looks' here, perhaps `MessageReceiver` should be renamed to something
// else instead.
message_receiver: MessageReceiver,
message_sender: Option<ReconstructedMessagesSender>,
// TODO: this will get cleared upon re-running the client
// but perhaps it should be changed to include timestamps of when the message was reconstructed
// and every now and then remove ids older than X
recently_reconstructed: HashSet<i32>,
}
impl ReceivedMessagesBufferInner {
fn process_received_fragment(&mut self, raw_fragment: Vec<u8>) -> Option<ReconstructedMessage> {
let fragment_data = match self
.message_receiver
.recover_plaintext(self.local_encryption_keypair.private_key(), raw_fragment)
{
Err(e) => {
warn!("failed to recover fragment data: {:?}. The whole underlying message might be corrupted and unrecoverable!", e);
return None;
}
Ok(frag_data) => frag_data,
};
if nymsphinx::cover::is_cover(&fragment_data) {
trace!("The message was a loop cover message! Skipping it");
return None;
}
let fragment = match self.message_receiver.recover_fragment(&fragment_data) {
Err(e) => {
warn!("failed to recover fragment from raw data: {:?}. The whole underlying message might be corrupted and unrecoverable!", e);
return None;
}
Ok(frag) => frag,
};
if self.recently_reconstructed.contains(&fragment.id()) {
debug!("Received a chunk of already re-assembled message ({:?})! It probably got here because the ack got lost", fragment.id());
return None;
}
// if we returned an error the underlying message is malformed in some way
match self.message_receiver.insert_new_fragment(fragment) {
Err(err) => match err {
MessageRecoveryError::MalformedReconstructedMessage(message_sets) => {
// TODO: should we really insert reconstructed sets? could this be abused for some attack?
for set_id in message_sets {
if !self.recently_reconstructed.insert(set_id) {
// or perhaps we should even panic at this point?
error!("Reconstructed another message containing already used set id!")
}
}
None
}
_ => unreachable!(
"no other error kind should have been returned here! If so, it's a bug!"
),
},
Ok(reconstruction_result) => match reconstruction_result {
Some((reconstructed_message, used_sets)) => {
for set_id in used_sets {
if !self.recently_reconstructed.insert(set_id) {
// or perhaps we should even panic at this point?
error!("Reconstructed another message containing already used set id!")
}
}
Some(reconstructed_message)
}
None => None,
},
}
}
}
#[derive(Debug, Clone)]
// Note: you should NEVER create more than a single instance of this using 'new()'.
// You should always use .clone() to create additional instances
struct ReceivedMessagesBuffer {
inner: Arc<Mutex<ReceivedMessagesBufferInner>>,
/// Storage containing keys to all [`ReplySURB`]s ever sent out that we did not receive back.
// There's no need to put it behind a Mutex since it's already properly concurrent
reply_key_storage: ReplyKeyStorage,
}
impl ReceivedMessagesBuffer {
fn new(
local_encryption_keypair: Arc<encryption::KeyPair>,
reply_key_storage: ReplyKeyStorage,
) -> Self {
ReceivedMessagesBuffer {
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
messages: Vec::new(),
local_encryption_keypair,
message_receiver: MessageReceiver::new(),
message_sender: None,
recently_reconstructed: HashSet::new(),
})),
reply_key_storage,
}
}
async fn disconnect_sender(&mut self) {
let mut guard = self.inner.lock().await;
if guard.message_sender.is_none() {
// in theory we could just ignore it, but that situation should have never happened
// in the first place, so this way we at least know we have an important bug to fix
panic!("trying to disconnect non-existent sender!")
}
guard.message_sender = None;
}
async fn connect_sender(&mut self, sender: ReconstructedMessagesSender) {
let mut guard = self.inner.lock().await;
if guard.message_sender.is_some() {
// in theory we could just ignore it, but that situation should have never happened
// in the first place, so this way we at least know we have an important bug to fix
panic!("trying overwrite an existing sender!")
}
// while we're at it, also empty the buffer if we happened to receive anything while
// no sender was connected
let stored_messages = std::mem::replace(&mut guard.messages, Vec::new());
if !stored_messages.is_empty() {
if let Err(err) = sender.unbounded_send(stored_messages) {
error!(
"The sender channel we just received is already invalidated - {:?}",
err
);
// put the values back to the buffer
// the returned error has two fields: err: SendError and val: T,
// where val is the value that was failed to get sent;
// it's returned by the `into_inner` call
guard.messages = err.into_inner();
return;
}
}
guard.message_sender = Some(sender);
}
async fn add_reconstructed_messages(&mut self, msgs: Vec<ReconstructedMessage>) {
debug!("Adding {:?} new messages to the buffer!", msgs.len());
trace!("Adding new messages to the buffer! {:?}", msgs);
self.inner.lock().await.messages.extend(msgs)
}
fn process_received_reply(
reply_ciphertext: &[u8],
reply_key: SURBEncryptionKey,
) -> Option<ReconstructedMessage> {
let zero_iv = stream_cipher::zero_iv::<ReplySURBEncryptionAlgorithm>();
let mut reply_msg = stream_cipher::decrypt::<ReplySURBEncryptionAlgorithm>(
&reply_key.inner(),
&zero_iv,
reply_ciphertext,
);
if let Err(err) = MessageReceiver::remove_padding(&mut reply_msg) {
warn!("Received reply had malformed padding! - {:?}", err);
None
} else {
// TODO: perhaps having to say it doesn't have a surb an indication the type should be changed?
Some(ReconstructedMessage {
message: reply_msg,
reply_SURB: None,
})
}
}
async fn handle_new_received(&mut self, msgs: Vec<Vec<u8>>) {
debug!(
"Processing {:?} new message that might get added to the buffer!",
msgs.len()
);
let mut completed_messages = Vec::new();
let mut inner_guard = self.inner.lock().await;
let reply_surb_digest_size = ReplySURBKeyDigestAlgorithm::output_size();
// first check if this is a reply or a chunked message
// TODO: verify with @AP if this way of doing it is safe or whether it could
// cause some attacks due to, I don't know, stupid edge case collisions?
// Update: this DOES introduce a possible leakage: https://github.com/nymtech/nym/issues/296
for msg in msgs {
let possible_key_digest =
EncryptionKeyDigest::clone_from_slice(&msg[..reply_surb_digest_size]);
// check first `HasherOutputSize` bytes if they correspond to known encryption key
// if yes - this is a reply message
// TODO: this might be a bottleneck - since the keys are stored on disk we, presumably,
// are doing a disk operation every single received fragment
if let Some(reply_encryption_key) = self
.reply_key_storage
.get_and_remove_encryption_key(possible_key_digest)
.expect("storage operation failed!")
{
if let Some(completed_message) = Self::process_received_reply(
&msg[reply_surb_digest_size..],
reply_encryption_key,
) {
completed_messages.push(completed_message)
}
} else {
// otherwise - it's a 'normal' message
if let Some(completed_message) = inner_guard.process_received_fragment(msg) {
completed_messages.push(completed_message)
}
}
}
if !completed_messages.is_empty() {
if let Some(sender) = &inner_guard.message_sender {
trace!("Sending reconstructed messages to announced sender");
if let Err(err) = sender.unbounded_send(completed_messages) {
warn!("The reconstructed message receiver went offline without explicit notification (relevant error: - {:?})", err);
// make sure to drop the lock to not deadlock
// (it is required by `add_reconstructed_messages`)
inner_guard.message_sender = None;
drop(inner_guard);
self.add_reconstructed_messages(err.into_inner()).await;
}
} else {
// make sure to drop the lock to not deadlock
// (it is required by `add_reconstructed_messages`)
drop(inner_guard);
trace!("No sender available - buffering reconstructed messages");
self.add_reconstructed_messages(completed_messages).await;
}
}
}
}
pub(crate) enum ReceivedBufferMessage {
// Signals a websocket connection (or a native implementation) was established and we should stop buffering messages,
// and instead send them directly to the received channel
ReceiverAnnounce(ReconstructedMessagesSender),
// Explicit signal that Receiver connection will no longer accept messages
ReceiverDisconnect,
}
struct RequestReceiver {
received_buffer: ReceivedMessagesBuffer,
query_receiver: ReceivedBufferRequestReceiver,
}
impl RequestReceiver {
fn new(
received_buffer: ReceivedMessagesBuffer,
query_receiver: ReceivedBufferRequestReceiver,
) -> Self {
RequestReceiver {
received_buffer,
query_receiver,
}
}
fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
while let Some(request) = self.query_receiver.next().await {
match request {
ReceivedBufferMessage::ReceiverAnnounce(sender) => {
self.received_buffer.connect_sender(sender).await;
}
ReceivedBufferMessage::ReceiverDisconnect => {
self.received_buffer.disconnect_sender().await
}
}
}
})
}
}
struct FragmentedMessageReceiver {
received_buffer: ReceivedMessagesBuffer,
mixnet_packet_receiver: MixnetMessageReceiver,
}
impl FragmentedMessageReceiver {
fn new(
received_buffer: ReceivedMessagesBuffer,
mixnet_packet_receiver: MixnetMessageReceiver,
) -> Self {
FragmentedMessageReceiver {
received_buffer,
mixnet_packet_receiver,
}
}
fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
while let Some(new_messages) = self.mixnet_packet_receiver.next().await {
self.received_buffer.handle_new_received(new_messages).await;
}
})
}
}
pub(crate) struct ReceivedMessagesBufferController {
fragmented_message_receiver: FragmentedMessageReceiver,
request_receiver: RequestReceiver,
}
impl ReceivedMessagesBufferController {
pub(crate) fn new(
local_encryption_keypair: Arc<encryption::KeyPair>,
query_receiver: ReceivedBufferRequestReceiver,
mixnet_packet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage,
) -> Self {
let received_buffer =
ReceivedMessagesBuffer::new(local_encryption_keypair, reply_key_storage);
ReceivedMessagesBufferController {
fragmented_message_receiver: FragmentedMessageReceiver::new(
received_buffer.clone(),
mixnet_packet_receiver,
),
request_receiver: RequestReceiver::new(received_buffer, query_receiver),
}
}
pub(crate) fn start(self, handle: &Handle) {
// TODO: should we do anything with JoinHandle(s) returned by start methods?
self.fragmented_message_receiver.start(handle);
self.request_receiver.start(handle);
}
}
@@ -1,204 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::built_info;
use directory_client::DirectoryClient;
use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::params::DEFAULT_NUM_MIX_HOPS;
use std::convert::TryInto;
use std::ops::Deref;
use std::sync::Arc;
use std::time;
use std::time::Duration;
use tokio::runtime::Handle;
use tokio::sync::{RwLock, RwLockReadGuard};
use tokio::task::JoinHandle;
use topology::NymTopology;
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
#[derive(Debug)]
pub(super) struct TopologyAccessorInner(Option<NymTopology>);
impl AsRef<Option<NymTopology>> for TopologyAccessorInner {
fn as_ref(&self) -> &Option<NymTopology> {
&self.0
}
}
impl TopologyAccessorInner {
fn new() -> Self {
TopologyAccessorInner(None)
}
fn update(&mut self, new: Option<NymTopology>) {
self.0 = new;
}
}
pub(super) struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, TopologyAccessorInner>,
}
impl<'a> Deref for TopologyReadPermit<'a> {
type Target = TopologyAccessorInner;
fn deref(&self) -> &Self::Target {
&self.permit
}
}
impl<'a> TopologyReadPermit<'a> {
/// Using provided topology read permit, tries to get an immutable reference to the underlying
/// topology. For obvious reasons the lifetime of the topology reference is bound to the permit.
pub(super) fn try_get_valid_topology_ref(
&'a self,
ack_recipient: &Recipient,
packet_recipient: Option<&Recipient>,
) -> Option<&'a NymTopology> {
// Note: implicit deref with Deref for TopologyReadPermit is happening here
let topology_ref_option = self.permit.as_ref();
match topology_ref_option {
None => None,
Some(topology_ref) => {
// see if it's possible to route the packet to both gateways
if !topology_ref.can_construct_path_through(DEFAULT_NUM_MIX_HOPS)
|| !topology_ref.gateway_exists(&ack_recipient.gateway())
|| if let Some(packet_recipient) = packet_recipient {
!topology_ref.gateway_exists(&packet_recipient.gateway())
} else {
false
}
{
None
} else {
Some(topology_ref)
}
}
}
}
}
impl<'a> From<RwLockReadGuard<'a, TopologyAccessorInner>> for TopologyReadPermit<'a> {
fn from(read_permit: RwLockReadGuard<'a, TopologyAccessorInner>) -> Self {
TopologyReadPermit {
permit: read_permit,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct TopologyAccessor {
// `RwLock` *seems to* be the better approach for this as write access is only requested every
// few seconds, while reads are needed every single packet generated.
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
// approach than a `Mutex`
inner: Arc<RwLock<TopologyAccessorInner>>,
}
impl TopologyAccessor {
pub(crate) fn new() -> Self {
TopologyAccessor {
inner: Arc::new(RwLock::new(TopologyAccessorInner::new())),
}
}
pub(super) async fn get_read_permit(&self) -> TopologyReadPermit<'_> {
self.inner.read().await.into()
}
async fn update_global_topology(&mut self, new_topology: Option<NymTopology>) {
self.inner.write().await.update(new_topology);
}
// only used by the client at startup to get a slightly more reasonable error message
// (currently displays as unused because health checker is disabled due to required changes)
pub(crate) async fn is_routable(&self) -> bool {
match &self.inner.read().await.0 {
None => false,
Some(ref topology) => topology.can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
}
}
}
pub(crate) struct TopologyRefresherConfig {
directory_server: String,
refresh_rate: time::Duration,
}
impl TopologyRefresherConfig {
pub(crate) fn new(directory_server: String, refresh_rate: time::Duration) -> Self {
TopologyRefresherConfig {
directory_server,
refresh_rate,
}
}
}
pub(crate) struct TopologyRefresher {
directory_client: directory_client::Client,
topology_accessor: TopologyAccessor,
refresh_rate: Duration,
}
impl TopologyRefresher {
pub(crate) fn new_directory_client(
cfg: TopologyRefresherConfig,
topology_accessor: TopologyAccessor,
) -> Self {
let directory_client_config = directory_client::Config::new(cfg.directory_server);
let directory_client = directory_client::Client::new(directory_client_config);
TopologyRefresher {
directory_client,
topology_accessor,
refresh_rate: cfg.refresh_rate,
}
}
async fn get_current_compatible_topology(&self) -> Option<NymTopology> {
match self.directory_client.get_topology().await {
Err(err) => {
error!("failed to get network topology! - {:?}", err);
None
}
Ok(topology) => {
let nym_topology: NymTopology = topology.try_into().ok()?;
Some(nym_topology.filter_system_version(built_info::PKG_VERSION))
}
}
}
pub(crate) async fn refresh(&mut self) {
trace!("Refreshing the topology");
let new_topology = self.get_current_compatible_topology().await;
self.topology_accessor
.update_global_topology(new_topology)
.await;
}
pub(crate) async fn is_topology_routable(&self) -> bool {
self.topology_accessor.is_routable().await
}
pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> {
handle.spawn(async move {
loop {
tokio::time::delay_for(self.refresh_rate).await;
self.refresh().await;
}
})
}
}
+3 -3
View File
@@ -13,10 +13,10 @@
// limitations under the License.
use crate::built_info;
use crate::client::key_manager::KeyManager;
use crate::commands::override_config;
use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use config::NymConfig;
use crypto::asymmetric::identity;
use directory_client::DirectoryClient;
@@ -149,7 +149,7 @@ pub fn execute(matches: &ArgMatches) {
println!("Initialising client...");
let id = matches.value_of("id").unwrap(); // required for now
let mut config = crate::config::Config::new(id);
let mut config = client_core::config::Config::new(id);
let mut rng = OsRng;
config = override_config(config, matches);
+1 -1
View File
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::{Config, SocketType};
use clap::ArgMatches;
use client_core::config::{Config, SocketType};
pub mod init;
pub mod run;
+1 -1
View File
@@ -14,8 +14,8 @@
use crate::client::NymClient;
use crate::commands::override_config;
use crate::config::Config;
use clap::{App, Arg, ArgMatches};
use client_core::config::Config;
use config::NymConfig;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
-503
View File
@@ -1,503 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::template::config_template;
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time;
pub mod persistence;
mod template;
// 'CLIENT'
const DEFAULT_LISTENING_PORT: u16 = 1977;
const DEFAULT_DIRECTORY_SERVER: &str = "https://directory.nymtech.net";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: u64 = 800;
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: u64 = 1000; // 1s
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: u64 = 500; // 0.5s
const DEFAULT_AVERAGE_PACKET_DELAY: u64 = 200; // 0.2s
const DEFAULT_TOPOLOGY_REFRESH_RATE: u64 = 30_000; // 30s
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: u64 = 5_000; // 5s
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: u64 = 1_500; // 1.5s
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketType {
WebSocket,
None,
}
impl SocketType {
pub fn from_string<S: Into<String>>(val: S) -> Self {
let mut upper = val.into();
upper.make_ascii_uppercase();
match upper.as_ref() {
"WEBSOCKET" | "WS" => SocketType::WebSocket,
_ => SocketType::None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
client: Client,
socket: Socket,
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: Debug,
}
impl NymConfig for Config {
fn template() -> &'static str {
config_template()
}
fn config_file_name() -> String {
"config.toml".to_string()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("clients")
}
fn root_directory(&self) -> PathBuf {
self.client.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.client
.nym_root_directory
.join(&self.client.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.client
.nym_root_directory
.join(&self.client.id)
.join("data")
}
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
Config::default().with_id(id)
}
// builder methods
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self {
let id = id.into();
// identity key setting
if self.client.private_identity_key_file.as_os_str().is_empty() {
self.client.private_identity_key_file =
self::Client::default_private_identity_key_file(&id);
}
if self.client.public_identity_key_file.as_os_str().is_empty() {
self.client.public_identity_key_file =
self::Client::default_public_identity_key_file(&id);
}
// encryption key setting
if self
.client
.private_encryption_key_file
.as_os_str()
.is_empty()
{
self.client.private_encryption_key_file =
self::Client::default_private_encryption_key_file(&id);
}
if self
.client
.public_encryption_key_file
.as_os_str()
.is_empty()
{
self.client.public_encryption_key_file =
self::Client::default_public_encryption_key_file(&id);
}
// shared gateway key setting
if self.client.gateway_shared_key_file.as_os_str().is_empty() {
self.client.gateway_shared_key_file =
self::Client::default_gateway_shared_key_file(&id);
}
// ack key setting
if self.client.ack_key_file.as_os_str().is_empty() {
self.client.ack_key_file = self::Client::default_ack_key_file(&id);
}
if self
.client
.reply_encryption_key_store_path
.as_os_str()
.is_empty()
{
self.client.reply_encryption_key_store_path =
self::Client::default_reply_encryption_key_store_path(&id);
}
self.client.id = id;
self
}
pub fn with_gateway_id<S: Into<String>>(mut self, id: S) -> Self {
self.client.gateway_id = id.into();
self
}
pub fn with_gateway_listener<S: Into<String>>(mut self, gateway_listener: S) -> Self {
self.client.gateway_listener = gateway_listener.into();
self
}
pub fn with_custom_directory<S: Into<String>>(mut self, directory_server: S) -> Self {
self.client.directory_server = directory_server.into();
self
}
pub fn with_socket(mut self, socket_type: SocketType) -> Self {
self.socket.socket_type = socket_type;
self
}
pub fn with_port(mut self, port: u16) -> Self {
self.socket.listening_port = port;
self
}
pub fn set_high_default_traffic_volume(mut self) -> Self {
self.debug.average_packet_delay = 10;
self.debug.loop_cover_traffic_average_delay = 20; // 50 cover messages / s
self.debug.message_sending_average_delay = 5; // 200 "real" messages / s
self
}
// getters
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
}
pub fn get_private_identity_key_file(&self) -> PathBuf {
self.client.private_identity_key_file.clone()
}
pub fn get_public_identity_key_file(&self) -> PathBuf {
self.client.public_identity_key_file.clone()
}
pub fn get_private_encryption_key_file(&self) -> PathBuf {
self.client.private_encryption_key_file.clone()
}
pub fn get_public_encryption_key_file(&self) -> PathBuf {
self.client.public_encryption_key_file.clone()
}
pub fn get_gateway_shared_key_file(&self) -> PathBuf {
self.client.gateway_shared_key_file.clone()
}
pub fn get_reply_encryption_key_store_path(&self) -> PathBuf {
self.client.reply_encryption_key_store_path.clone()
}
pub fn get_ack_key_file(&self) -> PathBuf {
self.client.ack_key_file.clone()
}
pub fn get_directory_server(&self) -> String {
self.client.directory_server.clone()
}
pub fn get_gateway_id(&self) -> String {
self.client.gateway_id.clone()
}
pub fn get_gateway_listener(&self) -> String {
self.client.gateway_listener.clone()
}
pub fn get_socket_type(&self) -> SocketType {
self.socket.socket_type
}
pub fn get_listening_port(&self) -> u16 {
self.socket.listening_port
}
// Debug getters
pub fn get_average_packet_delay(&self) -> time::Duration {
time::Duration::from_millis(self.debug.average_packet_delay)
}
pub fn get_average_ack_delay(&self) -> time::Duration {
time::Duration::from_millis(self.debug.average_ack_delay)
}
pub fn get_ack_wait_multiplier(&self) -> f64 {
self.debug.ack_wait_multiplier
}
pub fn get_ack_wait_addition(&self) -> time::Duration {
time::Duration::from_millis(self.debug.ack_wait_addition)
}
pub fn get_loop_cover_traffic_average_delay(&self) -> time::Duration {
time::Duration::from_millis(self.debug.loop_cover_traffic_average_delay)
}
pub fn get_message_sending_average_delay(&self) -> time::Duration {
time::Duration::from_millis(self.debug.message_sending_average_delay)
}
pub fn get_gateway_response_timeout(&self) -> time::Duration {
time::Duration::from_millis(self.debug.gateway_response_timeout)
}
pub fn get_topology_refresh_rate(&self) -> time::Duration {
time::Duration::from_millis(self.debug.topology_refresh_rate)
}
pub fn get_topology_resolution_timeout(&self) -> time::Duration {
time::Duration::from_millis(self.debug.topology_resolution_timeout)
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Client {
/// ID specifies the human readable ID of this particular client.
id: String,
/// URL to the directory server.
directory_server: String,
/// Path to file containing private identity key.
private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
public_identity_key_file: PathBuf,
/// Path to file containing private encryption key.
private_encryption_key_file: PathBuf,
/// Path to file containing public encryption key.
public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file: PathBuf,
/// Full path to file containing reply encryption keys of all reply-SURBs we have ever
/// sent but not received back.
reply_encryption_key_store_path: PathBuf,
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
gateway_id: String,
/// Address of the gateway listener to which all client requests should be sent.
gateway_listener: String,
/// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf,
}
impl Default for Client {
fn default() -> Self {
// there must be explicit checks for whether id is not empty later
Client {
id: "".to_string(),
directory_server: DEFAULT_DIRECTORY_SERVER.to_string(),
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_encryption_key_file: Default::default(),
public_encryption_key_file: Default::default(),
gateway_shared_key_file: Default::default(),
ack_key_file: Default::default(),
reply_encryption_key_store_path: Default::default(),
gateway_id: "".to_string(),
gateway_listener: "".to_string(),
nym_root_directory: Config::default_root_directory(),
}
}
}
impl Client {
fn default_private_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("private_identity.pem")
}
fn default_public_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("public_identity.pem")
}
fn default_private_encryption_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("private_encryption.pem")
}
fn default_public_encryption_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("public_encryption.pem")
}
fn default_gateway_shared_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("gateway_shared.pem")
}
fn default_ack_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("ack_key.pem")
}
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("reply_key_store")
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socket {
socket_type: SocketType,
listening_port: u16,
}
impl Default for Socket {
fn default() -> Self {
Socket {
socket_type: SocketType::WebSocket,
listening_port: DEFAULT_LISTENING_PORT,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Logging {}
impl Default for Logging {
fn default() -> Self {
Logging {}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Debug {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
/// The provided value is interpreted as milliseconds.
average_packet_delay: u64,
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
/// The provided value is interpreted as milliseconds.
average_ack_delay: u64,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
/// The provided value is interpreted as milliseconds.
ack_wait_addition: u64,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
/// The provided value is interpreted as milliseconds.
loop_cover_traffic_average_delay: u64,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
/// The provided value is interpreted as milliseconds.
message_sending_average_delay: u64,
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
/// The provided value is interpreted as milliseconds.
gateway_response_timeout: u64,
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
/// The provided value is interpreted as milliseconds.
topology_refresh_rate: u64,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
/// The provided value is interpreted as milliseconds.
topology_resolution_timeout: u64,
}
impl Default for Debug {
fn default() -> Self {
Debug {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
}
}
}
#[cfg(test)]
mod client_config {
use super::*;
#[test]
fn after_saving_default_config_the_loaded_one_is_identical() {
// need to figure out how to do something similar but without touching the disk
// or the file system at all...
let temp_location = tempfile::tempdir().unwrap().path().join("config.toml");
let default_config = Config::default().with_id("foomp".to_string());
default_config
.save_to_file(Some(temp_location.clone()))
.unwrap();
let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap();
assert_eq!(default_config, loaded_config);
}
}
@@ -1,76 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::Config;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct ClientKeyPathfinder {
identity_private_key: PathBuf,
identity_public_key: PathBuf,
encryption_private_key: PathBuf,
encryption_public_key: PathBuf,
gateway_shared_key: PathBuf,
ack_key: PathBuf,
}
impl ClientKeyPathfinder {
pub fn new(id: String) -> Self {
let os_config_dir = dirs::config_dir().expect("no config directory known for this OS"); // grabs the OS default config dir
let config_dir = os_config_dir.join("nym").join("clients").join(id);
ClientKeyPathfinder {
identity_private_key: config_dir.join("private_identity.pem"),
identity_public_key: config_dir.join("public_identity.pem"),
encryption_private_key: config_dir.join("public_encryption.pem"),
encryption_public_key: config_dir.join("private_encryption.pem"),
gateway_shared_key: config_dir.join("gateway_shared.pem"),
ack_key: config_dir.join("ack_key.pem"),
}
}
pub fn new_from_config(config: &Config) -> Self {
ClientKeyPathfinder {
identity_private_key: config.get_private_identity_key_file(),
identity_public_key: config.get_public_identity_key_file(),
encryption_private_key: config.get_private_encryption_key_file(),
encryption_public_key: config.get_public_encryption_key_file(),
gateway_shared_key: config.get_gateway_shared_key_file(),
ack_key: config.get_ack_key_file(),
}
}
pub fn private_identity_key(&self) -> &Path {
&self.identity_private_key
}
pub fn public_identity_key(&self) -> &Path {
&self.identity_public_key
}
pub fn private_encryption_key(&self) -> &Path {
&self.encryption_private_key
}
pub fn public_encryption_key(&self) -> &Path {
&self.encryption_public_key
}
pub fn gateway_shared_key(&self) -> &Path {
&self.gateway_shared_key
}
pub fn ack_key(&self) -> &Path {
&self.ack_key
}
}
-102
View File
@@ -1,102 +0,0 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub(crate) fn config_template() -> &'static str {
// While using normal toml marshalling would have been way simpler with less overhead,
// I think it's useful to have comments attached to the saved config file to explain behaviour of
// particular fields.
// Note: any changes to the template must be reflected in the appropriate structs in mod.rs.
r#"
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base client config options #####
[client]
# Human readable ID of this particular client.
id = '{{ client.id }}'
# URL to the directory server.
directory_server = '{{ client.directory_server }}'
# Path to file containing private identity key.
private_identity_key_file = '{{ client.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ client.public_identity_key_file }}'
# Path to file containing private encryption key.
private_encryption_key_file = '{{ client.private_encryption_key_file }}'
# Path to file containing public encryption key.
public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# Full path to file containing reply encryption keys of all reply-SURBs we have ever
# sent but not received back.
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
##### additional client config options #####
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_id }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_listener }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file = '{{ client.ack_key_file }}'
##### advanced configuration options #####
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
##### socket config options #####
[socket]
# allowed values are 'WebSocket' or 'None'
socket_type = '{{ socket.socket_type }}'
# if applicable (for the case of 'WebSocket'), the port on which the client
# will be listening for incoming requests
listening_port = {{ socket.listening_port }}
##### logging configuration options #####
[logging]
# TODO
##### debug configuration options #####
# The following options should not be modified unless you know EXACTLY what you are doing
# as if set incorrectly, they may impact your anonymity.
[debug]
average_packet_delay = {{ debug.average_packet_delay }}
average_ack_delay = {{ debug.average_ack_delay }}
loop_cover_traffic_average_delay = {{ debug.loop_cover_traffic_average_delay }}
message_sending_average_delay = {{ debug.message_sending_average_delay }}
"#
}
-1
View File
@@ -13,5 +13,4 @@
// limitations under the License.
pub mod built_info;
pub mod client;
pub mod config;
pub mod socks;
-1
View File
@@ -17,7 +17,6 @@ use clap::{App, ArgMatches};
pub mod built_info;
pub mod client;
mod commands;
pub mod config;
pub mod socks;
fn main() {
+2 -2
View File
@@ -9,8 +9,8 @@ use tokio::{self, net::TcpStream};
use nymsphinx::addressing::clients::Recipient;
use crate::client::inbound_messages::InputMessage;
use crate::client::inbound_messages::InputMessageSender;
use client_core::client::inbound_messages::InputMessage;
use client_core::client::inbound_messages::InputMessageSender;
use futures::{channel::oneshot, lock::Mutex};
use super::authentication::{AuthenticationMethods, Authenticator, User};
+2 -2
View File
@@ -1,6 +1,6 @@
use super::client::ActiveStreams;
use crate::client::received_buffer::ReconstructedMessagesReceiver;
use crate::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
+1 -1
View File
@@ -4,7 +4,7 @@ use super::{
mixnet_responses::MixnetResponseListener,
types::{ResponseCode, SocksProxyError},
};
use crate::client::{
use client_core::client::{
inbound_messages::InputMessageSender, received_buffer::ReceivedBufferRequestSender,
};
use futures::lock::Mutex;