Implemented P2P traffic encryption.
Changed serialization format of P2P messages. Refactored P2P network code.
This commit is contained in:
+4
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "alfis"
|
||||
version = "0.5.9"
|
||||
version = "0.6.0"
|
||||
authors = ["Revertron <alfis@revertron.com>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
@@ -18,7 +18,9 @@ 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"
|
||||
@@ -26,6 +28,7 @@ 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"
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
+27
-1
@@ -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()
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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(©.as_bytes()) == block.hash
|
||||
blakeout_data(©.as_bytes_compact()) == block.hash
|
||||
}
|
||||
|
||||
/// Hashes data by given hasher
|
||||
@@ -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(©.as_bytes(), ©.pub_key, &block.signature)
|
||||
Keystore::check(©.as_bytes_compact(), ©.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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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::*;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -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;
|
||||
+5
-1
@@ -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 {
|
||||
|
||||
+2
-2
@@ -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!");
|
||||
@@ -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());
|
||||
|
||||
+10
-28
@@ -7,8 +7,8 @@ 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,
|
||||
@@ -16,24 +16,23 @@ pub enum Message {
|
||||
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 {
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+683
-441
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ use std::collections::HashMap;
|
||||
use mio::net::TcpStream;
|
||||
use crate::p2p::State;
|
||||
use crate::Block;
|
||||
use crate::crypto::Chacha;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Peer {
|
||||
@@ -16,6 +17,7 @@ pub struct Peer {
|
||||
active: bool,
|
||||
reconnects: u32,
|
||||
received_block: u64,
|
||||
cipher: Option<Chacha>,
|
||||
fork: HashMap<u64, Block>
|
||||
}
|
||||
|
||||
@@ -32,10 +34,26 @@ impl Peer {
|
||||
active: false,
|
||||
reconnects: 0,
|
||||
received_block: 0,
|
||||
cipher: None,
|
||||
fork: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cipher(&mut self, cipher: Chacha) {
|
||||
self.cipher = Some(cipher);
|
||||
}
|
||||
|
||||
pub fn get_cipher(&self) -> &Option<Chacha> {
|
||||
&self.cipher
|
||||
}
|
||||
|
||||
pub fn get_nonce(&self) -> &[u8; 12] {
|
||||
match &self.cipher {
|
||||
None => { &crate::crypto::ZERO_NONCE }
|
||||
Some(chacha) => { chacha.get_nonce() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_addr(&self) -> SocketAddr {
|
||||
self.addr.clone()
|
||||
}
|
||||
|
||||
+22
-1
@@ -12,7 +12,6 @@ use rand::seq::IteratorRandom;
|
||||
use crate::{Bytes, commons};
|
||||
use crate::commons::*;
|
||||
use crate::p2p::{Message, Peer, State};
|
||||
use crate::commons::next;
|
||||
use std::io;
|
||||
|
||||
const PING_PERIOD: u64 = 30;
|
||||
@@ -84,6 +83,12 @@ impl Peers {
|
||||
State::Twin => {
|
||||
info!("Peer connection {} to {:?} is a twin", &token.0, &peer.get_addr());
|
||||
}
|
||||
State::ServerHandshake => {
|
||||
info!("Peer connection {} from {:?} didn't shake hands", &token.0, &peer.get_addr());
|
||||
}
|
||||
State::HandshakeFinished => {
|
||||
info!("Peer connection {} from {:?} shaked hands, but then failed", &token.0, &peer.get_addr());
|
||||
}
|
||||
}
|
||||
|
||||
self.peers.remove(token);
|
||||
@@ -193,6 +198,15 @@ impl Peers {
|
||||
count
|
||||
}
|
||||
|
||||
pub fn is_tween_connect(&self, id: &str) -> bool {
|
||||
for (_, peer) in self.peers.iter() {
|
||||
if peer.active() && peer.get_id() == id {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_peers_banned_count(&self) -> usize {
|
||||
self.ignored.len()
|
||||
}
|
||||
@@ -401,6 +415,13 @@ impl Peers {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
fn skip_private_addr(addr: &SocketAddr) -> bool {
|
||||
if addr.ip().is_loopback() {
|
||||
return true;
|
||||
|
||||
+4
-2
@@ -5,6 +5,8 @@ use crate::p2p::Message;
|
||||
pub enum State {
|
||||
Connecting,
|
||||
Connected,
|
||||
ServerHandshake,
|
||||
HandshakeFinished,
|
||||
Idle { from: Instant },
|
||||
Message { data: Vec<u8> },
|
||||
Error,
|
||||
@@ -25,8 +27,8 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn message(message: Message) -> Self {
|
||||
let response = serde_json::to_string(&message).unwrap();
|
||||
State::Message {data: Vec::from(response.as_bytes()) }
|
||||
let data = serde_cbor::to_vec(&message).unwrap();
|
||||
State::Message { data }
|
||||
}
|
||||
|
||||
pub fn is_idle(&self) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user