Send salted hash of node_id

This commit is contained in:
Dennis Schwerdel 2020-09-28 12:50:08 +02:00
parent 941ac62bac
commit af7a7f6a29
6 changed files with 128 additions and 52 deletions

View File

@ -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)?,

View File

@ -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

View File

@ -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) {

View File

@ -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");

View File

@ -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."

View File

@ -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 {