mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-19 13:36:54 +02:00
Build fixes, add more parsing and serialization to Identity.
This commit is contained in:
parent
ec7fae71b4
commit
ed309b9f18
18 changed files with 333 additions and 244 deletions
|
@ -10,9 +10,8 @@ use tokio_postgres::types::Type;
|
|||
use tokio_postgres::{Client, Statement};
|
||||
|
||||
use zerotier_crypto::secure_eq;
|
||||
use zerotier_crypto::typestate::Valid;
|
||||
|
||||
use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress};
|
||||
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
|
||||
use zerotier_network_hypervisor::vl2::rule::Rule;
|
||||
use zerotier_network_hypervisor::vl2::{IpRoute, NetworkId};
|
||||
|
||||
|
@ -21,7 +20,6 @@ use zerotier_utils::tokio;
|
|||
use zerotier_utils::tokio::runtime::Handle;
|
||||
use zerotier_utils::tokio::sync::broadcast::{channel, Receiver, Sender};
|
||||
use zerotier_utils::tokio::task::JoinHandle;
|
||||
use zerotier_vl1_service::VL1DataStorage;
|
||||
|
||||
use crate::database::*;
|
||||
use crate::model::{IpAssignmentPool, Member, Network, RequestLogItem};
|
||||
|
@ -130,15 +128,14 @@ impl<'a> Drop for ConnectionHolder<'a> {
|
|||
}
|
||||
|
||||
pub struct PostgresDatabase {
|
||||
local_controller_id_str: String,
|
||||
local_identity: Valid<Identity>,
|
||||
local_controller: Address,
|
||||
connections: Mutex<(Vec<Box<PostgresConnection>>, Sender<()>)>,
|
||||
postgres_path: String,
|
||||
runtime: Handle,
|
||||
}
|
||||
|
||||
impl PostgresDatabase {
|
||||
pub async fn new(runtime: Handle, postgres_path: String, num_connections: usize, local_identity: Valid<Identity>) -> Result<Arc<Self>, Error> {
|
||||
pub async fn new(runtime: Handle, postgres_path: String, num_connections: usize) -> Result<Arc<Self>, Error> {
|
||||
assert!(num_connections > 0);
|
||||
let (sender, _) = channel(4096);
|
||||
let mut connections = Vec::with_capacity(num_connections);
|
||||
|
@ -147,7 +144,6 @@ impl PostgresDatabase {
|
|||
}
|
||||
Ok(Arc::new(Self {
|
||||
local_controller_id_str: local_identity.address.to_string(),
|
||||
local_identity,
|
||||
connections: Mutex::new((connections, sender)),
|
||||
postgres_path,
|
||||
runtime,
|
||||
|
@ -177,16 +173,6 @@ impl PostgresDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
impl VL1DataStorage for PostgresDatabase {
|
||||
fn load_node_identity(&self) -> Option<Valid<Identity>> {
|
||||
Some(self.local_identity.clone())
|
||||
}
|
||||
|
||||
fn save_node_identity(&self, _id: &Valid<Identity>) -> bool {
|
||||
panic!("local identity saving not supported by PostgresDatabase")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Database for PostgresDatabase {
|
||||
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {
|
|
@ -9,6 +9,7 @@ use tokio::time::{Duration, Instant};
|
|||
use zerotier_crypto::secure_eq;
|
||||
use zerotier_network_hypervisor::protocol;
|
||||
use zerotier_network_hypervisor::protocol::{PacketBuffer, DEFAULT_MULTICAST_LIMIT, ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU};
|
||||
use zerotier_network_hypervisor::vl1::identity::{Identity, IdentitySecret};
|
||||
use zerotier_network_hypervisor::vl1::*;
|
||||
use zerotier_network_hypervisor::vl2;
|
||||
use zerotier_network_hypervisor::vl2::multicastauthority::MulticastAuthority;
|
||||
|
@ -37,7 +38,6 @@ pub struct Controller {
|
|||
reaper: Reaper,
|
||||
runtime: tokio::runtime::Handle,
|
||||
database: Arc<dyn Database>,
|
||||
local_identity: Valid<Identity>,
|
||||
|
||||
/// Handler for MULTICAST_LIKE and MULTICAST_GATHER messages.
|
||||
multicast_authority: MulticastAuthority,
|
||||
|
@ -47,7 +47,7 @@ pub struct Controller {
|
|||
|
||||
/// Recently authorized network members and when that authorization expires (in monotonic ticks).
|
||||
/// Note that this is not and should not be used for real authentication, just for locking up multicast info.
|
||||
recently_authorized: RwLock<HashMap<[u8; Identity::FINGERPRINT_SIZE], HashMap<NetworkId, i64>>>,
|
||||
recently_authorized: RwLock<HashMap<Address, HashMap<NetworkId, i64>>>,
|
||||
}
|
||||
|
||||
impl Controller {
|
||||
|
@ -63,7 +63,6 @@ impl Controller {
|
|||
reaper: Reaper::new(&runtime),
|
||||
runtime,
|
||||
database: database.clone(),
|
||||
local_identity: local_identity,
|
||||
multicast_authority: MulticastAuthority::new(),
|
||||
daemons: Mutex::new(Vec::with_capacity(2)),
|
||||
recently_authorized: RwLock::new(HashMap::new()),
|
||||
|
@ -232,7 +231,7 @@ impl Controller {
|
|||
if member.node_id != *m {
|
||||
if let Some(peer) = self.service.read().unwrap().upgrade().and_then(|s| s.node().peer(*m)) {
|
||||
revocations.clear();
|
||||
Revocation::new(member.network_id, time_clock, member.node_id, *m, &self.local_identity, false).map(|r| revocations.push(r));
|
||||
Revocation::new(&member.network_id, time_clock, &member.node_id, m, &self.local_identity, false).map(|r| revocations.push(r));
|
||||
self.send_revocations(&peer, &mut revocations);
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +426,7 @@ impl Controller {
|
|||
// the overhead (bandwidth and CPU) of generating these.
|
||||
|
||||
if let Some(com) =
|
||||
vl2::v1::CertificateOfMembership::new(&self.local_identity, network_id, &source_identity, time_clock, credential_ttl)
|
||||
vl2::v1::CertificateOfMembership::new(&self.local_identity, &network_id, &source_identity, time_clock, credential_ttl)
|
||||
{
|
||||
let mut v1cred = V1Credentials {
|
||||
revision: time_clock as u64,
|
||||
|
@ -438,7 +437,7 @@ impl Controller {
|
|||
};
|
||||
|
||||
if !nc.static_ips.is_empty() {
|
||||
let mut coo = vl2::v1::CertificateOfOwnership::new(network_id, time_clock, source_identity.address);
|
||||
let mut coo = vl2::v1::CertificateOfOwnership::new(&network_id, time_clock, &source_identity.address);
|
||||
for ip in nc.static_ips.iter() {
|
||||
coo.add_ip(ip);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use zerotier_crypto::secure_eq;
|
|||
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
|
||||
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||
use zerotier_utils::tokio::sync::broadcast::Receiver;
|
||||
use zerotier_vl1_service::VL1DataStorage;
|
||||
|
||||
use crate::model::*;
|
||||
|
||||
|
@ -22,7 +21,7 @@ pub enum Change {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Database: Sync + Send + VL1DataStorage + 'static {
|
||||
pub trait Database: Sync + Send + 'static {
|
||||
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error>;
|
||||
async fn get_network(&self, id: NetworkId) -> Result<Option<Network>, Error>;
|
||||
async fn save_network(&self, obj: Network, generate_change_notification: bool) -> Result<(), Error>;
|
||||
|
|
|
@ -5,7 +5,7 @@ use async_trait::async_trait;
|
|||
use notify::{RecursiveMode, Watcher};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use zerotier_network_hypervisor::vl1::{Address, Identity, Valid};
|
||||
use zerotier_network_hypervisor::vl1::Address;
|
||||
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||
use zerotier_utils::reaper::Reaper;
|
||||
use zerotier_utils::tokio::fs;
|
||||
|
@ -13,8 +13,6 @@ use zerotier_utils::tokio::runtime::Handle;
|
|||
use zerotier_utils::tokio::sync::broadcast::{channel, Receiver, Sender};
|
||||
use zerotier_utils::tokio::task::JoinHandle;
|
||||
use zerotier_utils::tokio::time::{sleep, Duration, Instant};
|
||||
use zerotier_vl1_service::datadir::{load_node_identity, save_node_identity};
|
||||
use zerotier_vl1_service::VL1DataStorage;
|
||||
|
||||
use crate::cache::Cache;
|
||||
use crate::database::{Change, Database, Error};
|
||||
|
@ -32,7 +30,7 @@ const EVENT_HANDLER_TASK_TIMEOUT: Duration = Duration::from_secs(10);
|
|||
/// is different from V1 so it'll need a converter to use with V1 FileDb controller data.
|
||||
pub struct FileDatabase {
|
||||
base_path: PathBuf,
|
||||
local_identity: Valid<Identity>,
|
||||
controller_address: Address,
|
||||
change_sender: Sender<Change>,
|
||||
tasks: Reaper,
|
||||
cache: Cache,
|
||||
|
@ -42,7 +40,7 @@ pub struct FileDatabase {
|
|||
// TODO: should cache at least hashes and detect changes in the filesystem live.
|
||||
|
||||
impl FileDatabase {
|
||||
pub async fn new<P: AsRef<Path>>(runtime: Handle, base_path: P) -> Result<Arc<Self>, Error> {
|
||||
pub async fn new<P: AsRef<Path>>(runtime: Handle, base_path: P, controller_address: Address) -> Result<Arc<Self>, Error> {
|
||||
let base_path: PathBuf = base_path.as_ref().into();
|
||||
|
||||
let (change_sender, _) = channel(256);
|
||||
|
@ -50,13 +48,9 @@ impl FileDatabase {
|
|||
let db_weak = db_weak_tmp.clone();
|
||||
let runtime2 = runtime.clone();
|
||||
|
||||
let local_identity =
|
||||
load_node_identity(base_path.as_path()).ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "identity.secret not found"))?;
|
||||
let controller_address = local_identity.address;
|
||||
|
||||
let db = Arc::new(Self {
|
||||
base_path: base_path.clone(),
|
||||
local_identity,
|
||||
controller_address: controller_address.clone(),
|
||||
change_sender,
|
||||
tasks: Reaper::new(&runtime2),
|
||||
cache: Cache::new(),
|
||||
|
@ -246,16 +240,6 @@ impl Drop for FileDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
impl VL1DataStorage for FileDatabase {
|
||||
fn load_node_identity(&self) -> Option<Valid<Identity>> {
|
||||
load_node_identity(self.base_path.as_path())
|
||||
}
|
||||
|
||||
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
|
||||
save_node_identity(self.base_path.as_path(), id)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Database for FileDatabase {
|
||||
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {
|
||||
|
@ -362,6 +346,7 @@ mod tests {
|
|||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use zerotier_network_hypervisor::vl1::identity::Identity;
|
||||
|
||||
#[allow(unused)]
|
||||
#[test]
|
||||
|
@ -375,11 +360,14 @@ mod tests {
|
|||
println!("test filedatabase is in: {}", test_dir.as_os_str().to_str().unwrap());
|
||||
|
||||
let _ = std::fs::remove_dir_all(&test_dir);
|
||||
let controller_id = Identity::generate();
|
||||
let controller_id = Identity::generate(false);
|
||||
|
||||
assert!(fs::create_dir_all(&test_dir).await.is_ok());
|
||||
assert!(save_node_identity(test_dir.as_path(), &controller_id));
|
||||
let db = Arc::new(FileDatabase::new(tokio_runtime.handle().clone(), test_dir).await.expect("new db"));
|
||||
let db = Arc::new(
|
||||
FileDatabase::new(tokio_runtime.handle().clone(), test_dir, controller_id.public.address.clone())
|
||||
.await
|
||||
.expect("new db"),
|
||||
);
|
||||
|
||||
let change_count = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
|
|
|
@ -7,6 +7,5 @@ pub(crate) mod cache;
|
|||
pub mod database;
|
||||
pub mod filedatabase;
|
||||
pub mod model;
|
||||
pub mod postgresdatabase;
|
||||
|
||||
pub use controller::*;
|
||||
|
|
|
@ -5,7 +5,8 @@ use std::hash::Hash;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress};
|
||||
use zerotier_network_hypervisor::vl1::identity::Identity;
|
||||
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
|
||||
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||
use zerotier_utils::blob::Blob;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use zerotier_crypto::secret::Secret;
|
|||
use zerotier_crypto::typestate::Valid;
|
||||
use zerotier_crypto::x25519::*;
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
use zerotier_utils::base24;
|
||||
use zerotier_utils::base64;
|
||||
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
|
||||
use zerotier_utils::error::InvalidFormatError;
|
||||
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
|
||||
|
@ -123,9 +123,7 @@ impl Identity {
|
|||
}
|
||||
|
||||
// Bits 40-384 of the address are filled from a SHA384 hash of all keys for a full length V2 address.
|
||||
let mut address = secret.public.address.clone();
|
||||
secret.public.populate_extended_address_bits(&mut address);
|
||||
secret.public.address = address;
|
||||
secret.public.address = secret.public.populate_extended_address_bits();
|
||||
|
||||
// For V2 identities we include two self signatures to ensure that all these different key pairs
|
||||
// are properly bound together and can't be changed independently.
|
||||
|
@ -148,7 +146,7 @@ impl Identity {
|
|||
// First, check that the full SHA384 (bits 40-384) in the address is correct. Check first since this is fast.
|
||||
let mut test_address = Address::new_uninitialized();
|
||||
test_address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]);
|
||||
self.populate_extended_address_bits(&mut test_address);
|
||||
test_address = self.populate_extended_address_bits();
|
||||
if self.address != test_address {
|
||||
return None;
|
||||
}
|
||||
|
@ -190,8 +188,8 @@ impl Identity {
|
|||
}
|
||||
}
|
||||
|
||||
/// Populate bits 40-384 of the address with a hash of everything else.
|
||||
fn populate_extended_address_bits(&self, address: &mut Address) {
|
||||
/// Populate bits 40-384 of the address with a hash of everything else and return the completed address.
|
||||
fn populate_extended_address_bits(&self) -> Address {
|
||||
let mut sha = SHA384::new();
|
||||
sha.update(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]); // include short address in full hash
|
||||
sha.update(&[Self::ALGORITHM_X25519
|
||||
|
@ -207,7 +205,9 @@ impl Identity {
|
|||
sha.update(p384.ecdsa.as_bytes());
|
||||
}
|
||||
let sha = sha.finish();
|
||||
let mut address = self.address.clone();
|
||||
address.0[PartialAddress::LEGACY_SIZE_BYTES..].copy_from_slice(&sha[..Address::SIZE_BYTES - PartialAddress::LEGACY_SIZE_BYTES]);
|
||||
address
|
||||
}
|
||||
|
||||
/// Encode for self-signing, used only with p384 keys enabled and panics otherwise.
|
||||
|
@ -224,7 +224,6 @@ impl Identity {
|
|||
/// Decode a byte serialized identity.
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidFormatError> {
|
||||
let mut id;
|
||||
let mut address = Address::new_uninitialized();
|
||||
if b.len() == packed::V2_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == (Self::ALGORITHM_X25519 | Self::ALGORITHM_P384) {
|
||||
let p: &packed::V2Public = memory::cast_to_struct(b);
|
||||
id = Self {
|
||||
|
@ -237,7 +236,7 @@ impl Identity {
|
|||
p384_self_signature: p.p384_self_signature,
|
||||
}),
|
||||
};
|
||||
address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
} else if b.len() == packed::V1_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == Self::ALGORITHM_X25519 {
|
||||
let p: &packed::V1Public = memory::cast_to_struct(b);
|
||||
id = Self {
|
||||
|
@ -245,12 +244,11 @@ impl Identity {
|
|||
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
|
||||
p384: None,
|
||||
};
|
||||
address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
|
||||
} else {
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
id.populate_extended_address_bits(&mut address);
|
||||
id.address = address;
|
||||
id.address = id.populate_extended_address_bits();
|
||||
return Ok(id);
|
||||
}
|
||||
|
||||
|
@ -283,23 +281,23 @@ impl ToString for Identity {
|
|||
fn to_string(&self) -> String {
|
||||
if let Some(p384) = self.p384.as_ref() {
|
||||
let mut s = String::with_capacity(1024);
|
||||
base24::encode_into(self.address.as_bytes(), &mut s);
|
||||
s.push_str(self.address.to_string().as_str());
|
||||
s.push_str(":1:");
|
||||
base24::encode_into(&self.x25519.ecdh, &mut s);
|
||||
base64::encode_into(&self.x25519.ecdh, &mut s);
|
||||
s.push(':');
|
||||
base24::encode_into(&self.x25519.eddsa, &mut s);
|
||||
base64::encode_into(&self.x25519.eddsa, &mut s);
|
||||
s.push(':');
|
||||
base24::encode_into(p384.ecdh.as_bytes(), &mut s);
|
||||
base64::encode_into(p384.ecdh.as_bytes(), &mut s);
|
||||
s.push(':');
|
||||
base24::encode_into(p384.ecdsa.as_bytes(), &mut s);
|
||||
base64::encode_into(p384.ecdsa.as_bytes(), &mut s);
|
||||
s.push(':');
|
||||
base24::encode_into(&p384.ed25519_self_signature, &mut s);
|
||||
base64::encode_into(&p384.ed25519_self_signature, &mut s);
|
||||
s.push(':');
|
||||
base24::encode_into(&p384.p384_self_signature, &mut s);
|
||||
base64::encode_into(&p384.p384_self_signature, &mut s);
|
||||
s
|
||||
} else {
|
||||
format!(
|
||||
"{}:0:{}:{}",
|
||||
"{}:0:{}{}",
|
||||
hex::to_string(self.address.legacy_bytes()),
|
||||
hex::to_string(&self.x25519.ecdh),
|
||||
hex::to_string(&self.x25519.eddsa)
|
||||
|
@ -314,54 +312,56 @@ impl FromStr for Identity {
|
|||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let ss: Vec<&str> = s.split(':').collect();
|
||||
if ss.len() >= 2 {
|
||||
if ss[1] == "1" && ss.len() == 8 {
|
||||
if ss[1] == "1" && ss.len() >= 8 {
|
||||
return Ok(Self {
|
||||
address: Address::from_str(ss[0]).map_err(|_| InvalidFormatError)?,
|
||||
x25519: X25519 {
|
||||
ecdh: base24::decode(ss[2].as_bytes())
|
||||
ecdh: base64::decode(ss[2].as_bytes())
|
||||
.map_err(|_| InvalidFormatError)?
|
||||
.try_into()
|
||||
.map_err(|_| InvalidFormatError)?,
|
||||
eddsa: base24::decode(ss[3].as_bytes())
|
||||
eddsa: base64::decode(ss[3].as_bytes())
|
||||
.map_err(|_| InvalidFormatError)?
|
||||
.try_into()
|
||||
.map_err(|_| InvalidFormatError)?,
|
||||
},
|
||||
p384: Some(P384 {
|
||||
ecdh: P384PublicKey::from_bytes(base24::decode(ss[4].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
|
||||
ecdh: P384PublicKey::from_bytes(base64::decode(ss[4].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
|
||||
.ok_or(InvalidFormatError)?,
|
||||
ecdsa: P384PublicKey::from_bytes(base24::decode(ss[5].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
|
||||
ecdsa: P384PublicKey::from_bytes(base64::decode(ss[5].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
|
||||
.ok_or(InvalidFormatError)?,
|
||||
ed25519_self_signature: base24::decode(ss[6].as_bytes())
|
||||
ed25519_self_signature: base64::decode(ss[6].as_bytes())
|
||||
.map_err(|_| InvalidFormatError)?
|
||||
.try_into()
|
||||
.map_err(|_| InvalidFormatError)?,
|
||||
p384_self_signature: base24::decode(ss[7].as_bytes())
|
||||
p384_self_signature: base64::decode(ss[7].as_bytes())
|
||||
.map_err(|_| InvalidFormatError)?
|
||||
.try_into()
|
||||
.map_err(|_| InvalidFormatError)?,
|
||||
}),
|
||||
});
|
||||
} else if ss[1] == "0" && ss.len() == 4 {
|
||||
let mut address = {
|
||||
let legacy_address = hex::from_string(ss[0]);
|
||||
if legacy_address.len() != PartialAddress::LEGACY_SIZE_BYTES {
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
let mut tmp = [0u8; Address::SIZE_BYTES];
|
||||
tmp[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(legacy_address.as_slice());
|
||||
Address(tmp)
|
||||
};
|
||||
} else if ss[1] == "0" && ss.len() >= 3 {
|
||||
let ecdh_eddsa = hex::from_string(ss[2]);
|
||||
if ecdh_eddsa.len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
let mut a = Self {
|
||||
address: Address::new_uninitialized(),
|
||||
address: {
|
||||
let legacy_address = hex::from_string(ss[0]);
|
||||
if legacy_address.len() != PartialAddress::LEGACY_SIZE_BYTES {
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
let mut tmp = [0u8; Address::SIZE_BYTES];
|
||||
tmp[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(legacy_address.as_slice());
|
||||
Address(tmp)
|
||||
},
|
||||
x25519: X25519 {
|
||||
ecdh: hex::from_string(ss[2]).try_into().map_err(|_| InvalidFormatError)?,
|
||||
eddsa: hex::from_string(ss[3]).try_into().map_err(|_| InvalidFormatError)?,
|
||||
ecdh: ecdh_eddsa[..C25519_PUBLIC_KEY_SIZE].try_into().unwrap(),
|
||||
eddsa: ecdh_eddsa[C25519_PUBLIC_KEY_SIZE..].try_into().unwrap(),
|
||||
},
|
||||
p384: None,
|
||||
};
|
||||
a.populate_extended_address_bits(&mut address);
|
||||
a.address = address;
|
||||
a.address = a.populate_extended_address_bits();
|
||||
return Ok(a);
|
||||
}
|
||||
}
|
||||
|
@ -401,53 +401,6 @@ impl Marshalable for Identity {
|
|||
}
|
||||
}
|
||||
|
||||
impl IdentitySecret {
|
||||
pub fn sign(&self, data: &[u8]) -> ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }> {
|
||||
let mut s = ArrayVec::new();
|
||||
if let Some(p384) = self.p384.as_ref() {
|
||||
s.push_slice(&p384.sign(data));
|
||||
} else {
|
||||
s.push_slice(&self.x25519.sign(data));
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl X25519Secret {
|
||||
#[inline]
|
||||
pub fn agree(&self, public: &Identity) -> Option<Secret<64>> {
|
||||
Some(Secret(SHA512::hash(self.ecdh.agree(&public.x25519.ecdh).as_bytes())))
|
||||
}
|
||||
|
||||
/// Sign with Ed25519 using the legacy signature format used by ZeroTier V1.
|
||||
/// This just means the last 32 bytes of a 96-byte signature are the first 32 bytes of the
|
||||
/// SHA512 hash. This isn't used even in V1 but was once used long ago to rapidly check
|
||||
/// signatures as part of a different design. Some nodes still expect it to be there though.
|
||||
#[inline(always)]
|
||||
pub fn sign(&self, data: &[u8]) -> [u8; 96] {
|
||||
self.eddsa.sign_zt(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl P384Secret {
|
||||
#[inline(always)]
|
||||
pub fn sign(&self, data: &[u8]) -> [u8; P384_ECDSA_SIGNATURE_SIZE] {
|
||||
self.ecdsa.sign(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for P384 {}
|
||||
|
||||
impl PartialEq for P384 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ecdh.as_bytes() == other.ecdh.as_bytes()
|
||||
&& self.ecdsa.as_bytes() == other.ecdsa.as_bytes()
|
||||
&& self.ed25519_self_signature == other.ed25519_self_signature
|
||||
&& self.p384_self_signature == other.p384_self_signature
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Identity {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
@ -505,6 +458,127 @@ impl<'de> Deserialize<'de> for Identity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Eq for P384 {}
|
||||
|
||||
impl PartialEq for P384 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ecdh.as_bytes() == other.ecdh.as_bytes()
|
||||
&& self.ecdsa.as_bytes() == other.ecdsa.as_bytes()
|
||||
&& self.ed25519_self_signature == other.ed25519_self_signature
|
||||
&& self.p384_self_signature == other.p384_self_signature
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentitySecret {
|
||||
pub fn sign(&self, data: &[u8]) -> ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }> {
|
||||
let mut s = ArrayVec::new();
|
||||
if let Some(p384) = self.p384.as_ref() {
|
||||
s.push_slice(&p384.sign(data));
|
||||
} else {
|
||||
s.push_slice(&self.x25519.sign(data));
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IdentitySecret {
|
||||
fn to_string(&self) -> String {
|
||||
let mut s = self.public.to_string();
|
||||
if let Some(p384) = self.p384.as_ref() {
|
||||
s.push(':');
|
||||
base64::encode_into(self.x25519.ecdh.secret_bytes().as_bytes(), &mut s);
|
||||
s.push(':');
|
||||
base64::encode_into(self.x25519.eddsa.secret_bytes().as_bytes(), &mut s);
|
||||
s.push(':');
|
||||
base64::encode_into(p384.ecdh.secret_key_bytes().as_bytes(), &mut s);
|
||||
s.push(':');
|
||||
base64::encode_into(p384.ecdsa.secret_key_bytes().as_bytes(), &mut s);
|
||||
} else {
|
||||
s.push(':');
|
||||
s.push_str(hex::to_string(self.x25519.ecdh.secret_bytes().as_bytes()).as_str());
|
||||
s.push_str(hex::to_string(self.x25519.eddsa.secret_bytes().as_bytes()).as_str());
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for IdentitySecret {
|
||||
type Err = InvalidFormatError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let public = Identity::from_str(s)?;
|
||||
let ss: Vec<&str> = s.split(':').collect();
|
||||
if ss.len() >= 2 {
|
||||
if ss[1] == "1" && ss.len() >= 12 && public.p384.is_some() {
|
||||
let x25519_ecdh = X25519KeyPair::from_bytes(
|
||||
&public.x25519.ecdh,
|
||||
base64::decode(ss[8].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
|
||||
)
|
||||
.ok_or(InvalidFormatError)?;
|
||||
let x25519_eddsa = Ed25519KeyPair::from_bytes(
|
||||
&public.x25519.ecdh,
|
||||
base64::decode(ss[9].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
|
||||
)
|
||||
.ok_or(InvalidFormatError)?;
|
||||
let p384_ecdh = P384KeyPair::from_bytes(
|
||||
public.p384.as_ref().unwrap().ecdh.as_bytes(),
|
||||
base64::decode(ss[10].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
|
||||
)
|
||||
.ok_or(InvalidFormatError)?;
|
||||
let p384_ecdsa = P384KeyPair::from_bytes(
|
||||
public.p384.as_ref().unwrap().ecdh.as_bytes(),
|
||||
base64::decode(ss[11].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
|
||||
)
|
||||
.ok_or(InvalidFormatError)?;
|
||||
return Ok(Self {
|
||||
public: public.validate().ok_or(InvalidFormatError)?,
|
||||
x25519: X25519Secret { ecdh: x25519_ecdh, eddsa: x25519_eddsa },
|
||||
p384: Some(P384Secret { ecdh: p384_ecdh, ecdsa: p384_ecdsa }),
|
||||
});
|
||||
} else if ss[1] == "0" && ss.len() >= 4 && public.p384.is_none() {
|
||||
let ecdh_eddsa_secrets = hex::from_string(ss[3]);
|
||||
if ecdh_eddsa_secrets.len() != C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE {
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
let x25519_ecdh =
|
||||
X25519KeyPair::from_bytes(&public.x25519.ecdh, &ecdh_eddsa_secrets[..C25519_SECRET_KEY_SIZE]).ok_or(InvalidFormatError)?;
|
||||
let x25519_eddsa =
|
||||
Ed25519KeyPair::from_bytes(&public.x25519.ecdh, &ecdh_eddsa_secrets[C25519_SECRET_KEY_SIZE..]).ok_or(InvalidFormatError)?;
|
||||
return Ok(Self {
|
||||
public: public.validate().ok_or(InvalidFormatError)?,
|
||||
x25519: X25519Secret { ecdh: x25519_ecdh, eddsa: x25519_eddsa },
|
||||
p384: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
}
|
||||
|
||||
impl X25519Secret {
|
||||
#[inline]
|
||||
pub fn agree(&self, public: &Identity) -> Option<Secret<64>> {
|
||||
Some(Secret(SHA512::hash(self.ecdh.agree(&public.x25519.ecdh).as_bytes())))
|
||||
}
|
||||
|
||||
/// Sign with Ed25519 using the legacy signature format used by ZeroTier V1.
|
||||
/// This just means the last 32 bytes of a 96-byte signature are the first 32 bytes of the
|
||||
/// SHA512 hash. This isn't used even in V1 but was once used long ago to rapidly check
|
||||
/// signatures as part of a different design. Some nodes still expect it to be there though.
|
||||
#[inline(always)]
|
||||
pub fn sign(&self, data: &[u8]) -> [u8; 96] {
|
||||
self.eddsa.sign_zt(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl P384Secret {
|
||||
#[inline(always)]
|
||||
pub fn sign(&self, data: &[u8]) -> [u8; P384_ECDSA_SIGNATURE_SIZE] {
|
||||
self.ecdsa.sign(data)
|
||||
}
|
||||
}
|
||||
|
||||
/// The actual serialization format for secret identities.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct IdentitySecretForSerialization<'a> {
|
||||
|
@ -572,7 +646,7 @@ impl<'de> Deserialize<'de> for IdentitySecret {
|
|||
let e2 = || D::Error::custom("invalid key");
|
||||
let e = |_e: TryFromSliceError| e2();
|
||||
Ok(IdentitySecret {
|
||||
public: Valid::mark_valid(Identity {
|
||||
public: Identity {
|
||||
address: Address::from_bytes(tmp.address).map_err(|_| e2())?,
|
||||
x25519: X25519 {
|
||||
ecdh: x25519_ecdh_public.try_into().map_err(e)?,
|
||||
|
@ -588,7 +662,9 @@ impl<'de> Deserialize<'de> for IdentitySecret {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
}),
|
||||
}
|
||||
.validate()
|
||||
.ok_or_else(e2)?,
|
||||
x25519: X25519Secret {
|
||||
ecdh: X25519KeyPair::from_bytes(x25519_ecdh_public, x25519_ecdh_secret).ok_or_else(e2)?,
|
||||
eddsa: Ed25519KeyPair::from_bytes(x25519_eddsa_public, x25519_eddsa_secret).ok_or_else(e2)?,
|
||||
|
|
|
@ -218,7 +218,7 @@ struct BackgroundTaskIntervals {
|
|||
}
|
||||
|
||||
pub struct Node<Application: ApplicationLayer + ?Sized> {
|
||||
pub(super) identity_secret: IdentitySecret,
|
||||
pub identity: IdentitySecret,
|
||||
intervals: Mutex<BackgroundTaskIntervals>,
|
||||
paths: RwLock<HashMap<PathKey<'static, 'static, Application::LocalSocket>, Arc<Path<Application>>>>,
|
||||
pub(super) peers: PeerMap<Application>,
|
||||
|
@ -229,7 +229,7 @@ pub struct Node<Application: ApplicationLayer + ?Sized> {
|
|||
impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
||||
pub fn new(identity_secret: IdentitySecret) -> Self {
|
||||
Self {
|
||||
identity_secret,
|
||||
identity: identity_secret,
|
||||
intervals: Mutex::new(BackgroundTaskIntervals::default()),
|
||||
paths: RwLock::new(HashMap::new()),
|
||||
peers: PeerMap::new(),
|
||||
|
@ -244,11 +244,6 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn identity(&self) -> &Valid<Identity> {
|
||||
&self.identity_secret.public
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn peer(&self, a: &Address) -> Option<Arc<Peer<Application>>> {
|
||||
self.peers.get_exact(a)
|
||||
|
@ -353,7 +348,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
|||
|
||||
for (_, rs) in roots.sets.iter() {
|
||||
for m in rs.members.iter() {
|
||||
if m.identity.eq(&self.identity_secret.public) {
|
||||
if m.identity.eq(&self.identity.public) {
|
||||
let _ = my_root_sets
|
||||
.get_or_insert_with(|| Vec::new())
|
||||
.write_all(rs.to_buffer::<{ RootSet::MAX_MARSHAL_SIZE }>().unwrap().as_bytes());
|
||||
|
@ -367,7 +362,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
|||
if let Some(peer) = self.peers.get_exact(&m.identity.address) {
|
||||
new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect());
|
||||
} else {
|
||||
if let Some(peer) = Peer::new(&self.identity_secret, Valid::mark_valid(m.identity.clone()), time_ticks) {
|
||||
if let Some(peer) = Peer::new(&self.identity, Valid::mark_valid(m.identity.clone()), time_ticks) {
|
||||
new_roots.insert(self.peers.add(Arc::new(peer)).0, m.endpoints.as_ref().unwrap().iter().cloned().collect());
|
||||
} else {
|
||||
bad_identities.push(m.identity.clone());
|
||||
|
@ -584,7 +579,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
|||
if let Ok(dest) = PartialAddress::from_legacy_address_bytes(&fragment_header.dest) {
|
||||
// Packet is addressed to this node.
|
||||
|
||||
if dest.matches(&self.identity_secret.public.address) {
|
||||
if dest.matches(&self.identity.public.address) {
|
||||
let fragment_header = &*fragment_header; // discard mut
|
||||
let path = self.canonical_path(source_endpoint, source_local_socket, source_local_interface, time_ticks);
|
||||
path.log_receive_anything(time_ticks);
|
||||
|
|
|
@ -326,7 +326,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
aes_gmac_siv.encrypt_init(&self.v1_proto_next_message_id().to_be_bytes());
|
||||
aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes(
|
||||
&self.identity.address,
|
||||
&node.identity().address,
|
||||
&node.identity.public.address,
|
||||
flags_cipher_hops,
|
||||
));
|
||||
let payload = packet.as_bytes_starting_at_mut(v1::HEADER_SIZE).unwrap();
|
||||
|
@ -338,7 +338,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
||||
header.id.copy_from_slice(&tag[0..8]);
|
||||
header.dest = *self.identity.address.legacy_bytes();
|
||||
header.src = *node.identity().address.legacy_bytes();
|
||||
header.src = *node.identity.public.address.legacy_bytes();
|
||||
header.flags_cipher_hops = flags_cipher_hops;
|
||||
header.mac.copy_from_slice(&tag[8..16]);
|
||||
} else {
|
||||
|
@ -355,7 +355,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
||||
header.id = self.v1_proto_next_message_id().to_be_bytes();
|
||||
header.dest = *self.identity.address.legacy_bytes();
|
||||
header.src = *node.identity().address.legacy_bytes();
|
||||
header.src = *node.identity.public.address.legacy_bytes();
|
||||
header.flags_cipher_hops = flags_cipher_hops;
|
||||
header
|
||||
},
|
||||
|
@ -413,7 +413,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
let f: &mut (v1::PacketHeader, v1::message_component_structs::HelloFixedHeaderFields) = packet.append_struct_get_mut().unwrap();
|
||||
f.0.id = message_id.to_ne_bytes();
|
||||
f.0.dest = *self.identity.address.legacy_bytes();
|
||||
f.0.src = *node.identity().address.legacy_bytes();
|
||||
f.0.src = *node.identity.public.address.legacy_bytes();
|
||||
f.0.flags_cipher_hops = v1::CIPHER_NOCRYPT_POLY1305;
|
||||
f.1.verb = message_type::VL1_HELLO;
|
||||
f.1.version_proto = PROTOCOL_VERSION;
|
||||
|
@ -424,7 +424,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
}
|
||||
|
||||
debug_assert_eq!(packet.len(), 41);
|
||||
assert!(node.identity().write_bytes(packet.as_mut(), !self.is_v2()).is_ok());
|
||||
assert!(node.identity.public.write_bytes(packet.as_mut(), !self.is_v2()).is_ok());
|
||||
|
||||
let (_, poly1305_key) = v1_proto_salsa_poly_create(
|
||||
&self.v1_proto_static_secret,
|
||||
|
|
|
@ -154,10 +154,10 @@ impl RootSet {
|
|||
///
|
||||
/// All current members must sign whether they are disabled (witnessing) or active. The verify()
|
||||
/// method will return true when signing is complete.
|
||||
pub fn sign(&mut self, member_identity: &Identity, member_identity_secret: &IdentitySecret) -> bool {
|
||||
pub fn sign(&mut self, member_identity_secret: &IdentitySecret) -> bool {
|
||||
let signature = member_identity_secret.sign(self.marshal_for_signing().as_bytes());
|
||||
let unsigned_entry = self.members.iter().find_map(|m| {
|
||||
if m.identity.eq(member_identity) {
|
||||
if m.identity.eq(&member_identity_secret.public) {
|
||||
Some(m.clone())
|
||||
} else {
|
||||
None
|
||||
|
@ -165,7 +165,7 @@ impl RootSet {
|
|||
});
|
||||
if unsigned_entry.is_some() {
|
||||
let unsigned_entry = unsigned_entry.unwrap();
|
||||
self.members.retain(|m| !m.identity.eq(member_identity));
|
||||
self.members.retain(|m| !m.identity.eq(&member_identity_secret.public));
|
||||
let _ = self.members.push(Root {
|
||||
identity: unsigned_entry.identity,
|
||||
endpoints: unsigned_entry.endpoints,
|
||||
|
|
|
@ -65,7 +65,7 @@ impl<Application: ApplicationLayer + ?Sized> Whois<Application> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(peer) = node.peers.get_or_add(&node.identity_secret, &identity, time_ticks) {
|
||||
if let Some(peer) = node.peers.get_or_add(&node.identity, &identity, time_ticks) {
|
||||
for qi in queued_items.iter() {
|
||||
for pkt in qi.pending_v1_packets.iter() {
|
||||
if let Some(source_path) = pkt.0.upgrade() {
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::vl2::NetworkId;
|
|||
/// "Anti-credential" revoking a network member's permission to communicate on a network.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Revocation {
|
||||
pub network_id: NetworkId,
|
||||
pub network_id: u64, // legacy 64-bit network ID
|
||||
pub threshold: i64,
|
||||
pub target: u64, // legacy 40-bit address
|
||||
pub issued_to: u64, // legacy 40-bit address
|
||||
|
@ -22,7 +22,7 @@ pub struct Revocation {
|
|||
|
||||
impl Revocation {
|
||||
pub fn new(
|
||||
network_id: NetworkId,
|
||||
network_id: &NetworkId,
|
||||
threshold: i64,
|
||||
target: &Address,
|
||||
issued_to: &Address,
|
||||
|
@ -31,7 +31,7 @@ impl Revocation {
|
|||
fast_propagate: bool,
|
||||
) -> Self {
|
||||
let mut r = Self {
|
||||
network_id,
|
||||
network_id: network_id.to_legacy_u64(),
|
||||
threshold,
|
||||
target: target.legacy_u64(),
|
||||
issued_to: issued_to.legacy_u64(),
|
||||
|
@ -50,7 +50,7 @@ impl Revocation {
|
|||
|
||||
let _ = v.write_all(&[0; 4]);
|
||||
let _ = v.write_all(&((self.threshold as u32) ^ (self.target as u32)).to_be_bytes()); // ID is arbitrary
|
||||
let _ = v.write_all(&self.network_id.to_bytes());
|
||||
let _ = v.write_all(&self.network_id.to_be_bytes());
|
||||
let _ = v.write_all(&[0; 8]);
|
||||
let _ = v.write_all(&self.threshold.to_be_bytes());
|
||||
let _ = v.write_all(&(self.fast_propagate as u64).to_be_bytes()); // 0x1 is the flag for this
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use zerotier_network_hypervisor::vl1::Identity;
|
||||
use zerotier_network_hypervisor::vl1::identity::Identity;
|
||||
use zerotier_utils::io::read_limit;
|
||||
|
||||
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].
|
||||
|
|
|
@ -105,7 +105,7 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn base24_encode_decode() {
|
||||
fn encode_decode() {
|
||||
let mut tmp = [0xffu8; 256];
|
||||
for _ in 0..3 {
|
||||
let mut s = String::with_capacity(1024);
|
||||
|
|
115
utils/src/base64.rs
Normal file
115
utils/src/base64.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
use crate::error::InvalidParameterError;
|
||||
|
||||
/// URL-safe base64 alphabet
|
||||
const ALPHABET: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
const ALPHABET_INV: [u8; 256] = [
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
|
||||
255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 63,
|
||||
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
];
|
||||
|
||||
pub fn encode_into(mut b: &[u8], s: &mut String) {
|
||||
while b.len() >= 3 {
|
||||
let bits = (b[0] as usize) | (b[1] as usize).wrapping_shl(8) | (b[2] as usize).wrapping_shl(16);
|
||||
b = &b[3..];
|
||||
let (i0, i1, i2, i3) = (bits & 63, bits.wrapping_shr(6) & 63, bits.wrapping_shr(12) & 63, bits.wrapping_shr(18));
|
||||
s.push(ALPHABET[i0] as char);
|
||||
s.push(ALPHABET[i1] as char);
|
||||
s.push(ALPHABET[i2] as char);
|
||||
s.push(ALPHABET[i3] as char);
|
||||
}
|
||||
if b.len() == 2 {
|
||||
let bits = (b[0] as usize) | (b[1] as usize).wrapping_shl(8);
|
||||
s.push(ALPHABET[bits & 63] as char);
|
||||
s.push(ALPHABET[bits.wrapping_shr(6) & 63] as char);
|
||||
s.push(ALPHABET[bits.wrapping_shr(12)] as char);
|
||||
} else if b.len() == 1 {
|
||||
let bits = b[0] as usize;
|
||||
s.push(ALPHABET[bits & 63] as char);
|
||||
s.push(ALPHABET[bits.wrapping_shr(6)] as char);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_into(mut s: &[u8], b: &mut Vec<u8>) -> Result<(), InvalidParameterError> {
|
||||
while s.len() >= 4 {
|
||||
let (i0, i1, i2, i3) = (
|
||||
ALPHABET_INV[s[0] as usize],
|
||||
ALPHABET_INV[s[1] as usize],
|
||||
ALPHABET_INV[s[2] as usize],
|
||||
ALPHABET_INV[s[3] as usize],
|
||||
);
|
||||
s = &s[4..];
|
||||
if (i0 | i1 | i2 | i3) > 64 {
|
||||
return Err(InvalidParameterError("invalid base64 string"));
|
||||
}
|
||||
let bits = (i0 as usize) | (i1 as usize).wrapping_shl(6) | (i2 as usize).wrapping_shl(12) | (i3 as usize).wrapping_shl(18);
|
||||
b.push((bits & 0xff) as u8);
|
||||
b.push((bits.wrapping_shr(8) & 0xff) as u8);
|
||||
b.push((bits.wrapping_shr(16) & 0xff) as u8);
|
||||
}
|
||||
match s.len() {
|
||||
1 => return Err(InvalidParameterError("invalid base64 string")),
|
||||
2 => {
|
||||
let (i0, i1) = (ALPHABET_INV[s[0] as usize], ALPHABET_INV[s[1] as usize]);
|
||||
if (i0 | i1) > 64 {
|
||||
return Err(InvalidParameterError("invalid base64 string"));
|
||||
}
|
||||
let bits = (i0 as usize) | (i1 as usize).wrapping_shl(6);
|
||||
b.push((bits & 0xff) as u8);
|
||||
}
|
||||
3 => {
|
||||
let (i0, i1, i2) = (ALPHABET_INV[s[0] as usize], ALPHABET_INV[s[1] as usize], ALPHABET_INV[s[2] as usize]);
|
||||
if (i0 | i1 | i2) > 64 {
|
||||
return Err(InvalidParameterError("invalid base64 string"));
|
||||
}
|
||||
let bits = (i0 as usize) | (i1 as usize).wrapping_shl(6) | (i2 as usize).wrapping_shl(12);
|
||||
b.push((bits & 0xff) as u8);
|
||||
b.push((bits.wrapping_shr(8) & 0xff) as u8);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn encode(b: &[u8]) -> String {
|
||||
let mut tmp = String::with_capacity(((b.len() / 3) * 4) + 3);
|
||||
encode_into(b, &mut tmp);
|
||||
tmp
|
||||
}
|
||||
|
||||
pub fn decode(s: &[u8]) -> Result<Vec<u8>, InvalidParameterError> {
|
||||
let mut tmp = Vec::with_capacity(((s.len() / 4) * 3) + 3);
|
||||
decode_into(s, &mut tmp)?;
|
||||
Ok(tmp)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn encode_decode() {
|
||||
let mut tmp = [0xffu8; 256];
|
||||
for _ in 0..7 {
|
||||
let mut s = String::with_capacity(1024);
|
||||
let mut v: Vec<u8> = Vec::with_capacity(256);
|
||||
for i in 1..256 {
|
||||
s.clear();
|
||||
encode_into(&tmp[..i], &mut s);
|
||||
//println!("{}", s);
|
||||
v.clear();
|
||||
decode_into(s.as_str().as_bytes(), &mut v).expect("decode error");
|
||||
assert!(v.as_slice().eq(&tmp[..i]));
|
||||
}
|
||||
for b in tmp.iter_mut() {
|
||||
*b -= 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
pub mod arrayvec;
|
||||
pub mod base24;
|
||||
pub mod base64;
|
||||
pub mod blob;
|
||||
pub mod buffer;
|
||||
pub mod cast;
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use zerotier_crypto::random::next_u32_secure;
|
||||
use zerotier_network_hypervisor::vl1::{Identity, Valid};
|
||||
use zerotier_utils::io::{fs_restrict_permissions, read_limit, DEFAULT_FILE_IO_READ_LIMIT};
|
||||
use zerotier_utils::json::to_json_pretty;
|
||||
|
||||
use crate::vl1service::VL1DataStorage;
|
||||
|
||||
pub const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
|
||||
pub const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
|
||||
pub const IDENTITY_SECRET_FILENAME: &'static str = "identity.secret";
|
||||
|
@ -22,46 +18,12 @@ pub const CONFIG_FILENAME: &'static str = "local.conf";
|
|||
const AUTH_TOKEN_DEFAULT_LENGTH: usize = 48;
|
||||
const AUTH_TOKEN_POSSIBLE_CHARS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
pub fn load_node_identity(base_path: &Path) -> Option<Valid<Identity>> {
|
||||
let id_data = read_limit(base_path.join(IDENTITY_SECRET_FILENAME), 4096);
|
||||
if id_data.is_err() {
|
||||
return None;
|
||||
}
|
||||
let id_data = Identity::from_str(String::from_utf8_lossy(id_data.unwrap().as_slice()).as_ref());
|
||||
if id_data.is_err() {
|
||||
return None;
|
||||
}
|
||||
Some(Valid::mark_valid(id_data.unwrap()))
|
||||
}
|
||||
|
||||
pub fn save_node_identity(base_path: &Path, id: &Valid<Identity>) -> bool {
|
||||
assert!(id.secret.is_some());
|
||||
let id_secret_str = id.to_secret_string();
|
||||
let id_public_str = id.to_string();
|
||||
let secret_path = base_path.join(IDENTITY_SECRET_FILENAME);
|
||||
if std::fs::write(&secret_path, id_secret_str.as_bytes()).is_err() {
|
||||
return false;
|
||||
}
|
||||
assert!(fs_restrict_permissions(&secret_path));
|
||||
return std::fs::write(base_path.join(IDENTITY_PUBLIC_FILENAME), id_public_str.as_bytes()).is_ok();
|
||||
}
|
||||
|
||||
pub struct DataDir<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> {
|
||||
pub base_path: PathBuf,
|
||||
config: RwLock<Arc<Config>>,
|
||||
authtoken: Mutex<String>,
|
||||
}
|
||||
|
||||
impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> VL1DataStorage for DataDir<Config> {
|
||||
fn load_node_identity(&self) -> Option<Valid<Identity>> {
|
||||
load_node_identity(self.base_path.as_path())
|
||||
}
|
||||
|
||||
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
|
||||
save_node_identity(self.base_path.as_path(), id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> DataDir<Config> {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
|
||||
let base_path = path.as_ref().to_path_buf();
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::time::Duration;
|
|||
|
||||
use zerotier_crypto::random;
|
||||
use zerotier_network_hypervisor::protocol::{PacketBufferFactory, PacketBufferPool};
|
||||
use zerotier_network_hypervisor::vl1::identity::IdentitySecret;
|
||||
use zerotier_network_hypervisor::vl1::*;
|
||||
use zerotier_utils::{ms_monotonic, ms_since_epoch};
|
||||
|
||||
|
@ -20,12 +21,6 @@ use crate::LocalSocket;
|
|||
/// Update UDP bindings every this many seconds.
|
||||
const UPDATE_UDP_BINDINGS_EVERY_SECS: usize = 10;
|
||||
|
||||
/// Trait to implement to provide storage for VL1-related state information.
|
||||
pub trait VL1DataStorage: Sync + Send {
|
||||
fn load_node_identity(&self) -> Option<Valid<Identity>>;
|
||||
fn save_node_identity(&self, id: &Valid<Identity>) -> bool;
|
||||
}
|
||||
|
||||
/// VL1 service that connects to the physical network and hosts an inner protocol like ZeroTier VL2.
|
||||
///
|
||||
/// This is the "outward facing" half of a full ZeroTier stack on a normal system. It binds sockets,
|
||||
|
@ -33,11 +28,10 @@ pub trait VL1DataStorage: Sync + Send {
|
|||
/// whatever inner protocol implementation is using it. This would typically be VL2 but could be
|
||||
/// a test harness or just the controller for a controller that runs stand-alone.
|
||||
pub struct VL1Service<Inner: InnerProtocolLayer + ?Sized + 'static> {
|
||||
pub node: Node<Self>,
|
||||
state: RwLock<VL1ServiceMutableState>,
|
||||
vl1_data_storage: Arc<dyn VL1DataStorage>,
|
||||
inner: Arc<Inner>,
|
||||
buffer_pool: Arc<PacketBufferPool>,
|
||||
node_container: Option<Node<Self>>, // never None, set in new()
|
||||
}
|
||||
|
||||
struct VL1ServiceMutableState {
|
||||
|
@ -48,25 +42,21 @@ struct VL1ServiceMutableState {
|
|||
}
|
||||
|
||||
impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
|
||||
pub fn new(vl1_data_storage: Arc<dyn VL1DataStorage>, inner: Arc<Inner>, settings: VL1Settings) -> Result<Arc<Self>, Box<dyn Error>> {
|
||||
let mut service = Self {
|
||||
pub fn new(identity: IdentitySecret, inner: Arc<Inner>, settings: VL1Settings) -> Result<Arc<Self>, Box<dyn Error>> {
|
||||
let service = Arc::new(Self {
|
||||
node: Node::<Self>::new(identity),
|
||||
state: RwLock::new(VL1ServiceMutableState {
|
||||
daemons: Vec::with_capacity(2),
|
||||
udp_sockets: HashMap::with_capacity(8),
|
||||
settings,
|
||||
running: true,
|
||||
}),
|
||||
vl1_data_storage,
|
||||
inner,
|
||||
buffer_pool: Arc::new(PacketBufferPool::new(
|
||||
std::thread::available_parallelism().map_or(2, |c| c.get() + 2),
|
||||
PacketBufferFactory::new(),
|
||||
)),
|
||||
node_container: None,
|
||||
};
|
||||
|
||||
service.node_container.replace(Node::new(&service, true, false)?);
|
||||
let service = Arc::new(service);
|
||||
});
|
||||
|
||||
let mut daemons = Vec::new();
|
||||
let s = service.clone();
|
||||
|
@ -78,12 +68,6 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
|
|||
Ok(service)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn node(&self) -> &Node<Self> {
|
||||
debug_assert!(self.node_container.is_some());
|
||||
unsafe { self.node_container.as_ref().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
pub fn bound_udp_ports(&self) -> Vec<u16> {
|
||||
self.state.read().unwrap().udp_sockets.keys().cloned().collect()
|
||||
}
|
||||
|
@ -173,7 +157,7 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
|
|||
self.update_udp_bindings();
|
||||
}
|
||||
udp_binding_check_every = udp_binding_check_every.wrapping_add(1);
|
||||
std::thread::sleep(self.node().do_background_tasks(self.as_ref()));
|
||||
std::thread::sleep(self.node.do_background_tasks(self.as_ref()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +171,7 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> UdpPacketHandler for VL1Servi
|
|||
source_address: &InetAddress,
|
||||
packet: zerotier_network_hypervisor::protocol::PooledPacketBuffer,
|
||||
) {
|
||||
self.node().handle_incoming_physical_packet(
|
||||
self.node.handle_incoming_physical_packet(
|
||||
self.as_ref(),
|
||||
self.inner.as_ref(),
|
||||
&Endpoint::IpUdp(source_address.clone()),
|
||||
|
@ -216,22 +200,6 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> ApplicationLayer for VL1Servi
|
|||
socket.is_valid()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn should_respond_to(&self, _: &Valid<Identity>) -> bool {
|
||||
// TODO: provide a way for the user of VL1Service to control this
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn load_node_identity(&self) -> Option<Valid<Identity>> {
|
||||
self.vl1_data_storage.load_node_identity()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
|
||||
self.vl1_data_storage.save_node_identity(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_buffer(&self) -> zerotier_network_hypervisor::protocol::PooledPacketBuffer {
|
||||
self.buffer_pool.get()
|
||||
|
|
Loading…
Add table
Reference in a new issue