mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-06 12:33:44 +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 tokio_postgres::{Client, Statement};
|
||||||
|
|
||||||
use zerotier_crypto::secure_eq;
|
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::rule::Rule;
|
||||||
use zerotier_network_hypervisor::vl2::{IpRoute, NetworkId};
|
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::runtime::Handle;
|
||||||
use zerotier_utils::tokio::sync::broadcast::{channel, Receiver, Sender};
|
use zerotier_utils::tokio::sync::broadcast::{channel, Receiver, Sender};
|
||||||
use zerotier_utils::tokio::task::JoinHandle;
|
use zerotier_utils::tokio::task::JoinHandle;
|
||||||
use zerotier_vl1_service::VL1DataStorage;
|
|
||||||
|
|
||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
use crate::model::{IpAssignmentPool, Member, Network, RequestLogItem};
|
use crate::model::{IpAssignmentPool, Member, Network, RequestLogItem};
|
||||||
|
@ -130,15 +128,14 @@ impl<'a> Drop for ConnectionHolder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PostgresDatabase {
|
pub struct PostgresDatabase {
|
||||||
local_controller_id_str: String,
|
local_controller: Address,
|
||||||
local_identity: Valid<Identity>,
|
|
||||||
connections: Mutex<(Vec<Box<PostgresConnection>>, Sender<()>)>,
|
connections: Mutex<(Vec<Box<PostgresConnection>>, Sender<()>)>,
|
||||||
postgres_path: String,
|
postgres_path: String,
|
||||||
runtime: Handle,
|
runtime: Handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PostgresDatabase {
|
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);
|
assert!(num_connections > 0);
|
||||||
let (sender, _) = channel(4096);
|
let (sender, _) = channel(4096);
|
||||||
let mut connections = Vec::with_capacity(num_connections);
|
let mut connections = Vec::with_capacity(num_connections);
|
||||||
|
@ -147,7 +144,6 @@ impl PostgresDatabase {
|
||||||
}
|
}
|
||||||
Ok(Arc::new(Self {
|
Ok(Arc::new(Self {
|
||||||
local_controller_id_str: local_identity.address.to_string(),
|
local_controller_id_str: local_identity.address.to_string(),
|
||||||
local_identity,
|
|
||||||
connections: Mutex::new((connections, sender)),
|
connections: Mutex::new((connections, sender)),
|
||||||
postgres_path,
|
postgres_path,
|
||||||
runtime,
|
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]
|
#[async_trait]
|
||||||
impl Database for PostgresDatabase {
|
impl Database for PostgresDatabase {
|
||||||
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {
|
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_crypto::secure_eq;
|
||||||
use zerotier_network_hypervisor::protocol;
|
use zerotier_network_hypervisor::protocol;
|
||||||
use zerotier_network_hypervisor::protocol::{PacketBuffer, DEFAULT_MULTICAST_LIMIT, ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU};
|
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::vl1::*;
|
||||||
use zerotier_network_hypervisor::vl2;
|
use zerotier_network_hypervisor::vl2;
|
||||||
use zerotier_network_hypervisor::vl2::multicastauthority::MulticastAuthority;
|
use zerotier_network_hypervisor::vl2::multicastauthority::MulticastAuthority;
|
||||||
|
@ -37,7 +38,6 @@ pub struct Controller {
|
||||||
reaper: Reaper,
|
reaper: Reaper,
|
||||||
runtime: tokio::runtime::Handle,
|
runtime: tokio::runtime::Handle,
|
||||||
database: Arc<dyn Database>,
|
database: Arc<dyn Database>,
|
||||||
local_identity: Valid<Identity>,
|
|
||||||
|
|
||||||
/// Handler for MULTICAST_LIKE and MULTICAST_GATHER messages.
|
/// Handler for MULTICAST_LIKE and MULTICAST_GATHER messages.
|
||||||
multicast_authority: MulticastAuthority,
|
multicast_authority: MulticastAuthority,
|
||||||
|
@ -47,7 +47,7 @@ pub struct Controller {
|
||||||
|
|
||||||
/// Recently authorized network members and when that authorization expires (in monotonic ticks).
|
/// 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.
|
/// 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 {
|
impl Controller {
|
||||||
|
@ -63,7 +63,6 @@ impl Controller {
|
||||||
reaper: Reaper::new(&runtime),
|
reaper: Reaper::new(&runtime),
|
||||||
runtime,
|
runtime,
|
||||||
database: database.clone(),
|
database: database.clone(),
|
||||||
local_identity: local_identity,
|
|
||||||
multicast_authority: MulticastAuthority::new(),
|
multicast_authority: MulticastAuthority::new(),
|
||||||
daemons: Mutex::new(Vec::with_capacity(2)),
|
daemons: Mutex::new(Vec::with_capacity(2)),
|
||||||
recently_authorized: RwLock::new(HashMap::new()),
|
recently_authorized: RwLock::new(HashMap::new()),
|
||||||
|
@ -232,7 +231,7 @@ impl Controller {
|
||||||
if member.node_id != *m {
|
if member.node_id != *m {
|
||||||
if let Some(peer) = self.service.read().unwrap().upgrade().and_then(|s| s.node().peer(*m)) {
|
if let Some(peer) = self.service.read().unwrap().upgrade().and_then(|s| s.node().peer(*m)) {
|
||||||
revocations.clear();
|
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);
|
self.send_revocations(&peer, &mut revocations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,7 +426,7 @@ impl Controller {
|
||||||
// the overhead (bandwidth and CPU) of generating these.
|
// the overhead (bandwidth and CPU) of generating these.
|
||||||
|
|
||||||
if let Some(com) =
|
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 {
|
let mut v1cred = V1Credentials {
|
||||||
revision: time_clock as u64,
|
revision: time_clock as u64,
|
||||||
|
@ -438,7 +437,7 @@ impl Controller {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !nc.static_ips.is_empty() {
|
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() {
|
for ip in nc.static_ips.iter() {
|
||||||
coo.add_ip(ip);
|
coo.add_ip(ip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use zerotier_crypto::secure_eq;
|
||||||
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
|
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
|
||||||
use zerotier_network_hypervisor::vl2::NetworkId;
|
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||||
use zerotier_utils::tokio::sync::broadcast::Receiver;
|
use zerotier_utils::tokio::sync::broadcast::Receiver;
|
||||||
use zerotier_vl1_service::VL1DataStorage;
|
|
||||||
|
|
||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ pub enum Change {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[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 list_networks(&self) -> Result<Vec<NetworkId>, Error>;
|
||||||
async fn get_network(&self, id: NetworkId) -> Result<Option<Network>, 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>;
|
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 notify::{RecursiveMode, Watcher};
|
||||||
use serde::de::DeserializeOwned;
|
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_network_hypervisor::vl2::NetworkId;
|
||||||
use zerotier_utils::reaper::Reaper;
|
use zerotier_utils::reaper::Reaper;
|
||||||
use zerotier_utils::tokio::fs;
|
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::sync::broadcast::{channel, Receiver, Sender};
|
||||||
use zerotier_utils::tokio::task::JoinHandle;
|
use zerotier_utils::tokio::task::JoinHandle;
|
||||||
use zerotier_utils::tokio::time::{sleep, Duration, Instant};
|
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::cache::Cache;
|
||||||
use crate::database::{Change, Database, Error};
|
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.
|
/// is different from V1 so it'll need a converter to use with V1 FileDb controller data.
|
||||||
pub struct FileDatabase {
|
pub struct FileDatabase {
|
||||||
base_path: PathBuf,
|
base_path: PathBuf,
|
||||||
local_identity: Valid<Identity>,
|
controller_address: Address,
|
||||||
change_sender: Sender<Change>,
|
change_sender: Sender<Change>,
|
||||||
tasks: Reaper,
|
tasks: Reaper,
|
||||||
cache: Cache,
|
cache: Cache,
|
||||||
|
@ -42,7 +40,7 @@ pub struct FileDatabase {
|
||||||
// TODO: should cache at least hashes and detect changes in the filesystem live.
|
// TODO: should cache at least hashes and detect changes in the filesystem live.
|
||||||
|
|
||||||
impl FileDatabase {
|
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 base_path: PathBuf = base_path.as_ref().into();
|
||||||
|
|
||||||
let (change_sender, _) = channel(256);
|
let (change_sender, _) = channel(256);
|
||||||
|
@ -50,13 +48,9 @@ impl FileDatabase {
|
||||||
let db_weak = db_weak_tmp.clone();
|
let db_weak = db_weak_tmp.clone();
|
||||||
let runtime2 = runtime.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 {
|
let db = Arc::new(Self {
|
||||||
base_path: base_path.clone(),
|
base_path: base_path.clone(),
|
||||||
local_identity,
|
controller_address: controller_address.clone(),
|
||||||
change_sender,
|
change_sender,
|
||||||
tasks: Reaper::new(&runtime2),
|
tasks: Reaper::new(&runtime2),
|
||||||
cache: Cache::new(),
|
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]
|
#[async_trait]
|
||||||
impl Database for FileDatabase {
|
impl Database for FileDatabase {
|
||||||
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {
|
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {
|
||||||
|
@ -362,6 +346,7 @@ mod tests {
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use zerotier_network_hypervisor::vl1::identity::Identity;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -375,11 +360,14 @@ mod tests {
|
||||||
println!("test filedatabase is in: {}", test_dir.as_os_str().to_str().unwrap());
|
println!("test filedatabase is in: {}", test_dir.as_os_str().to_str().unwrap());
|
||||||
|
|
||||||
let _ = std::fs::remove_dir_all(&test_dir);
|
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!(fs::create_dir_all(&test_dir).await.is_ok());
|
||||||
assert!(save_node_identity(test_dir.as_path(), &controller_id));
|
let db = Arc::new(
|
||||||
let db = Arc::new(FileDatabase::new(tokio_runtime.handle().clone(), test_dir).await.expect("new db"));
|
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));
|
let change_count = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,5 @@ pub(crate) mod cache;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod filedatabase;
|
pub mod filedatabase;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod postgresdatabase;
|
|
||||||
|
|
||||||
pub use controller::*;
|
pub use controller::*;
|
||||||
|
|
|
@ -5,7 +5,8 @@ use std::hash::Hash;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
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_network_hypervisor::vl2::NetworkId;
|
||||||
use zerotier_utils::blob::Blob;
|
use zerotier_utils::blob::Blob;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ use zerotier_crypto::secret::Secret;
|
||||||
use zerotier_crypto::typestate::Valid;
|
use zerotier_crypto::typestate::Valid;
|
||||||
use zerotier_crypto::x25519::*;
|
use zerotier_crypto::x25519::*;
|
||||||
use zerotier_utils::arrayvec::ArrayVec;
|
use zerotier_utils::arrayvec::ArrayVec;
|
||||||
use zerotier_utils::base24;
|
use zerotier_utils::base64;
|
||||||
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
|
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
|
||||||
use zerotier_utils::error::InvalidFormatError;
|
use zerotier_utils::error::InvalidFormatError;
|
||||||
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
|
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.
|
// 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.address = secret.public.populate_extended_address_bits();
|
||||||
secret.public.populate_extended_address_bits(&mut address);
|
|
||||||
secret.public.address = address;
|
|
||||||
|
|
||||||
// For V2 identities we include two self signatures to ensure that all these different key pairs
|
// 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.
|
// 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.
|
// 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();
|
let mut test_address = Address::new_uninitialized();
|
||||||
test_address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]);
|
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 {
|
if self.address != test_address {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -190,8 +188,8 @@ impl Identity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Populate bits 40-384 of the address with a hash of everything else.
|
/// 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: &mut Address) {
|
fn populate_extended_address_bits(&self) -> Address {
|
||||||
let mut sha = SHA384::new();
|
let mut sha = SHA384::new();
|
||||||
sha.update(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]); // include short address in full hash
|
sha.update(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]); // include short address in full hash
|
||||||
sha.update(&[Self::ALGORITHM_X25519
|
sha.update(&[Self::ALGORITHM_X25519
|
||||||
|
@ -207,7 +205,9 @@ impl Identity {
|
||||||
sha.update(p384.ecdsa.as_bytes());
|
sha.update(p384.ecdsa.as_bytes());
|
||||||
}
|
}
|
||||||
let sha = sha.finish();
|
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.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.
|
/// Encode for self-signing, used only with p384 keys enabled and panics otherwise.
|
||||||
|
@ -224,7 +224,6 @@ impl Identity {
|
||||||
/// Decode a byte serialized identity.
|
/// Decode a byte serialized identity.
|
||||||
pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidFormatError> {
|
pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidFormatError> {
|
||||||
let mut id;
|
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) {
|
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);
|
let p: &packed::V2Public = memory::cast_to_struct(b);
|
||||||
id = Self {
|
id = Self {
|
||||||
|
@ -237,7 +236,7 @@ impl Identity {
|
||||||
p384_self_signature: p.p384_self_signature,
|
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 {
|
} 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);
|
let p: &packed::V1Public = memory::cast_to_struct(b);
|
||||||
id = Self {
|
id = Self {
|
||||||
|
@ -245,12 +244,11 @@ impl Identity {
|
||||||
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
|
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
|
||||||
p384: None,
|
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 {
|
} else {
|
||||||
return Err(InvalidFormatError);
|
return Err(InvalidFormatError);
|
||||||
}
|
}
|
||||||
id.populate_extended_address_bits(&mut address);
|
id.address = id.populate_extended_address_bits();
|
||||||
id.address = address;
|
|
||||||
return Ok(id);
|
return Ok(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,23 +281,23 @@ impl ToString for Identity {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
if let Some(p384) = self.p384.as_ref() {
|
if let Some(p384) = self.p384.as_ref() {
|
||||||
let mut s = String::with_capacity(1024);
|
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:");
|
s.push_str(":1:");
|
||||||
base24::encode_into(&self.x25519.ecdh, &mut s);
|
base64::encode_into(&self.x25519.ecdh, &mut s);
|
||||||
s.push(':');
|
s.push(':');
|
||||||
base24::encode_into(&self.x25519.eddsa, &mut s);
|
base64::encode_into(&self.x25519.eddsa, &mut s);
|
||||||
s.push(':');
|
s.push(':');
|
||||||
base24::encode_into(p384.ecdh.as_bytes(), &mut s);
|
base64::encode_into(p384.ecdh.as_bytes(), &mut s);
|
||||||
s.push(':');
|
s.push(':');
|
||||||
base24::encode_into(p384.ecdsa.as_bytes(), &mut s);
|
base64::encode_into(p384.ecdsa.as_bytes(), &mut s);
|
||||||
s.push(':');
|
s.push(':');
|
||||||
base24::encode_into(&p384.ed25519_self_signature, &mut s);
|
base64::encode_into(&p384.ed25519_self_signature, &mut s);
|
||||||
s.push(':');
|
s.push(':');
|
||||||
base24::encode_into(&p384.p384_self_signature, &mut s);
|
base64::encode_into(&p384.p384_self_signature, &mut s);
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{}:0:{}:{}",
|
"{}:0:{}{}",
|
||||||
hex::to_string(self.address.legacy_bytes()),
|
hex::to_string(self.address.legacy_bytes()),
|
||||||
hex::to_string(&self.x25519.ecdh),
|
hex::to_string(&self.x25519.ecdh),
|
||||||
hex::to_string(&self.x25519.eddsa)
|
hex::to_string(&self.x25519.eddsa)
|
||||||
|
@ -314,54 +312,56 @@ impl FromStr for Identity {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let ss: Vec<&str> = s.split(':').collect();
|
let ss: Vec<&str> = s.split(':').collect();
|
||||||
if ss.len() >= 2 {
|
if ss.len() >= 2 {
|
||||||
if ss[1] == "1" && ss.len() == 8 {
|
if ss[1] == "1" && ss.len() >= 8 {
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
address: Address::from_str(ss[0]).map_err(|_| InvalidFormatError)?,
|
address: Address::from_str(ss[0]).map_err(|_| InvalidFormatError)?,
|
||||||
x25519: X25519 {
|
x25519: X25519 {
|
||||||
ecdh: base24::decode(ss[2].as_bytes())
|
ecdh: base64::decode(ss[2].as_bytes())
|
||||||
.map_err(|_| InvalidFormatError)?
|
.map_err(|_| InvalidFormatError)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| InvalidFormatError)?,
|
.map_err(|_| InvalidFormatError)?,
|
||||||
eddsa: base24::decode(ss[3].as_bytes())
|
eddsa: base64::decode(ss[3].as_bytes())
|
||||||
.map_err(|_| InvalidFormatError)?
|
.map_err(|_| InvalidFormatError)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| InvalidFormatError)?,
|
.map_err(|_| InvalidFormatError)?,
|
||||||
},
|
},
|
||||||
p384: Some(P384 {
|
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)?,
|
.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)?,
|
.ok_or(InvalidFormatError)?,
|
||||||
ed25519_self_signature: base24::decode(ss[6].as_bytes())
|
ed25519_self_signature: base64::decode(ss[6].as_bytes())
|
||||||
.map_err(|_| InvalidFormatError)?
|
.map_err(|_| InvalidFormatError)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| InvalidFormatError)?,
|
.map_err(|_| InvalidFormatError)?,
|
||||||
p384_self_signature: base24::decode(ss[7].as_bytes())
|
p384_self_signature: base64::decode(ss[7].as_bytes())
|
||||||
.map_err(|_| InvalidFormatError)?
|
.map_err(|_| InvalidFormatError)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| InvalidFormatError)?,
|
.map_err(|_| InvalidFormatError)?,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} else if ss[1] == "0" && ss.len() == 4 {
|
} else if ss[1] == "0" && ss.len() >= 3 {
|
||||||
let mut address = {
|
let ecdh_eddsa = hex::from_string(ss[2]);
|
||||||
let legacy_address = hex::from_string(ss[0]);
|
if ecdh_eddsa.len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
||||||
if legacy_address.len() != PartialAddress::LEGACY_SIZE_BYTES {
|
return Err(InvalidFormatError);
|
||||||
return Err(InvalidFormatError);
|
}
|
||||||
}
|
|
||||||
let mut tmp = [0u8; Address::SIZE_BYTES];
|
|
||||||
tmp[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(legacy_address.as_slice());
|
|
||||||
Address(tmp)
|
|
||||||
};
|
|
||||||
let mut a = Self {
|
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 {
|
x25519: X25519 {
|
||||||
ecdh: hex::from_string(ss[2]).try_into().map_err(|_| InvalidFormatError)?,
|
ecdh: ecdh_eddsa[..C25519_PUBLIC_KEY_SIZE].try_into().unwrap(),
|
||||||
eddsa: hex::from_string(ss[3]).try_into().map_err(|_| InvalidFormatError)?,
|
eddsa: ecdh_eddsa[C25519_PUBLIC_KEY_SIZE..].try_into().unwrap(),
|
||||||
},
|
},
|
||||||
p384: None,
|
p384: None,
|
||||||
};
|
};
|
||||||
a.populate_extended_address_bits(&mut address);
|
a.address = a.populate_extended_address_bits();
|
||||||
a.address = address;
|
|
||||||
return Ok(a);
|
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 {
|
impl Serialize for Identity {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
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.
|
/// The actual serialization format for secret identities.
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct IdentitySecretForSerialization<'a> {
|
struct IdentitySecretForSerialization<'a> {
|
||||||
|
@ -572,7 +646,7 @@ impl<'de> Deserialize<'de> for IdentitySecret {
|
||||||
let e2 = || D::Error::custom("invalid key");
|
let e2 = || D::Error::custom("invalid key");
|
||||||
let e = |_e: TryFromSliceError| e2();
|
let e = |_e: TryFromSliceError| e2();
|
||||||
Ok(IdentitySecret {
|
Ok(IdentitySecret {
|
||||||
public: Valid::mark_valid(Identity {
|
public: Identity {
|
||||||
address: Address::from_bytes(tmp.address).map_err(|_| e2())?,
|
address: Address::from_bytes(tmp.address).map_err(|_| e2())?,
|
||||||
x25519: X25519 {
|
x25519: X25519 {
|
||||||
ecdh: x25519_ecdh_public.try_into().map_err(e)?,
|
ecdh: x25519_ecdh_public.try_into().map_err(e)?,
|
||||||
|
@ -588,7 +662,9 @@ impl<'de> Deserialize<'de> for IdentitySecret {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
}),
|
}
|
||||||
|
.validate()
|
||||||
|
.ok_or_else(e2)?,
|
||||||
x25519: X25519Secret {
|
x25519: X25519Secret {
|
||||||
ecdh: X25519KeyPair::from_bytes(x25519_ecdh_public, x25519_ecdh_secret).ok_or_else(e2)?,
|
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)?,
|
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 struct Node<Application: ApplicationLayer + ?Sized> {
|
||||||
pub(super) identity_secret: IdentitySecret,
|
pub identity: IdentitySecret,
|
||||||
intervals: Mutex<BackgroundTaskIntervals>,
|
intervals: Mutex<BackgroundTaskIntervals>,
|
||||||
paths: RwLock<HashMap<PathKey<'static, 'static, Application::LocalSocket>, Arc<Path<Application>>>>,
|
paths: RwLock<HashMap<PathKey<'static, 'static, Application::LocalSocket>, Arc<Path<Application>>>>,
|
||||||
pub(super) peers: PeerMap<Application>,
|
pub(super) peers: PeerMap<Application>,
|
||||||
|
@ -229,7 +229,7 @@ pub struct Node<Application: ApplicationLayer + ?Sized> {
|
||||||
impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
||||||
pub fn new(identity_secret: IdentitySecret) -> Self {
|
pub fn new(identity_secret: IdentitySecret) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identity_secret,
|
identity: identity_secret,
|
||||||
intervals: Mutex::new(BackgroundTaskIntervals::default()),
|
intervals: Mutex::new(BackgroundTaskIntervals::default()),
|
||||||
paths: RwLock::new(HashMap::new()),
|
paths: RwLock::new(HashMap::new()),
|
||||||
peers: PeerMap::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)]
|
#[inline(always)]
|
||||||
pub fn peer(&self, a: &Address) -> Option<Arc<Peer<Application>>> {
|
pub fn peer(&self, a: &Address) -> Option<Arc<Peer<Application>>> {
|
||||||
self.peers.get_exact(a)
|
self.peers.get_exact(a)
|
||||||
|
@ -353,7 +348,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
|
||||||
|
|
||||||
for (_, rs) in roots.sets.iter() {
|
for (_, rs) in roots.sets.iter() {
|
||||||
for m in rs.members.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
|
let _ = my_root_sets
|
||||||
.get_or_insert_with(|| Vec::new())
|
.get_or_insert_with(|| Vec::new())
|
||||||
.write_all(rs.to_buffer::<{ RootSet::MAX_MARSHAL_SIZE }>().unwrap().as_bytes());
|
.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) {
|
if let Some(peer) = self.peers.get_exact(&m.identity.address) {
|
||||||
new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect());
|
new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect());
|
||||||
} else {
|
} 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());
|
new_roots.insert(self.peers.add(Arc::new(peer)).0, m.endpoints.as_ref().unwrap().iter().cloned().collect());
|
||||||
} else {
|
} else {
|
||||||
bad_identities.push(m.identity.clone());
|
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) {
|
if let Ok(dest) = PartialAddress::from_legacy_address_bytes(&fragment_header.dest) {
|
||||||
// Packet is addressed to this node.
|
// 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 fragment_header = &*fragment_header; // discard mut
|
||||||
let path = self.canonical_path(source_endpoint, source_local_socket, source_local_interface, time_ticks);
|
let path = self.canonical_path(source_endpoint, source_local_socket, source_local_interface, time_ticks);
|
||||||
path.log_receive_anything(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_init(&self.v1_proto_next_message_id().to_be_bytes());
|
||||||
aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes(
|
aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes(
|
||||||
&self.identity.address,
|
&self.identity.address,
|
||||||
&node.identity().address,
|
&node.identity.public.address,
|
||||||
flags_cipher_hops,
|
flags_cipher_hops,
|
||||||
));
|
));
|
||||||
let payload = packet.as_bytes_starting_at_mut(v1::HEADER_SIZE).unwrap();
|
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();
|
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
||||||
header.id.copy_from_slice(&tag[0..8]);
|
header.id.copy_from_slice(&tag[0..8]);
|
||||||
header.dest = *self.identity.address.legacy_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.flags_cipher_hops = flags_cipher_hops;
|
||||||
header.mac.copy_from_slice(&tag[8..16]);
|
header.mac.copy_from_slice(&tag[8..16]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -355,7 +355,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
||||||
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
||||||
header.id = self.v1_proto_next_message_id().to_be_bytes();
|
header.id = self.v1_proto_next_message_id().to_be_bytes();
|
||||||
header.dest = *self.identity.address.legacy_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.flags_cipher_hops = flags_cipher_hops;
|
||||||
header
|
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();
|
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.id = message_id.to_ne_bytes();
|
||||||
f.0.dest = *self.identity.address.legacy_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.0.flags_cipher_hops = v1::CIPHER_NOCRYPT_POLY1305;
|
||||||
f.1.verb = message_type::VL1_HELLO;
|
f.1.verb = message_type::VL1_HELLO;
|
||||||
f.1.version_proto = PROTOCOL_VERSION;
|
f.1.version_proto = PROTOCOL_VERSION;
|
||||||
|
@ -424,7 +424,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert_eq!(packet.len(), 41);
|
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(
|
let (_, poly1305_key) = v1_proto_salsa_poly_create(
|
||||||
&self.v1_proto_static_secret,
|
&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()
|
/// All current members must sign whether they are disabled (witnessing) or active. The verify()
|
||||||
/// method will return true when signing is complete.
|
/// 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 signature = member_identity_secret.sign(self.marshal_for_signing().as_bytes());
|
||||||
let unsigned_entry = self.members.iter().find_map(|m| {
|
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())
|
Some(m.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -165,7 +165,7 @@ impl RootSet {
|
||||||
});
|
});
|
||||||
if unsigned_entry.is_some() {
|
if unsigned_entry.is_some() {
|
||||||
let unsigned_entry = unsigned_entry.unwrap();
|
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 {
|
let _ = self.members.push(Root {
|
||||||
identity: unsigned_entry.identity,
|
identity: unsigned_entry.identity,
|
||||||
endpoints: unsigned_entry.endpoints,
|
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 qi in queued_items.iter() {
|
||||||
for pkt in qi.pending_v1_packets.iter() {
|
for pkt in qi.pending_v1_packets.iter() {
|
||||||
if let Some(source_path) = pkt.0.upgrade() {
|
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.
|
/// "Anti-credential" revoking a network member's permission to communicate on a network.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct Revocation {
|
pub struct Revocation {
|
||||||
pub network_id: NetworkId,
|
pub network_id: u64, // legacy 64-bit network ID
|
||||||
pub threshold: i64,
|
pub threshold: i64,
|
||||||
pub target: u64, // legacy 40-bit address
|
pub target: u64, // legacy 40-bit address
|
||||||
pub issued_to: u64, // legacy 40-bit address
|
pub issued_to: u64, // legacy 40-bit address
|
||||||
|
@ -22,7 +22,7 @@ pub struct Revocation {
|
||||||
|
|
||||||
impl Revocation {
|
impl Revocation {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
network_id: NetworkId,
|
network_id: &NetworkId,
|
||||||
threshold: i64,
|
threshold: i64,
|
||||||
target: &Address,
|
target: &Address,
|
||||||
issued_to: &Address,
|
issued_to: &Address,
|
||||||
|
@ -31,7 +31,7 @@ impl Revocation {
|
||||||
fast_propagate: bool,
|
fast_propagate: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut r = Self {
|
let mut r = Self {
|
||||||
network_id,
|
network_id: network_id.to_legacy_u64(),
|
||||||
threshold,
|
threshold,
|
||||||
target: target.legacy_u64(),
|
target: target.legacy_u64(),
|
||||||
issued_to: issued_to.legacy_u64(),
|
issued_to: issued_to.legacy_u64(),
|
||||||
|
@ -50,7 +50,7 @@ impl Revocation {
|
||||||
|
|
||||||
let _ = v.write_all(&[0; 4]);
|
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.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(&[0; 8]);
|
||||||
let _ = v.write_all(&self.threshold.to_be_bytes());
|
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
|
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::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use zerotier_network_hypervisor::vl1::Identity;
|
use zerotier_network_hypervisor::vl1::identity::Identity;
|
||||||
use zerotier_utils::io::read_limit;
|
use zerotier_utils::io::read_limit;
|
||||||
|
|
||||||
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].
|
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].
|
||||||
|
|
|
@ -105,7 +105,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn base24_encode_decode() {
|
fn encode_decode() {
|
||||||
let mut tmp = [0xffu8; 256];
|
let mut tmp = [0xffu8; 256];
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
let mut s = String::with_capacity(1024);
|
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 arrayvec;
|
||||||
pub mod base24;
|
pub mod base24;
|
||||||
|
pub mod base64;
|
||||||
pub mod blob;
|
pub mod blob;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
pub mod cast;
|
pub mod cast;
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use zerotier_crypto::random::next_u32_secure;
|
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::io::{fs_restrict_permissions, read_limit, DEFAULT_FILE_IO_READ_LIMIT};
|
||||||
use zerotier_utils::json::to_json_pretty;
|
use zerotier_utils::json::to_json_pretty;
|
||||||
|
|
||||||
use crate::vl1service::VL1DataStorage;
|
|
||||||
|
|
||||||
pub const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
|
pub const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
|
||||||
pub const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
|
pub const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
|
||||||
pub const IDENTITY_SECRET_FILENAME: &'static str = "identity.secret";
|
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_DEFAULT_LENGTH: usize = 48;
|
||||||
const AUTH_TOKEN_POSSIBLE_CHARS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
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 struct DataDir<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> {
|
||||||
pub base_path: PathBuf,
|
pub base_path: PathBuf,
|
||||||
config: RwLock<Arc<Config>>,
|
config: RwLock<Arc<Config>>,
|
||||||
authtoken: Mutex<String>,
|
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> {
|
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> {
|
pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
|
||||||
let base_path = path.as_ref().to_path_buf();
|
let base_path = path.as_ref().to_path_buf();
|
||||||
|
|
|
@ -9,6 +9,7 @@ use std::time::Duration;
|
||||||
|
|
||||||
use zerotier_crypto::random;
|
use zerotier_crypto::random;
|
||||||
use zerotier_network_hypervisor::protocol::{PacketBufferFactory, PacketBufferPool};
|
use zerotier_network_hypervisor::protocol::{PacketBufferFactory, PacketBufferPool};
|
||||||
|
use zerotier_network_hypervisor::vl1::identity::IdentitySecret;
|
||||||
use zerotier_network_hypervisor::vl1::*;
|
use zerotier_network_hypervisor::vl1::*;
|
||||||
use zerotier_utils::{ms_monotonic, ms_since_epoch};
|
use zerotier_utils::{ms_monotonic, ms_since_epoch};
|
||||||
|
|
||||||
|
@ -20,12 +21,6 @@ use crate::LocalSocket;
|
||||||
/// Update UDP bindings every this many seconds.
|
/// Update UDP bindings every this many seconds.
|
||||||
const UPDATE_UDP_BINDINGS_EVERY_SECS: usize = 10;
|
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.
|
/// 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,
|
/// 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
|
/// 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.
|
/// a test harness or just the controller for a controller that runs stand-alone.
|
||||||
pub struct VL1Service<Inner: InnerProtocolLayer + ?Sized + 'static> {
|
pub struct VL1Service<Inner: InnerProtocolLayer + ?Sized + 'static> {
|
||||||
|
pub node: Node<Self>,
|
||||||
state: RwLock<VL1ServiceMutableState>,
|
state: RwLock<VL1ServiceMutableState>,
|
||||||
vl1_data_storage: Arc<dyn VL1DataStorage>,
|
|
||||||
inner: Arc<Inner>,
|
inner: Arc<Inner>,
|
||||||
buffer_pool: Arc<PacketBufferPool>,
|
buffer_pool: Arc<PacketBufferPool>,
|
||||||
node_container: Option<Node<Self>>, // never None, set in new()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VL1ServiceMutableState {
|
struct VL1ServiceMutableState {
|
||||||
|
@ -48,25 +42,21 @@ struct VL1ServiceMutableState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
|
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>> {
|
pub fn new(identity: IdentitySecret, inner: Arc<Inner>, settings: VL1Settings) -> Result<Arc<Self>, Box<dyn Error>> {
|
||||||
let mut service = Self {
|
let service = Arc::new(Self {
|
||||||
|
node: Node::<Self>::new(identity),
|
||||||
state: RwLock::new(VL1ServiceMutableState {
|
state: RwLock::new(VL1ServiceMutableState {
|
||||||
daemons: Vec::with_capacity(2),
|
daemons: Vec::with_capacity(2),
|
||||||
udp_sockets: HashMap::with_capacity(8),
|
udp_sockets: HashMap::with_capacity(8),
|
||||||
settings,
|
settings,
|
||||||
running: true,
|
running: true,
|
||||||
}),
|
}),
|
||||||
vl1_data_storage,
|
|
||||||
inner,
|
inner,
|
||||||
buffer_pool: Arc::new(PacketBufferPool::new(
|
buffer_pool: Arc::new(PacketBufferPool::new(
|
||||||
std::thread::available_parallelism().map_or(2, |c| c.get() + 2),
|
std::thread::available_parallelism().map_or(2, |c| c.get() + 2),
|
||||||
PacketBufferFactory::new(),
|
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 mut daemons = Vec::new();
|
||||||
let s = service.clone();
|
let s = service.clone();
|
||||||
|
@ -78,12 +68,6 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
|
||||||
Ok(service)
|
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> {
|
pub fn bound_udp_ports(&self) -> Vec<u16> {
|
||||||
self.state.read().unwrap().udp_sockets.keys().cloned().collect()
|
self.state.read().unwrap().udp_sockets.keys().cloned().collect()
|
||||||
}
|
}
|
||||||
|
@ -173,7 +157,7 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
|
||||||
self.update_udp_bindings();
|
self.update_udp_bindings();
|
||||||
}
|
}
|
||||||
udp_binding_check_every = udp_binding_check_every.wrapping_add(1);
|
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,
|
source_address: &InetAddress,
|
||||||
packet: zerotier_network_hypervisor::protocol::PooledPacketBuffer,
|
packet: zerotier_network_hypervisor::protocol::PooledPacketBuffer,
|
||||||
) {
|
) {
|
||||||
self.node().handle_incoming_physical_packet(
|
self.node.handle_incoming_physical_packet(
|
||||||
self.as_ref(),
|
self.as_ref(),
|
||||||
self.inner.as_ref(),
|
self.inner.as_ref(),
|
||||||
&Endpoint::IpUdp(source_address.clone()),
|
&Endpoint::IpUdp(source_address.clone()),
|
||||||
|
@ -216,22 +200,6 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> ApplicationLayer for VL1Servi
|
||||||
socket.is_valid()
|
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]
|
#[inline]
|
||||||
fn get_buffer(&self) -> zerotier_network_hypervisor::protocol::PooledPacketBuffer {
|
fn get_buffer(&self) -> zerotier_network_hypervisor::protocol::PooledPacketBuffer {
|
||||||
self.buffer_pool.get()
|
self.buffer_pool.get()
|
||||||
|
|
Loading…
Add table
Reference in a new issue