mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 08:57:26 +02:00
183 lines
5.6 KiB
Rust
183 lines
5.6 KiB
Rust
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
|
|
|
|
use std::io::{Read, Write};
|
|
|
|
use crate::vl1::identity::Identity;
|
|
use crate::vl1::protocol::IDENTITY_FINGERPRINT_SIZE;
|
|
|
|
use zerotier_utils::varint;
|
|
|
|
/// A signed bundle of identity fingerprints of nodes through which a node might be reached (e.g. roots).
|
|
///
|
|
/// This can be sent by nodes to indicate which other nodes they wish to have used to reach them. Typically
|
|
/// these would be roots. It prevents a misbehaving or malicious root from pretending to host a node.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct CareOf {
|
|
pub timestamp: i64,
|
|
pub fingerprints: Vec<[u8; IDENTITY_FINGERPRINT_SIZE]>,
|
|
pub signature: Vec<u8>,
|
|
}
|
|
|
|
impl CareOf {
|
|
pub fn new(timestamp: i64) -> Self {
|
|
Self { timestamp, fingerprints: Vec::new(), signature: Vec::new() }
|
|
}
|
|
|
|
pub fn add_care_of(&mut self, id: &Identity) {
|
|
self.fingerprints.push(id.fingerprint);
|
|
}
|
|
|
|
fn to_bytes_internal(&self, include_signature: bool) -> Vec<u8> {
|
|
let mut v: Vec<u8> = Vec::with_capacity(128 + (self.fingerprints.len() * 48));
|
|
let _ = varint::write(&mut v, self.timestamp as u64);
|
|
let _ = varint::write(&mut v, self.fingerprints.len() as u64);
|
|
for f in self.fingerprints.iter() {
|
|
let _ = v.write_all(f);
|
|
}
|
|
let _ = varint::write(&mut v, 0); // flags, reserved for future use
|
|
let _ = varint::write(&mut v, 0); // extra bytes, reserved for future use
|
|
if include_signature {
|
|
let _ = varint::write(&mut v, self.signature.len() as u64);
|
|
let _ = v.write_all(self.signature.as_slice());
|
|
}
|
|
v
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
self.to_bytes_internal(true)
|
|
}
|
|
|
|
pub fn from_bytes(mut b: &[u8]) -> Option<CareOf> {
|
|
let mut f = move || -> std::io::Result<CareOf> {
|
|
let (timestamp, _) = varint::read(&mut b)?;
|
|
let mut care_of = CareOf {
|
|
timestamp: timestamp as i64,
|
|
fingerprints: Vec::new(),
|
|
signature: Vec::new(),
|
|
};
|
|
let (fingerprint_count, _) = varint::read(&mut b)?;
|
|
for _ in 0..fingerprint_count {
|
|
let mut tmp = [0_u8; IDENTITY_FINGERPRINT_SIZE];
|
|
b.read_exact(&mut tmp)?;
|
|
care_of.fingerprints.push(tmp);
|
|
}
|
|
let _ = varint::read(&mut b)?; // flags, currently ignored
|
|
let (extra_bytes, _) = varint::read(&mut b)?;
|
|
if extra_bytes > (b.len() as u64) {
|
|
return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
|
|
}
|
|
b = &b[(extra_bytes as usize)..];
|
|
let (signature_len, _) = varint::read(&mut b)?;
|
|
if signature_len > (b.len() as u64) {
|
|
return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
|
|
}
|
|
let _ = care_of.signature.write_all(&b[..(signature_len as usize)]);
|
|
return Ok(care_of);
|
|
};
|
|
if let Ok(care_of) = f() {
|
|
Some(care_of)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Sort, deduplicate, and sign this care-of packet.
|
|
///
|
|
/// The supplied identitiy must contain its secret keys. False is returned if there is an error.
|
|
pub fn sign(&mut self, signer: &Identity) -> bool {
|
|
self.fingerprints.sort_unstable();
|
|
self.fingerprints.dedup();
|
|
if let Some(sig) = signer.sign(self.to_bytes_internal(false).as_slice(), false) {
|
|
self.signature = sig;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
pub fn verify(&self, signer: &Identity) -> bool {
|
|
signer.verify(self.to_bytes_internal(false).as_slice(), self.signature.as_slice())
|
|
}
|
|
|
|
#[allow(unused)]
|
|
pub fn contains(&self, id: &Identity) -> bool {
|
|
self.fingerprints.binary_search(&id.fingerprint).is_ok()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::sync::mpsc;
|
|
|
|
use super::CareOf;
|
|
use super::Identity;
|
|
|
|
#[test]
|
|
fn add_contains() {
|
|
let (s, r) = mpsc::channel();
|
|
|
|
for _ in 0..10 {
|
|
let s2 = s.clone();
|
|
|
|
std::thread::spawn(move || {
|
|
let id = Identity::generate();
|
|
let mut c = CareOf::new(rand::random());
|
|
|
|
s2.send(!c.contains(&id)).unwrap();
|
|
c.add_care_of(&id);
|
|
s2.send(c.contains(&id)).unwrap();
|
|
});
|
|
}
|
|
|
|
for _ in 0..20 {
|
|
assert!(r.recv().unwrap());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn to_from_bytes() {
|
|
let (s, r) = mpsc::channel();
|
|
|
|
for _ in 0..10 {
|
|
let s2 = s.clone();
|
|
|
|
std::thread::spawn(move || {
|
|
let id = Identity::generate();
|
|
let mut c = CareOf::new(rand::random());
|
|
|
|
c.add_care_of(&id);
|
|
s2.send(c.eq(&CareOf::from_bytes(&c.to_bytes()).unwrap())).unwrap();
|
|
});
|
|
}
|
|
|
|
for _ in 0..10 {
|
|
assert!(r.recv().unwrap());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn sign_verify() {
|
|
let (s, r) = mpsc::channel();
|
|
|
|
for _ in 0..10 {
|
|
let s2 = s.clone();
|
|
|
|
std::thread::spawn(move || {
|
|
let id = Identity::generate();
|
|
let id2 = Identity::generate();
|
|
let mut c = CareOf::new(rand::random());
|
|
|
|
c.add_care_of(&id2);
|
|
|
|
s2.send(c.sign(&id)).unwrap();
|
|
s2.send(c.verify(&id)).unwrap();
|
|
});
|
|
}
|
|
|
|
for _ in 0..20 {
|
|
assert!(r.recv().unwrap());
|
|
}
|
|
}
|
|
}
|