Skip to content

Commit

Permalink
Merge pull request #124 from Revertron/feature/encryption
Browse files Browse the repository at this point in the history
Implemented P2P traffic encryption.
  • Loading branch information
Revertron authored May 29, 2021
2 parents 5398410 + a49e6bd commit 24975e3
Show file tree
Hide file tree
Showing 16 changed files with 2,159 additions and 478 deletions.
1,317 changes: 1,317 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "alfis"
version = "0.5.9"
version = "0.6.0"
authors = ["Revertron <[email protected]>"]
edition = "2018"
build = "build.rs"
Expand All @@ -18,14 +18,17 @@ toml = "0.5.8"
digest = "0.9.0"
sha2 = "0.9.5"
ed25519-dalek = "1.0.1"
x25519-dalek = "1.1.1"
ecies-ed25519 = "0.5.1"
chacha20poly1305 = "0.8.0"
signature = "1.3.0"
blakeout = "0.3.0"
num_cpus = "1.13.0"
byteorder = "1.4.3"
serde = { version = "1.0.126", features = ["derive"] }
serde_json = "1.0.64"
bincode = "1.3.3"
serde_cbor = "0.11.1"
base64 = "0.13.0"
num-bigint = "0.4.0"
num-traits = "0.2.14"
Expand Down
10 changes: 10 additions & 0 deletions src/blockchain/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,23 @@ impl Block {
}
}

pub fn from_bytes(data: &[u8]) -> serde_cbor::Result<Self> {
serde_cbor::from_slice(data)
}

pub fn is_genesis(&self) -> bool {
self.index == 1 &&
matches!(Transaction::get_type(&self.transaction), TransactionType::Origin) &&
self.prev_block_hash == Bytes::default()
}

/// Serializes block to CBOR for network
pub fn as_bytes(&self) -> Vec<u8> {
serde_cbor::to_vec(&self).unwrap()
}

/// Serializes block to bincode format for hashing.
pub fn as_bytes_compact(&self) -> Vec<u8> {
bincode::serialize(&self).unwrap()
}

