mirror of https://github.com/dswd/vpncloud.git
Send salted hash of node_id
This commit is contained in:
parent
941ac62bac
commit
af7a7f6a29
17
src/cloud.rs
17
src/cloud.rs
|
@ -292,7 +292,12 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
|
||||||
peers.partial_shuffle(&mut rng, 20);
|
peers.partial_shuffle(&mut rng, 20);
|
||||||
peers.truncate(20);
|
peers.truncate(20);
|
||||||
}
|
}
|
||||||
NodeInfo { peers, claims: self.claims.clone(), peer_timeout: Some(self.peer_timeout_publish) }
|
NodeInfo {
|
||||||
|
node_id: self.node_id,
|
||||||
|
peers,
|
||||||
|
claims: self.claims.clone(),
|
||||||
|
peer_timeout: Some(self.peer_timeout_publish)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_sock(&mut self, addr: SocketAddr) -> Result<(), Error> {
|
fn connect_sock(&mut self, addr: SocketAddr) -> Result<(), Error> {
|
||||||
|
@ -584,12 +589,12 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_new_peer(&mut self, addr: SocketAddr, node_id: NodeId, info: NodeInfo) -> Result<(), Error> {
|
fn add_new_peer(&mut self, addr: SocketAddr, info: NodeInfo) -> Result<(), Error> {
|
||||||
info!("Added peer {}", addr_nice(addr));
|
info!("Added peer {}", addr_nice(addr));
|
||||||
if let Some(init) = self.pending_inits.remove(&addr) {
|
if let Some(init) = self.pending_inits.remove(&addr) {
|
||||||
self.peers.insert(addr, PeerData {
|
self.peers.insert(addr, PeerData {
|
||||||
crypto: init,
|
crypto: init,
|
||||||
node_id,
|
node_id: info.node_id,
|
||||||
peer_timeout: info.peer_timeout.unwrap_or(DEFAULT_PEER_TIMEOUT),
|
peer_timeout: info.peer_timeout.unwrap_or(DEFAULT_PEER_TIMEOUT),
|
||||||
last_seen: TS::now(),
|
last_seen: TS::now(),
|
||||||
timeout: TS::now() + self.config.peer_timeout as Time
|
timeout: TS::now() + self.config.peer_timeout as Time
|
||||||
|
@ -684,9 +689,9 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
|
||||||
_ => return Err(Error::Message("Unknown message type"))
|
_ => return Err(Error::Message("Unknown message type"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MessageResult::Initialized(node_id, info) => self.add_new_peer(src, node_id, info)?,
|
MessageResult::Initialized(info) => self.add_new_peer(src, info)?,
|
||||||
MessageResult::InitializedWithReply(node_id, info) => {
|
MessageResult::InitializedWithReply(info) => {
|
||||||
self.add_new_peer(src, node_id, info)?;
|
self.add_new_peer(src, info)?;
|
||||||
self.send_to(src, data)?
|
self.send_to(src, data)?
|
||||||
}
|
}
|
||||||
MessageResult::Reply => self.send_to(src, data)?,
|
MessageResult::Reply => self.send_to(src, data)?,
|
||||||
|
|
|
@ -417,7 +417,7 @@ pub struct Args {
|
||||||
pub version: bool,
|
pub version: bool,
|
||||||
|
|
||||||
/// Generate and print a key-pair and exit
|
/// Generate and print a key-pair and exit
|
||||||
#[structopt(long, conflicts_with_all=&["password", "private_key"])]
|
#[structopt(long, conflicts_with="private_key")]
|
||||||
pub genkey: bool,
|
pub genkey: bool,
|
||||||
|
|
||||||
/// Disable automatic port forwarding
|
/// Disable automatic port forwarding
|
||||||
|
|
|
@ -55,7 +55,7 @@ use super::{
|
||||||
core::{CryptoCore, EXTRA_LEN},
|
core::{CryptoCore, EXTRA_LEN},
|
||||||
Algorithms, EcdhPrivateKey, EcdhPublicKey, Ed25519PublicKey, Error, MsgBuffer, Payload
|
Algorithms, EcdhPrivateKey, EcdhPublicKey, Ed25519PublicKey, Error, MsgBuffer, Payload
|
||||||
};
|
};
|
||||||
use crate::types::{NodeId, NODE_ID_BYTES};
|
use crate::types::NodeId;
|
||||||
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use ring::{
|
use ring::{
|
||||||
aead::{Algorithm, LessSafeKey, UnboundKey, AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305},
|
aead::{Algorithm, LessSafeKey, UnboundKey, AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305},
|
||||||
|
@ -79,20 +79,35 @@ pub const STAGE_PENG: u8 = 3;
|
||||||
pub const WAITING_TO_CLOSE: u8 = 4;
|
pub const WAITING_TO_CLOSE: u8 = 4;
|
||||||
pub const CLOSING: u8 = 5;
|
pub const CLOSING: u8 = 5;
|
||||||
|
|
||||||
|
pub const SALTED_NODE_ID_HASH_LEN: usize = 20;
|
||||||
|
pub type SaltedNodeIdHash = [u8; SALTED_NODE_ID_HASH_LEN];
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum InitMsg {
|
pub enum InitMsg {
|
||||||
Ping { node_id: NodeId, ecdh_public_key: EcdhPublicKey, algorithms: Algorithms },
|
// TODO: include only salted hashes of node_id and use public_key for leader election
|
||||||
Pong { node_id: NodeId, ecdh_public_key: EcdhPublicKey, algorithms: Algorithms, encrypted_payload: MsgBuffer },
|
Ping {
|
||||||
Peng { node_id: NodeId, encrypted_payload: MsgBuffer }
|
salted_node_id_hash: SaltedNodeIdHash,
|
||||||
|
ecdh_public_key: EcdhPublicKey,
|
||||||
|
algorithms: Algorithms
|
||||||
|
},
|
||||||
|
Pong {
|
||||||
|
salted_node_id_hash: SaltedNodeIdHash,
|
||||||
|
ecdh_public_key: EcdhPublicKey,
|
||||||
|
algorithms: Algorithms,
|
||||||
|
encrypted_payload: MsgBuffer
|
||||||
|
},
|
||||||
|
Peng {
|
||||||
|
salted_node_id_hash: SaltedNodeIdHash,
|
||||||
|
encrypted_payload: MsgBuffer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InitMsg {
|
impl InitMsg {
|
||||||
const PART_ALGORITHMS: u8 = 4;
|
const PART_ALGORITHMS: u8 = 4;
|
||||||
const PART_ECDH_PUBLIC_KEY: u8 = 3;
|
const PART_ECDH_PUBLIC_KEY: u8 = 3;
|
||||||
const PART_END: u8 = 0;
|
const PART_END: u8 = 0;
|
||||||
const PART_NODE_ID: u8 = 2;
|
|
||||||
const PART_PAYLOAD: u8 = 5;
|
const PART_PAYLOAD: u8 = 5;
|
||||||
|
const PART_SALTED_NODE_ID_HASH: u8 = 2;
|
||||||
const PART_STAGE: u8 = 1;
|
const PART_STAGE: u8 = 1;
|
||||||
|
|
||||||
fn stage(&self) -> u8 {
|
fn stage(&self) -> u8 {
|
||||||
|
@ -103,9 +118,11 @@ impl InitMsg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_id(&self) -> NodeId {
|
fn salted_node_id_hash(&self) -> &SaltedNodeIdHash {
|
||||||
match self {
|
match self {
|
||||||
InitMsg::Ping { node_id, .. } | InitMsg::Pong { node_id, .. } | InitMsg::Peng { node_id, .. } => *node_id
|
InitMsg::Ping { salted_node_id_hash, .. }
|
||||||
|
| InitMsg::Pong { salted_node_id_hash, .. }
|
||||||
|
| InitMsg::Peng { salted_node_id_hash, .. } => salted_node_id_hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +157,7 @@ impl InitMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut stage = None;
|
let mut stage = None;
|
||||||
let mut node_id = None;
|
let mut salted_node_id_hash = None;
|
||||||
let mut ecdh_public_key = None;
|
let mut ecdh_public_key = None;
|
||||||
let mut encrypted_payload = None;
|
let mut encrypted_payload = None;
|
||||||
let mut algorithms = None;
|
let mut algorithms = None;
|
||||||
|
@ -158,13 +175,13 @@ impl InitMsg {
|
||||||
}
|
}
|
||||||
stage = Some(r.read_u8().map_err(|_| Error::Parse("Init message too short"))?)
|
stage = Some(r.read_u8().map_err(|_| Error::Parse("Init message too short"))?)
|
||||||
}
|
}
|
||||||
Self::PART_NODE_ID => {
|
Self::PART_SALTED_NODE_ID_HASH => {
|
||||||
if field_len != NODE_ID_BYTES {
|
if field_len != SALTED_NODE_ID_HASH_LEN {
|
||||||
return Err(Error::CryptoInit("Invalid size for node id field"))
|
return Err(Error::CryptoInit("Invalid size for salted node id hash field"))
|
||||||
}
|
}
|
||||||
let mut id = [0; NODE_ID_BYTES];
|
let mut id = [0; SALTED_NODE_ID_HASH_LEN];
|
||||||
r.read_exact(&mut id).map_err(|_| Error::Parse("Init message too short"))?;
|
r.read_exact(&mut id).map_err(|_| Error::Parse("Init message too short"))?;
|
||||||
node_id = Some(id)
|
salted_node_id_hash = Some(id)
|
||||||
}
|
}
|
||||||
Self::PART_ECDH_PUBLIC_KEY => {
|
Self::PART_ECDH_PUBLIC_KEY => {
|
||||||
let mut pub_key_data = smallvec![0; field_len];
|
let mut pub_key_data = smallvec![0; field_len];
|
||||||
|
@ -223,7 +240,7 @@ impl InitMsg {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return Err(Error::CryptoInit("Init message without stage"))
|
None => return Err(Error::CryptoInit("Init message without stage"))
|
||||||
};
|
};
|
||||||
let node_id = match node_id {
|
let salted_node_id_hash = match salted_node_id_hash {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return Err(Error::CryptoInit("Init message without node id"))
|
None => return Err(Error::CryptoInit("Init message without node id"))
|
||||||
};
|
};
|
||||||
|
@ -238,7 +255,7 @@ impl InitMsg {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return Err(Error::CryptoInit("Init message without algorithms"))
|
None => return Err(Error::CryptoInit("Init message without algorithms"))
|
||||||
};
|
};
|
||||||
Self::Ping { node_id, ecdh_public_key, algorithms }
|
Self::Ping { salted_node_id_hash, ecdh_public_key, algorithms }
|
||||||
}
|
}
|
||||||
STAGE_PONG => {
|
STAGE_PONG => {
|
||||||
let ecdh_public_key = match ecdh_public_key {
|
let ecdh_public_key = match ecdh_public_key {
|
||||||
|
@ -253,14 +270,14 @@ impl InitMsg {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return Err(Error::CryptoInit("Init message without payload"))
|
None => return Err(Error::CryptoInit("Init message without payload"))
|
||||||
};
|
};
|
||||||
Self::Pong { node_id, ecdh_public_key, algorithms, encrypted_payload }
|
Self::Pong { salted_node_id_hash, ecdh_public_key, algorithms, encrypted_payload }
|
||||||
}
|
}
|
||||||
STAGE_PENG => {
|
STAGE_PENG => {
|
||||||
let encrypted_payload = match encrypted_payload {
|
let encrypted_payload = match encrypted_payload {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return Err(Error::CryptoInit("Init message without payload"))
|
None => return Err(Error::CryptoInit("Init message without payload"))
|
||||||
};
|
};
|
||||||
Self::Peng { node_id, encrypted_payload }
|
Self::Peng { salted_node_id_hash, encrypted_payload }
|
||||||
}
|
}
|
||||||
_ => return Err(Error::CryptoInit("Invalid stage"))
|
_ => return Err(Error::CryptoInit("Invalid stage"))
|
||||||
};
|
};
|
||||||
|
@ -285,10 +302,12 @@ impl InitMsg {
|
||||||
w.write_u8(self.stage())?;
|
w.write_u8(self.stage())?;
|
||||||
|
|
||||||
match &self {
|
match &self {
|
||||||
Self::Ping { node_id, .. } | Self::Pong { node_id, .. } | Self::Peng { node_id, .. } => {
|
Self::Ping { salted_node_id_hash, .. }
|
||||||
w.write_u8(Self::PART_NODE_ID)?;
|
| Self::Pong { salted_node_id_hash, .. }
|
||||||
w.write_u16::<NetworkEndian>(NODE_ID_BYTES as u16)?;
|
| Self::Peng { salted_node_id_hash, .. } => {
|
||||||
w.write_all(node_id)?;
|
w.write_u8(Self::PART_SALTED_NODE_ID_HASH)?;
|
||||||
|
w.write_u16::<NetworkEndian>(SALTED_NODE_ID_HASH_LEN as u16)?;
|
||||||
|
w.write_all(salted_node_id_hash)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,12 +373,13 @@ impl InitMsg {
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum InitResult<P: Payload> {
|
pub enum InitResult<P: Payload> {
|
||||||
Continue,
|
Continue,
|
||||||
Success { peer_payload: P, node_id: NodeId, is_initiator: bool }
|
Success { peer_payload: P, is_initiator: bool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct InitState<P: Payload> {
|
pub struct InitState<P: Payload> {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
|
salted_node_id_hash: SaltedNodeIdHash,
|
||||||
payload: P,
|
payload: P,
|
||||||
key_pair: Arc<Ed25519KeyPair>,
|
key_pair: Arc<Ed25519KeyPair>,
|
||||||
trusted_keys: Arc<Vec<Ed25519PublicKey>>,
|
trusted_keys: Arc<Vec<Ed25519PublicKey>>,
|
||||||
|
@ -378,8 +398,15 @@ impl<P: Payload> InitState<P> {
|
||||||
algorithms: Algorithms
|
algorithms: Algorithms
|
||||||
) -> Self
|
) -> Self
|
||||||
{
|
{
|
||||||
|
let mut hash = [0; SALTED_NODE_ID_HASH_LEN];
|
||||||
|
let rng = SystemRandom::new();
|
||||||
|
rng.fill(&mut hash[0..4]).unwrap();
|
||||||
|
hash[4..].clone_from_slice(&node_id);
|
||||||
|
let d = digest::digest(&digest::SHA256, &hash);
|
||||||
|
hash[4..].clone_from_slice(&d.as_ref()[..16]);
|
||||||
Self {
|
Self {
|
||||||
node_id,
|
node_id,
|
||||||
|
salted_node_id_hash: hash,
|
||||||
payload,
|
payload,
|
||||||
key_pair,
|
key_pair,
|
||||||
trusted_keys,
|
trusted_keys,
|
||||||
|
@ -462,6 +489,14 @@ impl<P: Payload> InitState<P> {
|
||||||
Ok(P::read_from(Cursor::new(data.message()))?)
|
Ok(P::read_from(Cursor::new(data.message()))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_salted_node_id_hash(&self, hash: &SaltedNodeIdHash, node_id: NodeId) -> bool {
|
||||||
|
let mut h2 = [0; SALTED_NODE_ID_HASH_LEN];
|
||||||
|
h2[0..4].clone_from_slice(&hash[0..4]);
|
||||||
|
h2[4..].clone_from_slice(&node_id);
|
||||||
|
let d = digest::digest(&digest::SHA256, &h2);
|
||||||
|
hash == d.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn send_message(
|
fn send_message(
|
||||||
&mut self, stage: u8, ecdh_public_key: Option<EcdhPublicKey>, out: &mut MsgBuffer
|
&mut self, stage: u8, ecdh_public_key: Option<EcdhPublicKey>, out: &mut MsgBuffer
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
@ -472,20 +507,25 @@ impl<P: Payload> InitState<P> {
|
||||||
let msg = match stage {
|
let msg = match stage {
|
||||||
STAGE_PING => {
|
STAGE_PING => {
|
||||||
InitMsg::Ping {
|
InitMsg::Ping {
|
||||||
node_id: self.node_id,
|
salted_node_id_hash: self.salted_node_id_hash,
|
||||||
ecdh_public_key: ecdh_public_key.unwrap(),
|
ecdh_public_key: ecdh_public_key.unwrap(),
|
||||||
algorithms: self.algorithms.clone()
|
algorithms: self.algorithms.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
STAGE_PONG => {
|
STAGE_PONG => {
|
||||||
InitMsg::Pong {
|
InitMsg::Pong {
|
||||||
node_id: self.node_id,
|
salted_node_id_hash: self.salted_node_id_hash,
|
||||||
ecdh_public_key: ecdh_public_key.unwrap(),
|
ecdh_public_key: ecdh_public_key.unwrap(),
|
||||||
algorithms: self.algorithms.clone(),
|
algorithms: self.algorithms.clone(),
|
||||||
encrypted_payload: self.encrypt_payload()?
|
encrypted_payload: self.encrypt_payload()?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
STAGE_PENG => InitMsg::Peng { node_id: self.node_id, encrypted_payload: self.encrypt_payload()? },
|
STAGE_PENG => {
|
||||||
|
InitMsg::Peng {
|
||||||
|
salted_node_id_hash: self.salted_node_id_hash,
|
||||||
|
encrypted_payload: self.encrypt_payload()?
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
let mut bytes = out.buffer();
|
let mut bytes = out.buffer();
|
||||||
|
@ -535,16 +575,18 @@ impl<P: Payload> InitState<P> {
|
||||||
let (msg, _peer_key) = InitMsg::read_from(out.buffer(), &self.trusted_keys)?;
|
let (msg, _peer_key) = InitMsg::read_from(out.buffer(), &self.trusted_keys)?;
|
||||||
out.clear();
|
out.clear();
|
||||||
let stage = msg.stage();
|
let stage = msg.stage();
|
||||||
let node_id = msg.node_id();
|
let salted_node_id_hash = *msg.salted_node_id_hash();
|
||||||
debug!("Received init with stage={}, expected stage={}", stage, self.next_stage);
|
debug!("Received init with stage={}, expected stage={}", stage, self.next_stage);
|
||||||
if self.node_id == node_id {
|
if self.salted_node_id_hash == salted_node_id_hash
|
||||||
|
|| self.check_salted_node_id_hash(&salted_node_id_hash, self.node_id)
|
||||||
|
{
|
||||||
return Err(Error::CryptoInit("Connected to self"))
|
return Err(Error::CryptoInit("Connected to self"))
|
||||||
}
|
}
|
||||||
if stage != self.next_stage {
|
if stage != self.next_stage {
|
||||||
if self.next_stage == STAGE_PONG && stage == STAGE_PING {
|
if self.next_stage == STAGE_PONG && stage == STAGE_PING {
|
||||||
// special case for concurrent init messages in both directions
|
// special case for concurrent init messages in both directions
|
||||||
// the node with the higher node_id "wins" and gets to initialize the connection
|
// the node with the higher node_id "wins" and gets to initialize the connection
|
||||||
if node_id > self.node_id {
|
if salted_node_id_hash > self.salted_node_id_hash {
|
||||||
// reset to initial state
|
// reset to initial state
|
||||||
self.next_stage = STAGE_PING;
|
self.next_stage = STAGE_PING;
|
||||||
self.last_message = None;
|
self.last_message = None;
|
||||||
|
@ -571,7 +613,7 @@ impl<P: Payload> InitState<P> {
|
||||||
let algorithm = self.select_algorithm(&algorithms)?;
|
let algorithm = self.select_algorithm(&algorithms)?;
|
||||||
if let Some((algorithm, _speed)) = algorithm {
|
if let Some((algorithm, _speed)) = algorithm {
|
||||||
let master_key = self.derive_master_key(algorithm, my_ecdh_private_key, &ecdh_public_key);
|
let master_key = self.derive_master_key(algorithm, my_ecdh_private_key, &ecdh_public_key);
|
||||||
self.crypto = Some(CryptoCore::new(master_key, self.node_id > node_id));
|
self.crypto = Some(CryptoCore::new(master_key, self.salted_node_id_hash > salted_node_id_hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
// create and send stage 2 reply
|
// create and send stage 2 reply
|
||||||
|
@ -586,7 +628,7 @@ impl<P: Payload> InitState<P> {
|
||||||
let algorithm = self.select_algorithm(&algorithms)?;
|
let algorithm = self.select_algorithm(&algorithms)?;
|
||||||
if let Some((algorithm, _speed)) = algorithm {
|
if let Some((algorithm, _speed)) = algorithm {
|
||||||
let master_key = self.derive_master_key(algorithm, ecdh_private_key, &ecdh_public_key);
|
let master_key = self.derive_master_key(algorithm, ecdh_private_key, &ecdh_public_key);
|
||||||
self.crypto = Some(CryptoCore::new(master_key, self.node_id > node_id));
|
self.crypto = Some(CryptoCore::new(master_key, self.salted_node_id_hash > salted_node_id_hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt the payload
|
// decrypt the payload
|
||||||
|
@ -598,7 +640,7 @@ impl<P: Payload> InitState<P> {
|
||||||
|
|
||||||
self.next_stage = WAITING_TO_CLOSE;
|
self.next_stage = WAITING_TO_CLOSE;
|
||||||
self.close_time = 60;
|
self.close_time = 60;
|
||||||
Ok(InitResult::Success { peer_payload, node_id, is_initiator: true })
|
Ok(InitResult::Success { peer_payload, is_initiator: true })
|
||||||
}
|
}
|
||||||
InitMsg::Peng { mut encrypted_payload, .. } => {
|
InitMsg::Peng { mut encrypted_payload, .. } => {
|
||||||
// decrypt the payload
|
// decrypt the payload
|
||||||
|
@ -606,7 +648,7 @@ impl<P: Payload> InitState<P> {
|
||||||
self.decrypt(&mut encrypted_payload).map_err(|_| Error::CryptoInit("Failed to decrypt payload"))?;
|
self.decrypt(&mut encrypted_payload).map_err(|_| Error::CryptoInit("Failed to decrypt payload"))?;
|
||||||
|
|
||||||
self.next_stage = CLOSING; // force resend when receiving any message
|
self.next_stage = CLOSING; // force resend when receiving any message
|
||||||
Ok(InitResult::Success { peer_payload, node_id, is_initiator: false })
|
Ok(InitResult::Success { peer_payload, is_initiator: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -620,6 +662,7 @@ impl<P: Payload> InitState<P> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::types::NODE_ID_BYTES;
|
||||||
|
|
||||||
impl Payload for Vec<u8> {
|
impl Payload for Vec<u8> {
|
||||||
fn write_to(&self, buffer: &mut MsgBuffer) {
|
fn write_to(&self, buffer: &mut MsgBuffer) {
|
||||||
|
|
|
@ -127,10 +127,23 @@ impl Crypto {
|
||||||
Ok(Self { node_id, key_pair: Arc::new(key_pair), trusted_keys: Arc::new(trusted_keys), algorithms: algos })
|
Ok(Self { node_id, key_pair: Arc::new(key_pair), trusted_keys: Arc::new(trusted_keys), algorithms: algos })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_keypair() -> (String, String) {
|
pub fn generate_keypair(password: Option<&str>) -> (String, String) {
|
||||||
let rng = SystemRandom::new();
|
|
||||||
let mut bytes = [0; 32];
|
let mut bytes = [0; 32];
|
||||||
rng.fill(&mut bytes).unwrap();
|
match password {
|
||||||
|
None => {
|
||||||
|
let rng = SystemRandom::new();
|
||||||
|
rng.fill(&mut bytes).unwrap();
|
||||||
|
}
|
||||||
|
Some(password) => {
|
||||||
|
pbkdf2::derive(
|
||||||
|
pbkdf2::PBKDF2_HMAC_SHA256,
|
||||||
|
NonZeroU32::new(4096).unwrap(),
|
||||||
|
SALT,
|
||||||
|
password.as_bytes(),
|
||||||
|
&mut bytes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
let keypair = Ed25519KeyPair::from_seed_unchecked(&bytes).unwrap();
|
let keypair = Ed25519KeyPair::from_seed_unchecked(&bytes).unwrap();
|
||||||
let privkey = to_base62(&bytes);
|
let privkey = to_base62(&bytes);
|
||||||
let pubkey = to_base62(keypair.public_key().as_ref());
|
let pubkey = to_base62(keypair.public_key().as_ref());
|
||||||
|
@ -183,8 +196,8 @@ impl Crypto {
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum MessageResult<P: Payload> {
|
pub enum MessageResult<P: Payload> {
|
||||||
Message(u8),
|
Message(u8),
|
||||||
Initialized(NodeId, P),
|
Initialized(P),
|
||||||
InitializedWithReply(NodeId, P),
|
InitializedWithReply(P),
|
||||||
Reply,
|
Reply,
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -262,7 +275,7 @@ impl<P: Payload> PeerCrypto<P> {
|
||||||
}
|
}
|
||||||
match result {
|
match result {
|
||||||
InitResult::Continue => Ok(MessageResult::Reply),
|
InitResult::Continue => Ok(MessageResult::Reply),
|
||||||
InitResult::Success { peer_payload, node_id, is_initiator } => {
|
InitResult::Success { peer_payload, is_initiator } => {
|
||||||
self.core = self.get_init()?.take_core();
|
self.core = self.get_init()?.take_core();
|
||||||
if self.core.is_none() {
|
if self.core.is_none() {
|
||||||
self.unencrypted = true;
|
self.unencrypted = true;
|
||||||
|
@ -275,13 +288,13 @@ impl<P: Payload> PeerCrypto<P> {
|
||||||
}
|
}
|
||||||
if !is_initiator {
|
if !is_initiator {
|
||||||
if self.unencrypted {
|
if self.unencrypted {
|
||||||
return Ok(MessageResult::Initialized(node_id, peer_payload))
|
return Ok(MessageResult::Initialized(peer_payload))
|
||||||
}
|
}
|
||||||
assert!(!buffer.is_empty());
|
assert!(!buffer.is_empty());
|
||||||
buffer.prepend_byte(MESSAGE_TYPE_ROTATION);
|
buffer.prepend_byte(MESSAGE_TYPE_ROTATION);
|
||||||
self.encrypt_message(buffer)?;
|
self.encrypt_message(buffer)?;
|
||||||
}
|
}
|
||||||
Ok(MessageResult::InitializedWithReply(node_id, peer_payload))
|
Ok(MessageResult::InitializedWithReply(peer_payload))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,12 +428,12 @@ mod tests {
|
||||||
|
|
||||||
debug!("Node1 <- Node2");
|
debug!("Node1 <- Node2");
|
||||||
let res = node1.handle_message(&mut msg).unwrap();
|
let res = node1.handle_message(&mut msg).unwrap();
|
||||||
assert_eq!(res, MessageResult::InitializedWithReply(node2.node_id, vec![]));
|
assert_eq!(res, MessageResult::InitializedWithReply(vec![]));
|
||||||
assert!(!msg.is_empty());
|
assert!(!msg.is_empty());
|
||||||
|
|
||||||
debug!("Node1 -> Node2");
|
debug!("Node1 -> Node2");
|
||||||
let res = node2.handle_message(&mut msg).unwrap();
|
let res = node2.handle_message(&mut msg).unwrap();
|
||||||
assert_eq!(res, MessageResult::InitializedWithReply(node1.node_id, vec![]));
|
assert_eq!(res, MessageResult::InitializedWithReply(vec![]));
|
||||||
assert!(!msg.is_empty());
|
assert!(!msg.is_empty());
|
||||||
|
|
||||||
debug!("Node1 <- Node2");
|
debug!("Node1 <- Node2");
|
||||||
|
|
|
@ -226,7 +226,7 @@ fn main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if args.genkey {
|
if args.genkey {
|
||||||
let (privkey, pubkey) = Crypto::generate_keypair();
|
let (privkey, pubkey) = Crypto::generate_keypair(args.password.as_deref());
|
||||||
println!("Private key: {}\nPublic key: {}\n", privkey, pubkey);
|
println!("Private key: {}\nPublic key: {}\n", privkey, pubkey);
|
||||||
println!(
|
println!(
|
||||||
"Attention: Keep the private key secret and use only the public key on other nodes to establish trust."
|
"Attention: Keep the private key secret and use only the public key on other nodes to establish trust."
|
||||||
|
|
|
@ -33,12 +33,14 @@ pub struct PeerInfo {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct NodeInfo {
|
pub struct NodeInfo {
|
||||||
|
pub node_id: NodeId,
|
||||||
pub peers: PeerList,
|
pub peers: PeerList,
|
||||||
pub claims: RangeList,
|
pub claims: RangeList,
|
||||||
pub peer_timeout: Option<u16>
|
pub peer_timeout: Option<u16>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeInfo {
|
impl NodeInfo {
|
||||||
|
const PART_NODEID: u8 = 4;
|
||||||
const PART_CLAIMS: u8 = 2;
|
const PART_CLAIMS: u8 = 2;
|
||||||
const PART_END: u8 = 0;
|
const PART_END: u8 = 0;
|
||||||
const PART_PEERS: u8 = 1;
|
const PART_PEERS: u8 = 1;
|
||||||
|
@ -89,6 +91,7 @@ impl NodeInfo {
|
||||||
let mut peers = smallvec![];
|
let mut peers = smallvec![];
|
||||||
let mut claims = smallvec![];
|
let mut claims = smallvec![];
|
||||||
let mut peer_timeout = None;
|
let mut peer_timeout = None;
|
||||||
|
let mut node_id = None;
|
||||||
loop {
|
loop {
|
||||||
let part = r.read_u8().map_err(|_| Error::Message("Truncated message"))?;
|
let part = r.read_u8().map_err(|_| Error::Message("Truncated message"))?;
|
||||||
if part == Self::PART_END {
|
if part == Self::PART_END {
|
||||||
|
@ -105,6 +108,11 @@ impl NodeInfo {
|
||||||
peer_timeout =
|
peer_timeout =
|
||||||
Some(rp.read_u16::<NetworkEndian>().map_err(|_| Error::Message("Truncated message"))?)
|
Some(rp.read_u16::<NetworkEndian>().map_err(|_| Error::Message("Truncated message"))?)
|
||||||
}
|
}
|
||||||
|
Self::PART_NODEID => {
|
||||||
|
let mut data = [0; NODE_ID_BYTES];
|
||||||
|
rp.read_exact(&mut data).map_err(|_| Error::Message("Truncated message"))?;
|
||||||
|
node_id = Some(data);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut data = vec![0; part_len];
|
let mut data = vec![0; part_len];
|
||||||
rp.read_exact(&mut data).map_err(|_| Error::Message("Truncated message"))?;
|
rp.read_exact(&mut data).map_err(|_| Error::Message("Truncated message"))?;
|
||||||
|
@ -112,7 +120,11 @@ impl NodeInfo {
|
||||||
}
|
}
|
||||||
r = rp.into_inner();
|
r = rp.into_inner();
|
||||||
}
|
}
|
||||||
Ok(Self { peers, claims, peer_timeout })
|
let node_id = match node_id {
|
||||||
|
Some(node_id) => node_id,
|
||||||
|
None => return Err(Error::Message("Payload without node_id"))
|
||||||
|
};
|
||||||
|
Ok(Self { node_id, peers, claims, peer_timeout })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode<R: Read>(r: R) -> Result<Self, Error> {
|
pub fn decode<R: Read>(r: R) -> Result<Self, Error> {
|
||||||
|
@ -174,6 +186,9 @@ impl NodeInfo {
|
||||||
let len;
|
let len;
|
||||||
{
|
{
|
||||||
let mut cursor = Cursor::new(buffer.buffer());
|
let mut cursor = Cursor::new(buffer.buffer());
|
||||||
|
Self::encode_part(&mut cursor, Self::PART_NODEID, |cursor| {
|
||||||
|
cursor.write_all(&self.node_id)
|
||||||
|
})?;
|
||||||
Self::encode_part(&mut cursor, Self::PART_PEERS, |cursor| self.encode_peer_list_part(cursor))?;
|
Self::encode_part(&mut cursor, Self::PART_PEERS, |cursor| self.encode_peer_list_part(cursor))?;
|
||||||
Self::encode_part(&mut cursor, Self::PART_CLAIMS, |mut cursor| {
|
Self::encode_part(&mut cursor, Self::PART_CLAIMS, |mut cursor| {
|
||||||
for c in &self.claims {
|
for c in &self.claims {
|
||||||
|
|
Loading…
Reference in New Issue