Merge branch 'develop' into feature/validator_health_checker

This commit is contained in:
Jedrzej Stuczynski
2020-01-10 14:38:59 +00:00
40 changed files with 1422 additions and 1113 deletions
-4
View File
@@ -7,7 +7,3 @@ jobs:
allow_failures:
- rust: nightly
fast_finish: true
before_script:
- git clone --depth=50 --branch=develop https://github.com/nymtech/sphinx.git /home/travis/build/nymtech/sphinx
- ls
Generated
+957 -903
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -6,6 +6,9 @@ members = [
"clients/mix-client",
"clients/provider-client",
"clients/validator-client",
"common/addressing",
"common/crypto",
"common/topology",
"mixnode",
"sfw-provider",
"sfw-provider/sfw-provider-requests",
+6 -6
View File
@@ -14,7 +14,7 @@ The platform is composed of multiple Rust crates. Top-level crates include:
#### Prerequisites
* Rust 1.39 or later. Stable works.
* [Rust](https://www.rust-lang.org/tools/install) 1.39 or later. Stable works.
* The `nym` platform repo (this one).
* Checkout the [Sphinx](https://github.com/nymtech/sphinx) repo beside the `nym` repo.
@@ -23,17 +23,17 @@ Your directory structure should look like this:
```
$ tree -L 1
├── nym
│   ├── client
│   ├── mixnode
│   ├── README.md
│   └── sfw-provider
├── sphinx
```
`cargo build` will build the software.
Change directory in `nym` and then `cargo build` will build the software.
As with any other Rust project, there are other ways to build:
* `cargo build --release` will build an optimized release version for use in production
* `cargo test` will run unit and integration tests for the crate (once)
* `cargo watch -x test` will run tests whenever you change a file in the crate. Very handy in development.
Binaries can be found at `target/debug/` if you've done `cargo build` (without specifying `--release`).
Production binaries can be found at `target/release/` if you've done `cargo build --release`.
+2 -1
View File
@@ -7,7 +7,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.8"
tokio = { version = "0.2", features = ["full"] }
## will be moved to proper dependencies once released
sphinx = { path = "../../../sphinx" }
sphinx = { git = "https://github.com/nymtech/sphinx", rev="50ba6c4bbeb9be59257f32ff1e8f734e4fd82633" }
+2 -1
View File
@@ -1,3 +1,4 @@
use log::*;
use sphinx::SphinxPacket;
use std::net::SocketAddr;
use tokio::prelude::*;
@@ -17,7 +18,7 @@ impl MixClient {
) -> Result<(), Box<dyn std::error::Error>> {
let bytes = packet.to_bytes();
println!("socket addr: {:?}", mix_addr);
info!("socket addr: {:?}", mix_addr);
let mut stream = tokio::net::TcpStream::connect(mix_addr).await?;
stream.write_all(&bytes[..]).await?;
+4 -1
View File
@@ -16,8 +16,10 @@ base64 = "0.11.0"
clap = "2.33.0"
curve25519-dalek = "1.2.3"
dirs = "2.0.2"
env_logger = "0.7.1"
futures = "0.3.1"
hex = "0.4.0"
log = "0.4.8"
pem = "0.7.0"
rand = "0.7.2"
rand_distr = "0.2.2"
@@ -29,6 +31,7 @@ tungstenite = "0.9.2"
## internal
addressing = {path = "../../common/addressing" }
crypto = {path = "../../common/crypto"}
directory-client = { path = "../directory-client" }
mix-client = { path = "../mix-client" }
provider-client = { path = "../provider-client" }
@@ -36,7 +39,7 @@ sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" }
topology = {path = "../../common/topology" }
## will be moved to proper dependencies once released
sphinx = { path = "../../../sphinx" }
sphinx = { git = "https://github.com/nymtech/sphinx", rev="50ba6c4bbeb9be59257f32ff1e8f734e4fd82633" }
# putting this explicitly below everything and most likely, the next time we look into it, it will already have a proper release
tokio-tungstenite = { git = "https://github.com/dbcfd/tokio-tungstenite", rev="6dc2018cbfe8fe7ddd75ff977343086503135b38" }
+18 -12
View File
@@ -7,9 +7,12 @@ use futures::join;
use futures::lock::Mutex as FMutex;
use futures::select;
use futures::{SinkExt, StreamExt};
use log::*;
use sfw_provider_requests::AuthToken;
use sphinx::route::{Destination, DestinationAddressBytes};
use sphinx::SphinxPacket;
use std::io;
use std::io::prelude::*;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
@@ -39,14 +42,17 @@ impl MixTrafficController {
async fn run(mut rx: mpsc::UnboundedReceiver<MixMessage>) {
let mix_client = mix_client::MixClient::new();
while let Some(mix_message) = rx.next().await {
println!(
info!(
"[MIX TRAFFIC CONTROL] - got a mix_message for {:?}",
mix_message.0
);
let send_res = mix_client.send(mix_message.1, mix_message.0).await;
match send_res {
Ok(_) => println!("We successfully sent the message!"),
Err(e) => eprintln!("We failed to send the message :( - {:?}", e),
Ok(_) => {
print!(".");
io::stdout().flush().ok().expect("Could not flush stdout");
}
Err(e) => error!("We failed to send the message :( - {:?}", e),
};
}
}
@@ -70,7 +76,7 @@ impl ReceivedMessagesBuffer {
}
async fn add_new_messages(buf: Arc<FMutex<Self>>, msgs: Vec<Vec<u8>>) {
println!("Adding new messages to the buffer! {:?}", msgs);
info!("Adding new messages to the buffer! {:?}", msgs);
let mut unlocked = buf.lock().await;
unlocked.messages.extend(msgs);
}
@@ -150,7 +156,7 @@ impl NymClient {
topology: Topology,
) {
loop {
println!("[LOOP COVER TRAFFIC STREAM] - next cover message!");
info!("[LOOP COVER TRAFFIC STREAM] - next cover message!");
let delay = utils::poisson::sample(LOOP_COVER_AVERAGE_DELAY);
let delay_duration = Duration::from_secs_f64(delay);
tokio::time::delay_for(delay_duration).await;
@@ -169,13 +175,13 @@ impl NymClient {
topology: Topology,
) {
loop {
println!("[OUT QUEUE] here I will be sending real traffic (or loop cover if nothing is available)");
info!("[OUT QUEUE] here I will be sending real traffic (or loop cover if nothing is available)");
// TODO: consider replacing select macro with our own proper future definition with polling
let traffic_message = select! {
real_message = input_rx.next() => {
println!("[OUT QUEUE] - we got a real message!");
info!("[OUT QUEUE] - we got a real message!");
if real_message.is_none() {
eprintln!("Unexpected 'None' real message!");
error!("Unexpected 'None' real message!");
std::process::exit(1);
}
let real_message = real_message.unwrap();
@@ -184,7 +190,7 @@ impl NymClient {
},
default => {
println!("[OUT QUEUE] - no real message - going to send extra loop cover");
info!("[OUT QUEUE] - no real message - going to send extra loop cover");
utils::sphinx::loop_cover_message(our_info.address, our_info.identifier, &topology)
}
};
@@ -208,7 +214,7 @@ impl NymClient {
loop {
let delay_duration = Duration::from_secs_f64(FETCH_MESSAGES_DELAY);
tokio::time::delay_for(delay_duration).await;
println!("[FETCH MSG] - Polling provider...");
info!("[FETCH MSG] - Polling provider...");
let messages = provider_client.retrieve_messages().await.unwrap();
let good_messages = messages
.into_iter()
@@ -228,7 +234,7 @@ impl NymClient {
let provider_client_listener_address: SocketAddr = topology
.get_mix_provider_nodes()
.first()
.unwrap()
.expect("Could not get a provider from the supplied network topology, are you using the right directory server?")
.client_listener;
let mut provider_client = provider_client::ProviderClient::new(
@@ -243,7 +249,7 @@ impl NymClient {
None => {
let auth_token = provider_client.register().await.unwrap();
provider_client.update_token(auth_token);
println!("Obtained new token! - {:?}", auth_token);
info!("Obtained new token! - {:?}", auth_token);
}
Some(token) => println!("Already got the token! - {:?}", token),
}
+3 -3
View File
@@ -1,8 +1,8 @@
use crate::banner;
use crate::identity::mixnet;
use crate::persistence::pathfinder::Pathfinder;
use crate::persistence::pemstore::PemStore;
use clap::ArgMatches;
use crypto::identity::MixnetIdentityKeyPair;
pub fn execute(matches: &ArgMatches) {
println!("{}", banner());
@@ -12,9 +12,9 @@ pub fn execute(matches: &ArgMatches) {
let pathfinder = Pathfinder::new(id);
println!("Writing keypairs to {:?}...", pathfinder.config_dir);
let mix_keys = mixnet::KeyPair::new();
let mix_keys = crypto::identity::DummyMixIdentityKeyPair::new();
let pem_store = PemStore::new(pathfinder);
pem_store.write(mix_keys);
pem_store.write_identity(mix_keys);
println!("Client configuration completed.\n\n\n")
}
-1
View File
@@ -1,4 +1,3 @@
pub mod init;
pub mod run;
pub mod tcpsocket;
pub mod websocket;
-17
View File
@@ -1,17 +0,0 @@
use crate::banner;
//use crate::clients::NymClient;
//use crate::persistence::pemstore;
use clap::ArgMatches;
pub fn execute(_matches: &ArgMatches) {
println!("{}", banner());
panic!("For time being this command is deprecated! Please use 'websocket' instead");
//
// let is_local = matches.is_present("local");
// let id = matches.value_of("id").unwrap().to_string();
// println!("Starting client...");
//
// let keypair = pemstore::read_keypair_from_disk(id);
// let client = NymClient::new(keypair.public_bytes(), is_local);
// client.start("127.0.0.1:9000".parse().unwrap()).unwrap();
}
+8 -3
View File
@@ -3,6 +3,7 @@ use crate::clients::{NymClient, SocketType};
use crate::persistence::pemstore;
use clap::ArgMatches;
use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey};
use std::net::ToSocketAddrs;
pub fn execute(matches: &ArgMatches) {
@@ -28,11 +29,15 @@ pub fn execute(matches: &ArgMatches) {
.next()
.expect("Failed to extract the socket address from the iterator");
let keypair = pemstore::read_keypair_from_disk(id);
let auth_token = None;
let keypair = pemstore::read_mix_identity_keypair_from_disk(id);
// TODO: reading auth_token from disk (if exists);
let mut temporary_address = [0u8; 32];
let public_key_bytes = keypair.public_key().to_bytes();
temporary_address.copy_from_slice(&public_key_bytes[..]);
let auth_token = None;
let client = NymClient::new(
keypair.public_bytes(),
temporary_address,
socket_address.clone(),
directory_server,
auth_token,
+7 -2
View File
@@ -3,6 +3,7 @@ use crate::clients::{NymClient, SocketType};
use crate::persistence::pemstore;
use clap::ArgMatches;
use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey};
use std::net::ToSocketAddrs;
pub fn execute(matches: &ArgMatches) {
@@ -28,11 +29,15 @@ pub fn execute(matches: &ArgMatches) {
.next()
.expect("Failed to extract the socket address from the iterator");
let keypair = pemstore::read_keypair_from_disk(id);
let keypair = pemstore::read_mix_identity_keypair_from_disk(id);
// TODO: reading auth_token from disk (if exists);
let mut temporary_address = [0u8; 32];
let public_key_bytes = keypair.public_key().to_bytes();
temporary_address.copy_from_slice(&public_key_bytes[..]);
let auth_token = None;
let client = NymClient::new(
keypair.public_bytes(),
temporary_address,
socket_address,
directory_server,
auth_token,
-33
View File
@@ -1,33 +0,0 @@
use curve25519_dalek::montgomery::MontgomeryPoint;
use curve25519_dalek::scalar::Scalar;
// This keypair serves as the user's identity within the Mixnet
pub struct KeyPair {
pub private: Scalar,
pub public: MontgomeryPoint,
}
impl KeyPair {
pub fn new() -> KeyPair {
let (private, public) = sphinx::crypto::keygen();
KeyPair { private, public }
}
pub fn from_bytes(private_bytes: Vec<u8>, public_bytes: Vec<u8>) -> KeyPair {
let mut bytes = [0; 32];
bytes.copy_from_slice(&private_bytes[..]);
let private = Scalar::from_canonical_bytes(bytes).unwrap();
let mut bytes = [0; 32];
bytes.copy_from_slice(&public_bytes[..]);
let public = MontgomeryPoint(bytes);
KeyPair { private, public }
}
pub fn private_bytes(&self) -> [u8; 32] {
self.private.to_bytes()
}
pub fn public_bytes(&self) -> [u8; 32] {
self.public.to_bytes()
}
}
-1
View File
@@ -1 +0,0 @@
pub mod mixnet;
@@ -1 +0,0 @@
// TODO types for Validator keys will go in here once we hook this up.
-1
View File
@@ -1,7 +1,6 @@
#![recursion_limit = "256"]
pub mod clients;
pub mod identity;
pub mod persistence;
pub mod sockets;
pub mod utils;
+5 -19
View File
@@ -1,16 +1,19 @@
#![recursion_limit = "256"]
use clap::{App, Arg, ArgMatches, SubCommand};
use env_logger;
use log::*;
use std::process;
pub mod clients;
mod commands;
mod identity;
mod persistence;
mod sockets;
pub mod utils;
fn main() {
env_logger::init();
let arg_matches = App::new("Nym Client")
.version(built_info::PKG_VERSION)
.author("Nymtech")
@@ -30,22 +33,6 @@ fn main() {
.takes_value(true)
)
)
.subcommand(
SubCommand::with_name("run")
.about("Run a persistent Nym client process")
.arg(Arg::with_name("id")
.long("id")
.help("Id of the nym-mixnet-client we want to run.")
.takes_value(true)
.required(true)
)
.arg(
Arg::with_name("directory")
.long("directory")
.help("Address of the directory server the client is getting topology from")
.takes_value(true),
)
)
.subcommand(
SubCommand::with_name("tcpsocket")
.about("Run Nym client that listens for bytes on a TCP socket")
@@ -96,7 +83,7 @@ fn main() {
.get_matches();
if let Err(e) = execute(arg_matches) {
println!("{}", e);
error!("{}", e);
process::exit(1);
}
}
@@ -109,7 +96,6 @@ pub mod built_info {
fn execute(matches: ArgMatches) -> Result<(), String> {
match matches.subcommand() {
("init", Some(m)) => Ok(commands::init::execute(m)),
("run", Some(m)) => Ok(commands::run::execute(m)),
("tcpsocket", Some(m)) => Ok(commands::tcpsocket::execute(m)),
("websocket", Some(m)) => Ok(commands::websocket::execute(m)),
_ => Err(usage()),
+44 -19
View File
@@ -1,17 +1,22 @@
use crate::identity::mixnet::KeyPair;
use crate::persistence::pathfinder::Pathfinder;
use pem::{encode, parse, Pem};
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
pub fn read_keypair_from_disk(id: String) -> KeyPair {
pub fn read_mix_identity_keypair_from_disk(
id: String,
) -> crypto::identity::DummyMixIdentityKeyPair {
let pathfinder = Pathfinder::new(id);
let pem_store = PemStore::new(pathfinder);
let keypair = pem_store.read();
let keypair = pem_store.read_identity();
keypair
}
pub fn read_mix_encryption_keypair_from_disk(id: String) -> crypto::encryption::x25519::KeyPair {
unimplemented!()
}
pub struct PemStore {
config_dir: PathBuf,
private_mix_key: PathBuf,
@@ -27,42 +32,62 @@ impl PemStore {
}
}
pub fn read(&self) -> KeyPair {
let private = self.read_file(self.private_mix_key.clone());
let public = self.read_file(self.public_mix_key.clone());
pub fn read_identity<IDPair, Priv, Pub>(&self) -> IDPair
where
IDPair: crypto::identity::MixnetIdentityKeyPair<Priv, Pub>,
Priv: crypto::identity::MixnetIdentityPrivateKey,
Pub: crypto::identity::MixnetIdentityPublicKey,
{
let private_pem = self.read_pem_file(self.private_mix_key.clone());
let public_pem = self.read_pem_file(self.public_mix_key.clone());
KeyPair::from_bytes(private, public)
let key_pair = IDPair::from_bytes(&private_pem.contents, &public_pem.contents);
assert_eq!(key_pair.private_key().pem_type(), private_pem.tag);
assert_eq!(key_pair.public_key().pem_type(), public_pem.tag);
key_pair
}
fn read_file(&self, filepath: PathBuf) -> Vec<u8> {
let mut pem_bytes = File::open(filepath).unwrap();
fn read_pem_file(&self, filepath: PathBuf) -> Pem {
let mut pem_bytes = File::open(filepath).expect("Could not open stored keys from disk.");
let mut buf = Vec::new();
pem_bytes.read_to_end(&mut buf).unwrap();
let pem = parse(&buf).unwrap();
pem.contents
pem_bytes
.read_to_end(&mut buf)
.expect("PEM bytes reading failed.");
let pem = parse(&buf).expect("PEM parsing failed while reading keys");
pem
}
// This should be refactored and made more generic for when we have other kinds of
// KeyPairs that we want to persist (e.g. validator keypairs, or keys for
// signing vs encryption). However, for the moment, it does the job.
pub fn write(&self, key_pair: KeyPair) {
pub fn write_identity<IDPair, Priv, Pub>(&self, key_pair: IDPair)
where
IDPair: crypto::identity::MixnetIdentityKeyPair<Priv, Pub>,
Priv: crypto::identity::MixnetIdentityPrivateKey,
Pub: crypto::identity::MixnetIdentityPublicKey,
{
std::fs::create_dir_all(self.config_dir.clone()).unwrap();
let private_key = key_pair.private_key();
let public_key = key_pair.public_key();
self.write_pem_file(
self.private_mix_key.clone(),
key_pair.private_bytes(),
String::from("SPHINX CURVE25519 PRIVATE KEY"),
private_key.to_bytes(),
private_key.pem_type(),
);
self.write_pem_file(
self.public_mix_key.clone(),
key_pair.public_bytes(),
String::from("SPHINX CURVE25519 PUBLIC KEY"),
public_key.to_bytes(),
public_key.pem_type(),
);
}
fn write_pem_file(&self, filepath: PathBuf, data: [u8; 32], tag: String) {
fn write_pem_file(&self, filepath: PathBuf, data: Vec<u8>, tag: String) {
let pem = Pem {
tag,
contents: data.to_vec(),
contents: data,
};
let key = encode(&pem);
+1 -1
View File
@@ -34,7 +34,7 @@ pub fn encapsulate_message<T: NymTopology>(
let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays).unwrap();
let first_node_address =
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address);
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address.to_bytes());
(first_node_address, packet)
}
+2 -1
View File
@@ -8,10 +8,11 @@ edition = "2018"
[dependencies]
futures = "0.3.1"
log = "0.4.8"
tokio = { version = "0.2", features = ["full"] }
## internal
sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" }
## will be moved to proper dependencies once released
sphinx = { path = "../../../sphinx" }
sphinx = { git = "https://github.com/nymtech/sphinx", rev="50ba6c4bbeb9be59257f32ff1e8f734e4fd82633" }
+3 -2
View File
@@ -1,4 +1,5 @@
use futures::io::Error;
use log::info;
use sfw_provider_requests::requests::{ProviderRequest, PullRequest, RegisterRequest};
use sfw_provider_requests::responses::{
ProviderResponse, ProviderResponseError, PullResponse, RegisterResponse,
@@ -64,7 +65,7 @@ impl ProviderClient {
pub async fn send_request(&self, bytes: Vec<u8>) -> Result<Vec<u8>, ProviderClientError> {
let mut socket = tokio::net::TcpStream::connect(self.provider_network_address).await?;
println!("keep alive: {:?}", socket.keepalive());
info!("keep alive: {:?}", socket.keepalive());
socket.set_keepalive(Some(Duration::from_secs(2))).unwrap();
socket.write_all(&bytes[..]).await?;
if let Err(_e) = socket.shutdown(Shutdown::Write) {
@@ -91,7 +92,7 @@ impl ProviderClient {
let bytes = pull_request.to_bytes();
let response = self.send_request(bytes).await?;
println!("Received the following response: {:?}", response);
info!("Received the following response: {:?}", response);
let parsed_response = PullResponse::from_bytes(&response)?;
Ok(parsed_response.messages)
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "addressing"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <jedrzej.stuczynski@gmail.com>"]
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "crypto"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
curve25519-dalek = "1.2.3"
rand = "0.7.2"
rand_os = "0.1"
+39
View File
@@ -0,0 +1,39 @@
use crate::PemStorable;
pub mod x25519;
pub trait MixnetEncryptionKeyPair<Priv, Pub>
where
Priv: MixnetEncryptionPrivateKey,
Pub: MixnetEncryptionPublicKey,
{
fn new() -> Self;
fn private_key(&self) -> &Priv;
fn public_key(&self) -> &Pub;
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self;
// TODO: encryption related methods
}
pub trait MixnetEncryptionPublicKey:
Sized + PemStorable + for<'a> From<&'a <Self as MixnetEncryptionPublicKey>::PrivateKeyMaterial>
{
// we need to couple public and private keys together
type PrivateKeyMaterial: MixnetEncryptionPrivateKey<PublicKeyMaterial = Self>;
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
pub trait MixnetEncryptionPrivateKey: Sized + PemStorable {
// we need to couple public and private keys together
type PublicKeyMaterial: MixnetEncryptionPublicKey<PrivateKeyMaterial = Self>;
/// Returns the associated public key
fn public_key(&self) -> Self::PublicKeyMaterial {
self.into()
}
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
+98
View File
@@ -0,0 +1,98 @@
use crate::encryption::{
MixnetEncryptionKeyPair, MixnetEncryptionPrivateKey, MixnetEncryptionPublicKey,
};
use crate::PemStorable;
use curve25519_dalek::montgomery::MontgomeryPoint;
use curve25519_dalek::scalar::Scalar;
// TODO: ensure this is a proper name for this considering we are not implementing entire DH here
const CURVE_GENERATOR: MontgomeryPoint = curve25519_dalek::constants::X25519_BASEPOINT;
pub struct KeyPair {
pub(crate) private_key: PrivateKey,
pub(crate) public_key: PublicKey,
}
impl MixnetEncryptionKeyPair<PrivateKey, PublicKey> for KeyPair {
fn new() -> Self {
let mut rng = rand_os::OsRng::new().unwrap();
let private_key_value = Scalar::random(&mut rng);
let public_key_value = CURVE_GENERATOR * private_key_value;
KeyPair {
private_key: PrivateKey(private_key_value),
public_key: PublicKey(public_key_value),
}
}
fn private_key(&self) -> &PrivateKey {
&self.private_key
}
fn public_key(&self) -> &PublicKey {
&self.public_key
}
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self {
KeyPair {
private_key: PrivateKey::from_bytes(priv_bytes),
public_key: PublicKey::from_bytes(pub_bytes),
}
}
}
// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct PrivateKey(pub Scalar);
impl MixnetEncryptionPrivateKey for PrivateKey {
type PublicKeyMaterial = PublicKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
fn from_bytes(b: &[u8]) -> Self {
let mut bytes = [0; 32];
bytes.copy_from_slice(&b[..]);
let key = Scalar::from_canonical_bytes(bytes).unwrap();
Self(key)
}
}
impl PemStorable for PrivateKey {
fn pem_type(&self) -> String {
String::from("X25519 PRIVATE KEY")
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PublicKey(pub MontgomeryPoint);
impl<'a> From<&'a PrivateKey> for PublicKey {
fn from(pk: &'a PrivateKey) -> Self {
PublicKey(CURVE_GENERATOR * pk.0)
}
}
impl MixnetEncryptionPublicKey for PublicKey {
type PrivateKeyMaterial = PrivateKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
fn from_bytes(b: &[u8]) -> Self {
let mut bytes = [0; 32];
bytes.copy_from_slice(&b[..]);
let key = MontgomeryPoint(bytes);
Self(key)
}
}
impl PemStorable for PublicKey {
fn pem_type(&self) -> String {
String::from("X25519 PUBLIC KEY")
}
}
+147
View File
@@ -0,0 +1,147 @@
use crate::encryption::{
MixnetEncryptionKeyPair, MixnetEncryptionPrivateKey, MixnetEncryptionPublicKey,
};
use crate::{encryption, PemStorable};
use curve25519_dalek::scalar::Scalar;
pub trait MixnetIdentityKeyPair<Priv, Pub>
where
Priv: MixnetIdentityPrivateKey,
Pub: MixnetIdentityPublicKey,
{
fn new() -> Self;
fn private_key(&self) -> &Priv;
fn public_key(&self) -> &Pub;
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self;
// TODO: signing related methods
}
pub trait MixnetIdentityPublicKey:
Sized + PemStorable + for<'a> From<&'a <Self as MixnetIdentityPublicKey>::PrivateKeyMaterial>
{
// we need to couple public and private keys together
type PrivateKeyMaterial: MixnetIdentityPrivateKey<PublicKeyMaterial = Self>;
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
pub trait MixnetIdentityPrivateKey: Sized + PemStorable {
// we need to couple public and private keys together
type PublicKeyMaterial: MixnetIdentityPublicKey<PrivateKeyMaterial = Self>;
/// Returns the associated public key
fn public_key(&self) -> Self::PublicKeyMaterial {
self.into()
}
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(b: &[u8]) -> Self;
}
// same for validator
// for time being define a dummy identity using x25519 encryption keys (as we've done so far)
// and replace it with proper keys, like ed25519 later on
pub struct DummyMixIdentityKeyPair {
pub private_key: DummyMixIdentityPrivateKey,
pub public_key: DummyMixIdentityPublicKey,
}
impl MixnetIdentityKeyPair<DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey>
for DummyMixIdentityKeyPair
{
fn new() -> Self {
let keypair = encryption::x25519::KeyPair::new();
DummyMixIdentityKeyPair {
private_key: DummyMixIdentityPrivateKey(keypair.private_key),
public_key: DummyMixIdentityPublicKey(keypair.public_key),
}
}
fn private_key(&self) -> &DummyMixIdentityPrivateKey {
&self.private_key
}
fn public_key(&self) -> &DummyMixIdentityPublicKey {
&self.public_key
}
fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self {
DummyMixIdentityKeyPair {
private_key: DummyMixIdentityPrivateKey::from_bytes(priv_bytes),
public_key: DummyMixIdentityPublicKey::from_bytes(pub_bytes),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DummyMixIdentityPublicKey(encryption::x25519::PublicKey);
impl MixnetIdentityPublicKey for DummyMixIdentityPublicKey {
type PrivateKeyMaterial = DummyMixIdentityPrivateKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
fn from_bytes(b: &[u8]) -> Self {
Self(encryption::x25519::PublicKey::from_bytes(b))
}
}
impl PemStorable for DummyMixIdentityPublicKey {
fn pem_type(&self) -> String {
format!("DUMMY KEY BASED ON {}", self.0.pem_type())
}
}
impl DummyMixIdentityPublicKey {
pub fn to_b64_string(&self) -> String {
base64::encode_config(&self.to_bytes(), base64::URL_SAFE)
}
fn from_b64_string(val: String) -> Self {
Self::from_bytes(&base64::decode_config(&val, base64::URL_SAFE).unwrap())
}
}
// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct DummyMixIdentityPrivateKey(pub encryption::x25519::PrivateKey);
impl<'a> From<&'a DummyMixIdentityPrivateKey> for DummyMixIdentityPublicKey {
fn from(pk: &'a DummyMixIdentityPrivateKey) -> Self {
let private_ref = &pk.0;
let public: encryption::x25519::PublicKey = private_ref.into();
DummyMixIdentityPublicKey(public)
}
}
impl MixnetIdentityPrivateKey for DummyMixIdentityPrivateKey {
type PublicKeyMaterial = DummyMixIdentityPublicKey;
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
fn from_bytes(b: &[u8]) -> Self {
Self(encryption::x25519::PrivateKey::from_bytes(b))
}
}
// TODO: this will be implemented differently by using the proper trait
impl DummyMixIdentityPrivateKey {
pub fn as_scalar(self) -> Scalar {
let encryption_key = self.0;
encryption_key.0
}
}
impl PemStorable for DummyMixIdentityPrivateKey {
fn pem_type(&self) -> String {
format!("DUMMY KEY BASED ON {}", self.0.pem_type())
}
}
+9
View File
@@ -0,0 +1,9 @@
pub mod encryption;
pub mod identity;
// TODO: this trait will need to be moved elsewhere, probably to some 'persistence' crate
// but since it will need to be used by all identities, it's not really appropriate if it lived in nym-client
pub trait PemStorable {
fn pem_type(&self) -> String;
}
+2 -2
View File
@@ -1,7 +1,7 @@
[package]
name = "topology"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net"]
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -18,4 +18,4 @@ serde = { version = "1.0.104", features = ["derive"] }
addressing = {path = "../addressing"}
## will be moved to proper dependencies once released
sphinx = { path = "../../../sphinx" }
sphinx = { git = "https://github.com/nymtech/sphinx", rev="50ba6c4bbeb9be59257f32ff1e8f734e4fd82633" }
+3 -3
View File
@@ -2,7 +2,7 @@ use addressing;
use curve25519_dalek::montgomery::MontgomeryPoint;
use itertools::Itertools;
use rand::seq::IteratorRandom;
use sphinx::route::Node as SphinxNode;
use sphinx::route::{Node as SphinxNode, NodeAddressBytes};
use std::cmp::max;
use std::collections::HashMap;
use std::net::SocketAddr;
@@ -24,7 +24,7 @@ impl Into<SphinxNode> for MixNode {
key_bytes.copy_from_slice(&decoded_key_bytes[..]);
let key = MontgomeryPoint(key_bytes);
SphinxNode::new(address_bytes, key)
SphinxNode::new(NodeAddressBytes::from_bytes(address_bytes), key)
}
}
@@ -51,7 +51,7 @@ impl Into<SphinxNode> for MixProviderNode {
key_bytes.copy_from_slice(&decoded_key_bytes[..]);
let key = MontgomeryPoint(key_bytes);
SphinxNode::new(address_bytes, key)
SphinxNode::new(NodeAddressBytes::from_bytes(address_bytes), key)
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ addressing = {path = "../common/addressing" }
directory-client = { path = "../clients/directory-client" }
## will be moved to proper dependencies once released
sphinx = { path = "../../sphinx" }
sphinx = { git = "https://github.com/nymtech/sphinx", rev="50ba6c4bbeb9be59257f32ff1e8f734e4fd82633" }
[build-dependencies]
built = "0.3.2"
+1 -17
View File
@@ -2,26 +2,10 @@
A Rust mixnode implementation.
## Building
* check out the code
* [install rust](https://www.rust-lang.org/tools/install) (stable)
* `cargo build --release` (for a production build)
The built binary can be found at `target/release/nym-mixnode`
## Usage
* `nym-mixnode` prints a help message showing usage options
* `nym-mixnode run --help` prints a help message showing usage options for the run command
* `nym-mixnode run --layer 1` will start the mixnode in layer 1 (coordinate with other people to find out which layer you need to start yours in)
* `nym-mixnode run --layer 1 --host x.x.x.x` will start the mixnode in layer 1 and bind to the specified host IP address. Coordinate with other people in your network to find out which layer needs coverage.
By default, the Nym Mixnode will start on port 1789. If desired, you can change the port using the `--port` option.
### Dependencies
It's important to download the following repositories before building the mixnode:
* https://github.com/nymtech/nym-client
* https://github.com/nymtech/nym-sfw-provider
* https://github.com/nymtech/sphinx
+3 -2
View File
@@ -1,4 +1,5 @@
use addressing;
use sphinx::route::NodeAddressBytes;
use std::error::Error;
use std::net::SocketAddr;
use tokio::prelude::*;
@@ -10,9 +11,9 @@ pub struct MixPeer {
impl MixPeer {
// note that very soon `next_hop_address` will be changed to `next_hop_metadata`
pub fn new(next_hop_address: [u8; 32]) -> MixPeer {
pub fn new(next_hop_address: NodeAddressBytes) -> MixPeer {
let next_hop_socket_address =
addressing::socket_address_from_encoded_bytes(next_hop_address);
addressing::socket_address_from_encoded_bytes(next_hop_address.to_bytes());
MixPeer {
connection: next_hop_socket_address,
}
+2 -2
View File
@@ -21,12 +21,12 @@ serde_json = "1.0.44"
hmac = "0.7.1"
## internal
crypto = {path = "../common/crypto"}
directory-client = { path = "../clients/directory-client" }
nym-client = { path = "../clients/nym-client" } # TODO: this is only due to dependecy on 'KeyPair'. Perhaps identity should be moved into own crate?
sfw-provider-requests = { path = "./sfw-provider-requests" }
## will be moved to proper dependencies once released
sphinx = { path = "../../sphinx" }
sphinx = { git = "https://github.com/nymtech/sphinx", rev="50ba6c4bbeb9be59257f32ff1e8f734e4fd82633" }
[build-dependencies]
built = "0.3.2"
@@ -7,4 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sphinx = { path = "../../../sphinx" }
sphinx = { git = "https://github.com/nymtech/sphinx", rev="50ba6c4bbeb9be59257f32ff1e8f734e4fd82633" }
+10 -22
View File
@@ -1,6 +1,6 @@
use crate::provider::ServiceProvider;
use clap::{App, Arg, ArgMatches, SubCommand};
use nym_client::identity::mixnet::KeyPair;
use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey};
use std::net::ToSocketAddrs;
use std::path::PathBuf;
use std::process;
@@ -71,15 +71,14 @@ fn main() {
}
fn run(matches: &ArgMatches) {
println!("{}", banner());
let config = new_config(matches);
let provider = ServiceProvider::new(&config);
let provider = ServiceProvider::new(config);
provider.start().unwrap()
}
fn new_config(matches: &ArgMatches) -> provider::Config {
println!("Running the service provider!");
let directory_server = matches
.value_of("directory")
.unwrap_or("https://directory.nymtech.net")
@@ -101,7 +100,7 @@ fn new_config(matches: &ArgMatches) -> provider::Config {
Err(err) => panic!("Invalid client port value provided - {:?}", err),
};
let key_pair = KeyPair::new(); // TODO: persist this so keypairs don't change every restart
let key_pair = crypto::identity::DummyMixIdentityKeyPair::new(); // TODO: persist this so keypairs don't change every restart
let store_dir = PathBuf::from(
matches
.value_of("storeDir")
@@ -113,14 +112,9 @@ fn new_config(matches: &ArgMatches) -> provider::Config {
.unwrap_or("/tmp/nym-provider/registered_clients"),
);
println!("The value of mix_host is: {:?}", mix_host);
println!("The value of mix_port is: {:?}", mix_port);
println!("The value of client_host is: {:?}", client_host);
println!("The value of client_port is: {:?}", client_port);
println!("The value of key is: {:?}", key_pair.private.clone());
println!("The value of store_dir is: {:?}", store_dir);
println!("store_dir is: {:?}", store_dir);
println!(
"The value of registered_client_ledger_dir is: {:?}",
"registered_client_ledger_dir is: {:?}",
registered_client_ledger_dir
);
@@ -136,21 +130,15 @@ fn new_config(matches: &ArgMatches) -> provider::Config {
.next()
.expect("Failed to extract the socket address from the iterator");
println!(
"The full combined mix socket address is {}",
mix_socket_address
);
println!(
"The full combined client socket address is {}",
client_socket_address
);
println!("Listening for mixnet packets on {}", mix_socket_address);
println!("Listening for client requests on {}", client_socket_address);
provider::Config {
mix_socket_address,
directory_server,
public_key: key_pair.public,
public_key: key_pair.public_key,
client_socket_address,
secret_key: key_pair.private,
secret_key: key_pair.private_key,
store_dir: PathBuf::from(store_dir),
}
}
@@ -1,5 +1,6 @@
use crate::provider::storage::{ClientStorage, StoreError};
use crate::provider::ClientLedger;
use crypto::identity::{DummyMixIdentityPrivateKey, MixnetIdentityPrivateKey};
use curve25519_dalek::scalar::Scalar;
use futures::lock::Mutex as FMutex;
use hmac::{Hmac, Mac};
@@ -53,14 +54,14 @@ impl From<io::Error> for ClientProcessingError {
pub(crate) struct ClientProcessingData {
store_dir: PathBuf,
registered_clients_ledger: Arc<FMutex<ClientLedger>>,
secret_key: Scalar,
secret_key: DummyMixIdentityPrivateKey,
}
impl ClientProcessingData {
pub(crate) fn new(
store_dir: PathBuf,
registered_clients_ledger: Arc<FMutex<ClientLedger>>,
secret_key: Scalar,
secret_key: DummyMixIdentityPrivateKey,
) -> Self {
ClientProcessingData {
store_dir,
@@ -150,7 +151,7 @@ impl ClientRequestProcessor {
std::fs::create_dir_all(full_store_dir)
}
fn generate_new_auth_token(data: Vec<u8>, key: Scalar) -> AuthToken {
fn generate_new_auth_token(data: Vec<u8>, key: DummyMixIdentityPrivateKey) -> AuthToken {
let mut auth_token_raw =
HmacSha256::new_varkey(&key.to_bytes()).expect("HMAC can take key of any size");
auth_token_raw.input(&data);
@@ -232,7 +233,7 @@ mod create_storage_dir {
#[test]
fn it_creates_a_correct_storage_directory() {
let client_address: DestinationAddressBytes = [1u8; 32];
let store_dir = Path::new("./foo/");
let store_dir = Path::new("/tmp/");
ClientRequestProcessor::create_storage_dir(client_address, store_dir).unwrap();
}
}
@@ -245,7 +246,7 @@ mod generating_new_auth_token {
fn for_the_same_input_generates_the_same_auth_token() {
let data1 = vec![1u8; 55];
let data2 = vec![1u8; 55];
let key = Scalar::from_bytes_mod_order([1u8; 32]);
let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_eq!(token1, token2);
@@ -255,14 +256,14 @@ mod generating_new_auth_token {
fn for_different_inputs_generates_different_auth_tokens() {
let data1 = vec![1u8; 55];
let data2 = vec![2u8; 55];
let key = Scalar::from_bytes_mod_order([1u8; 32]);
let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_ne!(token1, token2);
let data1 = vec![1u8; 50];
let data2 = vec![2u8; 55];
let key = Scalar::from_bytes_mod_order([1u8; 32]);
let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_ne!(token1, token2);
@@ -1,4 +1,5 @@
use crate::provider::storage::StoreData;
use crypto::identity::DummyMixIdentityPrivateKey;
use curve25519_dalek::scalar::Scalar;
use sphinx::{ProcessedPacket, SphinxPacket};
use std::path::PathBuf;
@@ -35,12 +36,12 @@ impl From<std::io::Error> for MixProcessingError {
// ProcessingData defines all data required to correctly unwrap sphinx packets
#[derive(Debug, Clone)]
pub(crate) struct MixProcessingData {
secret_key: Scalar,
secret_key: DummyMixIdentityPrivateKey,
pub(crate) store_dir: PathBuf,
}
impl MixProcessingData {
pub(crate) fn new(secret_key: Scalar, store_dir: PathBuf) -> Self {
pub(crate) fn new(secret_key: DummyMixIdentityPrivateKey, store_dir: PathBuf) -> Self {
MixProcessingData {
secret_key,
store_dir,
@@ -62,7 +63,7 @@ impl MixPacketProcessor {
let packet = SphinxPacket::from_bytes(packet_data.to_vec())?;
let read_processing_data = processing_data.read().unwrap();
let (client_address, client_surb_id, payload) =
match packet.process(read_processing_data.secret_key) {
match packet.process(read_processing_data.secret_key.as_scalar()) {
ProcessedPacket::ProcessedPacketFinalHop(client_address, surb_id, payload) => {
(client_address, surb_id, payload)
}
+12 -18
View File
@@ -1,8 +1,9 @@
use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor};
use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData};
use crate::provider::storage::ClientStorage;
use curve25519_dalek::montgomery::MontgomeryPoint;
use curve25519_dalek::scalar::Scalar;
use crypto::identity::{
DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, MixnetIdentityPublicKey,
};
use directory_client::presence::MixProviderClient;
use futures::io::Error;
use futures::lock::Mutex as FMutex;
@@ -29,18 +30,11 @@ pub struct Config {
pub client_socket_address: SocketAddr,
pub directory_server: String,
pub mix_socket_address: SocketAddr,
pub public_key: MontgomeryPoint,
pub secret_key: Scalar,
pub public_key: DummyMixIdentityPublicKey,
pub secret_key: DummyMixIdentityPrivateKey,
pub store_dir: PathBuf,
}
impl Config {
pub fn public_key_string(public_key: MontgomeryPoint) -> String {
let key_bytes = public_key.to_bytes().to_vec();
base64::encode_config(&key_bytes, base64::URL_SAFE)
}
}
#[derive(Debug)]
pub enum ProviderError {
TcpListenerBindingError,
@@ -94,7 +88,7 @@ impl ClientLedger {
fn current_clients(&self) -> Vec<MixProviderClient> {
self.0
.iter()
.map(|(_, &v)| Config::public_key_string(MontgomeryPoint(v)))
.map(|(_, v)| base64::encode_config(v, base64::URL_SAFE))
.map(|pub_key| MixProviderClient { pub_key })
.collect()
}
@@ -109,14 +103,14 @@ pub struct ServiceProvider {
directory_server: String,
mix_network_address: SocketAddr,
client_network_address: SocketAddr,
secret_key: Scalar,
public_key: MontgomeryPoint,
public_key: DummyMixIdentityPublicKey,
secret_key: DummyMixIdentityPrivateKey,
store_dir: PathBuf,
registered_clients_ledger: ClientLedger,
}
impl ServiceProvider {
pub fn new(config: &Config) -> Self {
pub fn new(config: Config) -> Self {
ServiceProvider {
mix_network_address: config.mix_socket_address,
client_network_address: config.client_socket_address,
@@ -238,7 +232,7 @@ impl ServiceProvider {
async fn start_mixnet_listening(
address: SocketAddr,
secret_key: Scalar,
secret_key: DummyMixIdentityPrivateKey,
store_dir: PathBuf,
) -> Result<(), ProviderError> {
let mut listener = tokio::net::TcpListener::bind(address).await?;
@@ -260,7 +254,7 @@ impl ServiceProvider {
address: SocketAddr,
store_dir: PathBuf,
client_ledger: Arc<FMutex<ClientLedger>>,
secret_key: Scalar,
secret_key: DummyMixIdentityPrivateKey,
) -> Result<(), ProviderError> {
let mut listener = tokio::net::TcpListener::bind(address).await?;
let processing_data =
@@ -299,7 +293,7 @@ impl ServiceProvider {
let presence_future = rt.spawn(presence_notifier.run());
let mix_future = rt.spawn(ServiceProvider::start_mixnet_listening(
self.mix_network_address,
self.secret_key,
self.secret_key.clone(),
self.store_dir.clone(),
));
let client_future = rt.spawn(ServiceProvider::start_client_listening(
+3 -2
View File
@@ -1,4 +1,5 @@
use crate::provider::{ClientLedger, Config};
use crypto::identity::DummyMixIdentityPublicKey;
use curve25519_dalek::montgomery::MontgomeryPoint;
use directory_client::presence::MixProviderPresence;
use directory_client::requests::presence_providers_post::PresenceMixProviderPoster;
@@ -21,7 +22,7 @@ impl Notifier {
directory_server_address: String,
client_listener: SocketAddr,
mixnet_listener: SocketAddr,
pub_key: MontgomeryPoint,
pub_key: DummyMixIdentityPublicKey,
client_ledger: Arc<FMutex<ClientLedger>>,
) -> Notifier {
let directory_config = directory_client::Config {
@@ -33,7 +34,7 @@ impl Notifier {
net_client,
client_listener: client_listener.to_string(),
mixnet_listener: mixnet_listener.to_string(),
pub_key: Config::public_key_string(pub_key),
pub_key: pub_key.to_b64_string(),
client_ledger,
}
}