Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Milestone 2 #3

Merged
merged 13 commits into from
Aug 15, 2022
4,389 changes: 3,662 additions & 727 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,31 @@ blake2 = { package = "blake2-rfc", version = "0.2"}
byteorder = "1"
bytes = "0.5.6"
chacha20 = "0.8.1"
clap = { version = "2.33", features = ["yaml"] }
failure = "0.1.8"
futures = "0.3"
hmac = { version = "0.11.0", features = ["std"]}
hmac = { version = "0.12.0", features = ["std"]}
hyper = { version = "0.14", features = ["full"] }
jsonrpc-core = "18.0"
jsonrpc-derive = "18.0"
jsonrpc-http-server = "18.0"
lazy_static = "1"
pbkdf2 = "0.8.0"
rand = "0.8.4"
ring = "0.16"
serde = { version = "1", features= ["derive"]}
serde_derive = "1"
serde_json = "1"
sha2 = "0.9.8"
sha2 = "0.10.0"
tokio = { version = "1", features = ["full"] }
toml = "0.5"
grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]}
grin_util = "5"
grin_util = "5"
grin_api = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
grin_core = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
grin_chain = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
grin_keychain = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
grin_servers = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", version = "5.1.0-alpha.1" }
grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", version = "5.1.0-alpha.1" }
grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", version = "5.1.0-alpha.1" }
37 changes: 37 additions & 0 deletions mwixnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: mwixnet
about: MWixnet CoinSwap Server
author: scilio

args:
- config_file:
help: Path to load/save the mwixnet-config.toml configuration file
short: c
long: config_file
takes_value: true
- pass:
help: Password to encrypt/decrypt the server key in the configuration file
short: p
long: pass
takes_value: true
required: true
- grin_node_url:
help: Api address of running GRIN node on which to check inputs and post transactions
short: n
long: grin_node_url
takes_value: true
- wallet_owner_url:
help: Api address of running wallet owner listener
short: l
long: wallet_owner_url
takes_value: true
- wallet_pass:
help: The wallet's password
long: wallet_pass
takes_value: true
- bind_addr:
help: Address to bind the rpc server to (e.g. 0.0.0.0:3000)
long: bind_addr
takes_value: true
subcommands:
- init-config:
about: Writes a new configuration file
177 changes: 177 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
use crate::error::{self, Result};
use crate::secp::SecretKey;

use core::num::NonZeroU32;
use grin_util::{ToHex, ZeroingString};
use rand::{thread_rng, Rng};
use ring::{aead, pbkdf2};
use serde_derive::{Deserialize, Serialize};
use std::fs::File;
use std::io::prelude::*;
use std::net::SocketAddr;
use std::path::PathBuf;

// The decrypted server config to be passed around and used by the rest of the mwixnet code
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ServerConfig {
pub key: SecretKey,
pub addr: SocketAddr,
pub grin_node_url: SocketAddr,
pub wallet_owner_url: SocketAddr,
}

/// Encrypted server key, for storing on disk and decrypting with provided password
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
struct EncryptedServerKey {
encrypted_key: String,
pub salt: String,
pub nonce: String,
}

impl EncryptedServerKey {
pub fn from_secret_key(
server_key: &SecretKey,
password: &ZeroingString,
) -> Result<EncryptedServerKey> {
let salt: [u8; 8] = thread_rng().gen();
let nonce: [u8; 12] = thread_rng().gen();
let password = password.as_bytes();
let mut key = [0; 32];
ring::pbkdf2::derive(
ring::pbkdf2::PBKDF2_HMAC_SHA512,
NonZeroU32::new(100).unwrap(),
&salt,
password,
&mut key,
);
let content = server_key.0.to_vec();
let mut enc_bytes = content;

let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
let sealing_key: aead::LessSafeKey = aead::LessSafeKey::new(unbound_key);
let aad = aead::Aad::from(&[]);
let _ = sealing_key.seal_in_place_append_tag(
aead::Nonce::assume_unique_for_key(nonce),
aad,
&mut enc_bytes,
).map_err(|_| error::ErrorKind::SaveConfigError)?;

Ok(EncryptedServerKey {
encrypted_key: enc_bytes.to_hex(),
salt: salt.to_hex(),
nonce: nonce.to_hex(),
})
}

pub fn decrypt(&self, password: &str) -> Result<SecretKey> {
let mut encrypted_seed = grin_util::from_hex(&self.encrypted_key.clone())
.map_err(|_| error::ErrorKind::LoadConfigError)?;
let salt = grin_util::from_hex(&self.salt.clone())
.map_err(|_| error::ErrorKind::LoadConfigError)?;
let nonce = grin_util::from_hex(&self.nonce.clone())
.map_err(|_| error::ErrorKind::LoadConfigError)?;
let password = password.as_bytes();
let mut key = [0; 32];
pbkdf2::derive(
ring::pbkdf2::PBKDF2_HMAC_SHA512,
NonZeroU32::new(100).unwrap(),
&salt,
password,
&mut key,
);

let mut n = [0u8; 12];
n.copy_from_slice(&nonce[0..12]);
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
let opening_key: aead::LessSafeKey = aead::LessSafeKey::new(unbound_key);
let aad = aead::Aad::from(&[]);
let _ = opening_key.open_in_place(
aead::Nonce::assume_unique_for_key(n),
aad,
&mut encrypted_seed,
).map_err(|_| error::ErrorKind::LoadConfigError)?;

for _ in 0..aead::AES_256_GCM.tag_len() {
encrypted_seed.pop();
}

let secp = secp256k1zkp::Secp256k1::new();
Ok(SecretKey::from_slice(&secp, &encrypted_seed).unwrap())
}
}