Expand Down
28 changes: 27 additions & 1 deletion src/blockchain/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,8 +1010,10 @@ impl SignersCache {
pub mod tests {
use log::LevelFilter;
use simplelog::{ColorChoice, ConfigBuilder, TerminalMode, TermLogger};
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};

use crate::{Chain, Settings};
use crate::{Chain, Settings, Block};

fn init_logger() {
let config = ConfigBuilder::new()
Expand All @@ -1035,4 +1037,28 @@ pub mod tests {
chain.check_chain(u64::MAX);
assert_eq!(chain.get_height(), 149);
}

#[test]
pub fn check_serde() {
let settings = Settings::default();
let chain = Chain::new(&settings, "./tests/blockchain.db");

// Check the first block, its transaction doesn't have identity
let block = chain.get_block(1).unwrap();
let buf = serde_cbor::to_vec(&block).unwrap();
let block2: Block = serde_cbor::from_slice(&buf[..]).unwrap();
assert_eq!(block, block2);

// Check second block, it is common "full" block with domain
let block = chain.get_block(2).unwrap();
let buf = serde_cbor::to_vec(&block).unwrap();
let block2: Block = serde_cbor::from_slice(&buf[..]).unwrap();
assert_eq!(block, block2);

// Check block 36, it is an "empty" block, used to sign full blocks
let block = chain.get_block(36).unwrap();
let buf = serde_cbor::to_vec(&block).unwrap();
let block2: Block = serde_cbor::from_slice(&buf[..]).unwrap();
assert_eq!(block, block2);
}
}
4 changes: 2 additions & 2 deletions src/blockchain/hash_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn check_block_hash(block: &Block) -> bool {
let mut copy: Block = block.clone();
copy.hash = Bytes::default();
copy.signature = Bytes::default();
blakeout_data(&copy.as_bytes()) == block.hash
blakeout_data(&copy.as_bytes_compact()) == block.hash
}

/// Hashes data by given hasher
Expand All @@ -23,7 +23,7 @@ pub fn blakeout_data(data: &[u8]) -> Bytes {
pub fn check_block_signature(block: &Block) -> bool {
let mut copy = block.clone();
copy.signature = Bytes::default();
Keystore::check(&copy.as_bytes(), &copy.pub_key, &block.signature)
Keystore::check(&copy.as_bytes_compact(), &copy.pub_key, &block.signature)
}

/// Hashes some identity (domain in case of DNS). If you give it a public key, it will hash with it as well.
Expand Down
5 changes: 0 additions & 5 deletions src/blockchain/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@ impl Transaction {
}
}

pub fn get_bytes(&self) -> Vec<u8> {
// Let it panic if something is not okay
serde_json::to_vec(&self).unwrap()
}

pub fn to_string(&self) -> String {
// Let it panic if something is not okay
serde_json::to_string(&self).unwrap()
Expand Down
8 changes: 0 additions & 8 deletions src/commons/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::net::IpAddr;
use std::num;

use mio::Token;
use rand::Rng;
#[cfg(not(target_os = "macos"))]
use thread_priority::*;
Expand Down Expand Up @@ -116,13 +115,6 @@ pub fn is_yggdrasil(addr: &IpAddr) -> bool {
false
}

/// Gets new token from old token, mutating the last
pub fn next(current: &mut Token) -> Token {
let next = current.0;
current.0 += 1;
Token(next)
}

/// Checks if this record has IP from Yggdrasil network
/// https://yggdrasil-network.github.io
pub fn is_yggdrasil_record(record: &DnsRecord) -> bool {
Expand Down
66 changes: 66 additions & 0 deletions src/crypto/chacha.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
use chacha20poly1305::aead::{Aead, NewAead};
use std::fmt::{Debug, Formatter};
use std::fmt;

pub const ZERO_NONCE: [u8; 12] = [0u8; 12];
const FAILURE: &str = "encryption failure!";

/// A small wrap-up to use Chacha20 encryption for domain names.
#[derive(Clone)]
pub struct Chacha {
cipher: ChaCha20Poly1305,
nonce: [u8; 12]
}

impl Chacha {
pub fn new(key: &[u8], nonce: &[u8]) -> Self {
let key = Key::from_slice(key);
let cipher = ChaCha20Poly1305::new(key);
let mut buf = [0u8; 12];
buf.copy_from_slice(nonce);
Chacha { cipher, nonce: buf }
}

pub fn encrypt(&self, data: &[u8]) -> Vec<u8> {
let nonce = Nonce::from(self.nonce.clone());
self.cipher.encrypt(&nonce, data.as_ref()).expect(FAILURE)
}

pub fn decrypt(&self, data: &[u8]) -> Vec<u8> {
let nonce = Nonce::from(self.nonce.clone());
self.cipher.decrypt(&nonce, data.as_ref()).expect(FAILURE)
}

pub fn get_nonce(&self) -> &[u8; 12] {
&self.nonce
}
}

impl Debug for Chacha {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
fmt.write_str("ChaCha20Poly1305")
}
}

#[cfg(test)]
mod tests {
use crate::crypto::Chacha;
use crate::{to_hex};

#[test]
pub fn test_chacha() {
let buf = b"178135D209C697625E3EC71DA5C760382E54936F824EE5083908DA66B14ECE18";
let chacha1 = Chacha::new(b"178135D209C697625E3EC71DA5C76038", &buf[..12]);
let bytes1 = chacha1.encrypt(b"TEST");
println!("{}", to_hex(&bytes1));

let chacha2 = Chacha::new(b"178135D209C697625E3EC71DA5C76038", &buf[..12]);
let bytes2 = chacha2.decrypt(&bytes1);
assert_eq!(String::from_utf8(bytes2).unwrap(), "TEST");

let bytes2 = chacha2.encrypt(b"TEST");

assert_eq!(bytes1, bytes2);
}
}
5 changes: 4 additions & 1 deletion src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
mod crypto_box;
mod chacha;

pub use crypto_box::CryptoBox;
pub use crypto_box::CryptoBox;
pub use chacha::Chacha;
pub use chacha::ZERO_NONCE;
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,11 @@ fn main() {
let miner: Arc<Mutex<Miner>> = Arc::new(Mutex::new(miner_obj));

let mut network = Network::new(Arc::clone(&context));
network.start().expect("Error starting network component");
thread::spawn(move || {
// Give UI some time to appear :)
thread::sleep(Duration::from_millis(1000));
network.start();
});

create_genesis_if_needed(&context, &miner);
if no_gui {
Expand Down
4 changes: 2 additions & 2 deletions src/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ impl Miner {
Some(mut block) => {
let index = block.index;
let mut context = context.lock().unwrap();
block.signature = Bytes::from_bytes(&job.keystore.sign(&block.as_bytes()));
block.signature = Bytes::from_bytes(&job.keystore.sign(&block.as_bytes_compact()));
let mut success = false;
if context.chain.check_new_block(&block) != BlockQuality::Good {
warn!("Error adding mined block!");
Expand Down Expand Up @@ -341,7 +341,7 @@ fn find_hash(context: Arc<Mutex<Context>>, mut block: Block, running: Arc<Atomic
block.nonce = nonce;

digest.reset();
digest.update(&block.as_bytes());
digest.update(&block.as_bytes_compact());
let diff = hash_difficulty(digest.result());
if diff >= target_diff {
block.hash = Bytes::from_bytes(digest.result());
Expand Down
38 changes: 10 additions & 28 deletions src/p2p/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,32 @@ use crate::Bytes;
#[derive(Debug, Serialize, Deserialize)]
pub enum Message {
Error,
Hand { #[serde(default = "default_version")] app_version: String, origin: String, version: u32, public: bool, #[serde(default)] rand: String },
Shake { #[serde(default = "default_version")] app_version: String, origin: String, version: u32, ok: bool, height: u64 },
Hand { app_version: String, origin: String, version: u32, public: bool, rand_id: String, },
Shake { app_version: String, origin: String, version: u32, public: bool, rand_id: String, height: u64 },
Ping { height: u64, hash: Bytes },
Pong { height: u64, hash: Bytes },
Twin,
Loop,
GetPeers,
Peers { peers: Vec<String> },
GetBlock { index: u64 },
Block { index: u64, block: String },
Block { index: u64, block: Vec<u8> },
}

impl Message {
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, ()> {
let text = String::from_utf8(bytes).unwrap_or(String::from("Error{}"));
match serde_json::from_str(&text) {
match serde_cbor::from_slice(bytes.as_slice()) {
Ok(cmd) => Ok(cmd),
Err(_) => Err(())
}
}

pub fn hand(app_version: &str, origin: &str, version: u32, public: bool, rand: &str) -> Self {
Message::Hand { app_version: app_version.to_owned(), origin: origin.to_owned(), version, public, rand: rand.to_owned() }
pub fn hand(app_version: &str, origin: &str, version: u32, public: bool, rand_id: &str) -> Self {
Message::Hand { app_version: app_version.to_owned(), origin: origin.to_owned(), version, public, rand_id: rand_id.to_owned() }
}

pub fn shake(app_version: &str, origin: &str, version: u32, ok: bool, height: u64) -> Self {
Message::Shake { app_version: app_version.to_owned(), origin: origin.to_owned(), version, ok, height }
pub fn shake(app_version: &str, origin: &str, version: u32, public: bool, rand_id: &str, height: u64) -> Self {
Message::Shake { app_version: app_version.to_owned(), origin: origin.to_owned(), version, public, rand_id: rand_id.to_owned(), height }
}

pub fn ping(height: u64, hash: Bytes) -> Self {
Expand All @@ -44,24 +43,7 @@ impl Message {
Message::Pong { height, hash }
}

pub fn block(height: u64, str: String) -> Self {
Message::Block { index: height, block: str }
pub fn block(height: u64, block: Vec<u8>) -> Self {
Message::Block { index: height, block }
}
}

fn default_version() -> String {
String::from("0.0.0")
}

#[cfg(test)]
mod tests {
use crate::p2p::Message;

#[test]
pub fn test_hand() {
assert!(serde_json::from_str::<Message>("\"Error\"").is_ok());
assert!(serde_json::from_str::<Message>("{\"Hand\":{\"origin\":\"\",\"version\":1,\"public\":false,\"rand\":\"123\"}}").is_ok());
assert!(serde_json::from_str::<Message>("{\"Hand\":{\"origin\":\"\",\"version\":1,\"public\":false}}").is_ok());
}

}
Loading

0 comments on commit 24975e3

Please sign in to comment.