/// The config attributes saved to disk
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
struct RawConfig {
pub encrypted_key: String,
pub salt: String,
pub nonce: String,
pub addr: SocketAddr,
pub grin_node_url: SocketAddr,
pub wallet_owner_url: SocketAddr,
}

/// Writes the server config to the config_path given, encrypting the server_key first.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should ideally be spat out in the config file, similar to grin and grin-wallet (can be done later)

pub fn write_config(config_path: &PathBuf, server_config: &ServerConfig, password: &ZeroingString) -> Result<()> {
let encrypted = EncryptedServerKey::from_secret_key(&server_config.key, &password)?;

let raw_config = RawConfig{
encrypted_key: encrypted.encrypted_key,
salt: encrypted.salt,
nonce: encrypted.nonce,
addr: server_config.addr,
grin_node_url: server_config.grin_node_url,
wallet_owner_url: server_config.wallet_owner_url,
};
let encoded: String = toml::to_string(&raw_config).map_err(|_| error::ErrorKind::SaveConfigError)?;

let mut file = File::create(config_path)?;
file.write_all(encoded.as_bytes()).map_err(|_| error::ErrorKind::SaveConfigError)?;

Ok(())
}

/// Reads the server config from the config_path given and decrypts it with the provided password.
pub fn load_config(config_path: &PathBuf, password: &ZeroingString) -> Result<ServerConfig> {
let contents = std::fs::read_to_string(config_path)?;
let raw_config: RawConfig = toml::from_str(&contents)
.map_err(|_| error::ErrorKind::LoadConfigError)?;

let encrypted_key = EncryptedServerKey{
encrypted_key: raw_config.encrypted_key,
salt: raw_config.salt,
nonce: raw_config.nonce
};
let secret_key = encrypted_key.decrypt(&password)?;

Ok(ServerConfig {
key: secret_key,
addr: raw_config.addr,
grin_node_url: raw_config.grin_node_url,
wallet_owner_url: raw_config.wallet_owner_url
})
}

#[cfg(test)]
mod tests {
use super::*;
use crate::secp;

#[test]
fn server_key_encrypt() {
let password = ZeroingString::from("password");
let server_key = secp::random_secret();
let mut enc_key = EncryptedServerKey::from_secret_key(&server_key, &password).unwrap();
let decrypted_key = enc_key.decrypt(&password).unwrap();
assert_eq!(server_key, decrypted_key);

// Wrong password
let decrypted_key = enc_key.decrypt("wrongpass");
assert!(decrypted_key.is_err());

// Wrong nonce
enc_key.nonce = "wrongnonce".to_owned();
let decrypted_key = enc_key.decrypt(&password);
assert!(decrypted_key.is_err());
}
}
72 changes: 70 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use failure::{self, Context, Fail};
use std::fmt::{self, Display};
use std::io;
use grin_wallet_libwallet as libwallet;

/// MWixnet error definition
#[derive(Debug)]
Expand Down Expand Up @@ -45,6 +46,27 @@ pub enum ErrorKind {
/// When asked to read too much data
#[fail(display = "TooLargeReadErr")]
TooLargeReadErr,
/// Error from grin's api crate
#[fail(display = "GRIN API Error")]
GrinApiError,
/// Error from grin core
#[fail(display = "grincore Error")]
GrinCoreError,
/// Error from grin-wallet's libwallet
#[fail(display = "libwallet Error")]
LibWalletError,
/// Error from serde-json
#[fail(display = "serde json Error")]
SerdeJsonError,
/// Error from invalid signature
#[fail(display = "invalid signature Error")]
InvalidSigError,
/// Error while saving config
#[fail(display = "save config Error")]
SaveConfigError,
/// Error while loading config
#[fail(display = "load config Error")]
LoadConfigError,
}

impl std::error::Error for Error {
Expand All @@ -70,6 +92,12 @@ impl Display for Error {
}

impl Error {
pub fn new(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}

pub fn kind(&self) -> ErrorKind {
self.inner.get_context().clone()
}
Expand Down Expand Up @@ -101,10 +129,50 @@ impl From<secp256k1zkp::Error> for Error {
}
}

impl From<hmac::crypto_mac::InvalidKeyLength> for Error {
fn from(_error: hmac::crypto_mac::InvalidKeyLength) -> Error {
impl From<hmac::digest::InvalidLength> for Error {
fn from(_error: hmac::digest::InvalidLength) -> Error {
Error {
inner: Context::new(ErrorKind::InvalidKeyLength),
}
}
}

impl From<grin_api::Error> for Error {
fn from(_error: grin_api::Error) -> Error {
Error {
inner: Context::new(ErrorKind::GrinApiError),
}
}
}

impl From<grin_api::json_rpc::Error> for Error {
fn from(_error: grin_api::json_rpc::Error) -> Error {
Error {
inner: Context::new(ErrorKind::GrinApiError),
}
}
}

impl From<grin_core::core::transaction::Error> for Error {
fn from(_error: grin_core::core::transaction::Error) -> Error {
Error {
inner: Context::new(ErrorKind::GrinCoreError),
}
}
}

impl From<libwallet::Error> for Error {
fn from(_error: libwallet::Error) -> Error {
Error {
inner: Context::new(ErrorKind::LibWalletError),
}
}
}

impl From<serde_json::Error> for Error {
fn from(_error: serde_json::Error) -> Error {
Error {
inner: Context::new(ErrorKind::SerdeJsonError),
}
}
}
Loading