mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-19 13:36:54 +02:00
Some cleanup and reorg.
This commit is contained in:
parent
7c561d544d
commit
36b7986a57
9 changed files with 47 additions and 92 deletions
|
@ -12,8 +12,6 @@ use zerotier_crypto::aes_gmac_siv::AesGmacSiv;
|
|||
use zerotier_crypto::hash::hmac_sha384;
|
||||
use zerotier_crypto::secret::Secret;
|
||||
|
||||
use self::v1::VERB_FLAG_COMPRESSED;
|
||||
|
||||
/*
|
||||
* Legacy V1 protocol versions:
|
||||
*
|
||||
|
@ -95,11 +93,6 @@ pub mod message_type {
|
|||
pub const VL2_NETWORK_CONFIG: u8 = 0x0c;
|
||||
pub const VL2_MULTICAST_GATHER: u8 = 0x0d;
|
||||
|
||||
// DR: Data replication protocol (scatter-gather based)
|
||||
|
||||
pub const DR_REQUEST: u8 = 0x1e;
|
||||
pub const DR_DATA: u8 = 0x1f;
|
||||
|
||||
pub fn name(verb: u8) -> &'static str {
|
||||
match verb {
|
||||
VL1_NOP => "VL1_NOP",
|
||||
|
@ -115,13 +108,17 @@ pub mod message_type {
|
|||
VL2_NETWORK_CONFIG_REQUEST => "VL2_NETWORK_CONFIG_REQUEST",
|
||||
VL2_NETWORK_CONFIG => "VL2_NETWORK_CONFIG",
|
||||
VL2_MULTICAST_GATHER => "VL2_MULTICAST_GATHER",
|
||||
DR_REQUEST => "DR_REQUEST",
|
||||
DR_DATA => "DR_DATA",
|
||||
_ => "???",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed.
|
||||
pub const MESSAGE_FLAG_COMPRESSED: u8 = 0x80;
|
||||
|
||||
/// Mask to get only the verb from the verb + verb flags byte.
|
||||
pub const MESSAGE_TYPE_MASK: u8 = 0x1f;
|
||||
|
||||
/// Default maximum payload size for UDP transport.
|
||||
///
|
||||
/// This is small enough to traverse numerous weird networks including PPPoE and Google Cloud's
|
||||
|
@ -212,18 +209,6 @@ pub mod v1 {
|
|||
/// Byte found at FRAGMENT_INDICATOR_INDEX to indicate a fragment.
|
||||
pub const FRAGMENT_INDICATOR: u8 = 0xff;
|
||||
|
||||
/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed.
|
||||
pub const VERB_FLAG_COMPRESSED: u8 = 0x80;
|
||||
|
||||
/// Verb (inner) flag indicating that payload is authenticated with HMAC-SHA384.
|
||||
pub const VERB_FLAG_EXTENDED_AUTHENTICATION: u8 = 0x40;
|
||||
|
||||
/// Mask to get only the verb from the verb + verb flags byte.
|
||||
pub const VERB_MASK: u8 = 0x1f;
|
||||
|
||||
/// Maximum number of verbs that the protocol can support.
|
||||
pub const VERB_MAX_COUNT: usize = 32;
|
||||
|
||||
/// Header (outer) flag indicating that this packet has additional fragments.
|
||||
pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40;
|
||||
|
||||
|
@ -256,30 +241,6 @@ pub mod v1 {
|
|||
/// Maximum number of hops to allow.
|
||||
pub const FORWARD_MAX_HOPS: u8 = 3;
|
||||
|
||||
/// Attempt to compress a packet's payload with LZ4
|
||||
///
|
||||
/// If this returns true the destination buffer will contain a compressed packet. If false is
|
||||
/// returned the contents of 'dest' are entirely undefined. This indicates that the data was not
|
||||
/// compressable or some other error occurred.
|
||||
pub fn compress_packet<const S: usize>(src: &[u8], dest: &mut Buffer<S>) -> bool {
|
||||
if src.len() > (VERB_INDEX + 16) {
|
||||
let compressed_data_size = {
|
||||
let d = unsafe { dest.entire_buffer_mut() };
|
||||
d[..VERB_INDEX].copy_from_slice(&src[..VERB_INDEX]);
|
||||
d[VERB_INDEX] = src[VERB_INDEX] | VERB_FLAG_COMPRESSED;
|
||||
lz4_flex::block::compress_into(&src[VERB_INDEX + 1..], &mut d[VERB_INDEX + 1..])
|
||||
};
|
||||
if compressed_data_size.is_ok() {
|
||||
let compressed_data_size = compressed_data_size.unwrap();
|
||||
if compressed_data_size > 0 && compressed_data_size < (src.len() - VERB_INDEX) {
|
||||
unsafe { dest.set_size_unchecked(VERB_INDEX + 1 + compressed_data_size) };
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Set header flag indicating that a packet is fragmented.
|
||||
///
|
||||
/// This will panic if the buffer provided doesn't contain a proper header.
|
||||
|
@ -545,7 +506,7 @@ pub fn compress(payload: &mut [u8]) -> usize {
|
|||
let mut tmp = [0u8; 65536];
|
||||
if let Ok(mut compressed_size) = lz4_flex::block::compress_into(&payload[1..], &mut tmp) {
|
||||
if compressed_size < (payload.len() - 1) {
|
||||
payload[0] |= VERB_FLAG_COMPRESSED;
|
||||
payload[0] |= MESSAGE_FLAG_COMPRESSED;
|
||||
payload[1..(1 + compressed_size)].copy_from_slice(&tmp[..compressed_size]);
|
||||
return 1 + compressed_size;
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ use crate::vl1::identity::IDENTITY_FINGERPRINT_SIZE;
|
|||
use crate::vl1::inetaddress::InetAddress;
|
||||
use crate::vl1::{Address, MAC};
|
||||
|
||||
use zerotier_utils::base64;
|
||||
use zerotier_utils::buffer::Buffer;
|
||||
use zerotier_utils::error::InvalidFormatError;
|
||||
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
|
||||
use zerotier_utils::{base64_decode_url_nopad, base64_encode_url_nopad};
|
||||
|
||||
pub const TYPE_NIL: u8 = 0;
|
||||
pub const TYPE_ZEROTIER: u8 = 1;
|
||||
|
@ -319,7 +319,7 @@ impl ToString for Endpoint {
|
|||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Endpoint::Nil => format!("nil"),
|
||||
Endpoint::ZeroTier(a, ah) => format!("zt:{}-{}", a.to_string(), base64_encode_url_nopad(ah)),
|
||||
Endpoint::ZeroTier(a, ah) => format!("zt:{}-{}", a.to_string(), base64::encode_url_nopad(ah)),
|
||||
Endpoint::Ethernet(m) => format!("eth:{}", m.to_string()),
|
||||
Endpoint::WifiDirect(m) => format!("wifip2p:{}", m.to_string()),
|
||||
Endpoint::Bluetooth(m) => format!("bt:{}", m.to_string()),
|
||||
|
@ -327,8 +327,8 @@ impl ToString for Endpoint {
|
|||
Endpoint::IpUdp(ip) => format!("udp:{}", ip.to_string()),
|
||||
Endpoint::IpTcp(ip) => format!("tcp:{}", ip.to_string()),
|
||||
Endpoint::Http(url) => format!("url:{}", url.clone()), // http or https
|
||||
Endpoint::WebRTC(offer) => format!("webrtc:{}", base64_encode_url_nopad(offer.as_slice())),
|
||||
Endpoint::ZeroTierEncap(a, ah) => format!("zte:{}-{}", a.to_string(), base64_encode_url_nopad(ah)),
|
||||
Endpoint::WebRTC(offer) => format!("webrtc:{}", base64::encode_url_nopad(offer.as_slice())),
|
||||
Endpoint::ZeroTierEncap(a, ah) => format!("zte:{}-{}", a.to_string(), base64::encode_url_nopad(ah)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ impl FromStr for Endpoint {
|
|||
let address_and_hash = endpoint_data.split_once("-");
|
||||
if address_and_hash.is_some() {
|
||||
let (address, hash) = address_and_hash.unwrap();
|
||||
if let Some(hash) = base64_decode_url_nopad(hash) {
|
||||
if let Some(hash) = base64::decode_url_nopad(hash) {
|
||||
if hash.len() == IDENTITY_FINGERPRINT_SIZE {
|
||||
if endpoint_type == "zt" {
|
||||
return Ok(Endpoint::ZeroTier(Address::from_str(address)?, hash.as_slice().try_into().unwrap()));
|
||||
|
@ -370,7 +370,7 @@ impl FromStr for Endpoint {
|
|||
"tcp" => return Ok(Endpoint::IpTcp(InetAddress::from_str(endpoint_data)?)),
|
||||
"url" => return Ok(Endpoint::Http(endpoint_data.into())),
|
||||
"webrtc" => {
|
||||
if let Some(offer) = base64_decode_url_nopad(endpoint_data) {
|
||||
if let Some(offer) = base64::decode_url_nopad(endpoint_data) {
|
||||
return Ok(Endpoint::WebRTC(offer));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@ use zerotier_crypto::x25519::*;
|
|||
use zerotier_crypto::{hash::*, secure_eq};
|
||||
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
use zerotier_utils::base64;
|
||||
use zerotier_utils::buffer::Buffer;
|
||||
use zerotier_utils::error::{InvalidFormatError, InvalidParameterError};
|
||||
use zerotier_utils::hex;
|
||||
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
|
||||
use zerotier_utils::{base64_decode_url_nopad, base64_encode_url_nopad, hex};
|
||||
|
||||
use crate::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD};
|
||||
use crate::vl1::Address;
|
||||
|
@ -492,7 +493,7 @@ impl Identity {
|
|||
&p384.ecdsa_self_signature,
|
||||
&p384.ed25519_self_signature,
|
||||
);
|
||||
s.push_str(base64_encode_url_nopad(&p384_joined).as_str());
|
||||
s.push_str(base64::encode_url_nopad(&p384_joined).as_str());
|
||||
if self.secret.is_some() && include_private {
|
||||
let secret = self.secret.as_ref().unwrap();
|
||||
if secret.p384.is_some() {
|
||||
|
@ -502,7 +503,7 @@ impl Identity {
|
|||
p384_secret.ecdsa.secret_key_bytes().as_bytes(),
|
||||
);
|
||||
s.push(':');
|
||||
s.push_str(base64_encode_url_nopad(&p384_secret_joined).as_str());
|
||||
s.push_str(base64::encode_url_nopad(&p384_secret_joined).as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -586,8 +587,8 @@ impl FromStr for Identity {
|
|||
let keys = [
|
||||
hex::from_string(keys[0].unwrap_or("")),
|
||||
hex::from_string(keys[1].unwrap_or("")),
|
||||
base64_decode_url_nopad(keys[2].unwrap_or("")).unwrap_or_else(|| Vec::new()),
|
||||
base64_decode_url_nopad(keys[3].unwrap_or("")).unwrap_or_else(|| Vec::new()),
|
||||
base64::decode_url_nopad(keys[2].unwrap_or("")).unwrap_or_else(|| Vec::new()),
|
||||
base64::decode_url_nopad(keys[3].unwrap_or("")).unwrap_or_else(|| Vec::new()),
|
||||
];
|
||||
if keys[0].len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
||||
return Err(InvalidFormatError);
|
||||
|
|
|
@ -385,10 +385,6 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
///
|
||||
/// If explicit_endpoint is not None the packet will be sent directly to this endpoint.
|
||||
/// Otherwise it will be sent via the best direct or indirect path known.
|
||||
///
|
||||
/// Unlike other messages HELLO is sent partially in the clear and always with the long-lived
|
||||
/// static identity key. Authentication in old versions is via Poly1305 and in new versions
|
||||
/// via HMAC-SHA512.
|
||||
pub(crate) fn send_hello(&self, app: &Application, node: &Node<Application>, explicit_endpoint: Option<&Endpoint>) -> bool {
|
||||
let mut path = None;
|
||||
let destination = if let Some(explicit_endpoint) = explicit_endpoint {
|
||||
|
@ -415,7 +411,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
f.0.dest = self.identity.address.to_bytes();
|
||||
f.0.src = node.identity.address.to_bytes();
|
||||
f.0.flags_cipher_hops = v1::CIPHER_NOCRYPT_POLY1305;
|
||||
f.1.verb = message_type::VL1_HELLO | v1::VERB_FLAG_EXTENDED_AUTHENTICATION;
|
||||
f.1.verb = message_type::VL1_HELLO;
|
||||
f.1.version_proto = PROTOCOL_VERSION;
|
||||
f.1.version_major = VERSION_MAJOR;
|
||||
f.1.version_minor = VERSION_MINOR;
|
||||
|
@ -498,7 +494,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
};
|
||||
|
||||
if let Ok(mut verb) = payload.u8_at(0) {
|
||||
if (verb & v1::VERB_FLAG_COMPRESSED) != 0 {
|
||||
if (verb & MESSAGE_FLAG_COMPRESSED) != 0 {
|
||||
let mut decompressed_payload = [0u8; v1::SIZE_MAX];
|
||||
decompressed_payload[0] = verb;
|
||||
if let Ok(dlen) = lz4_flex::block::decompress_into(&payload.as_bytes()[1..], &mut decompressed_payload[1..]) {
|
||||
|
@ -523,7 +519,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
|
|||
}
|
||||
}
|
||||
|
||||
verb &= v1::VERB_MASK; // mask off flags
|
||||
verb &= MESSAGE_TYPE_MASK; // mask off flags
|
||||
debug_event!(
|
||||
app,
|
||||
"[vl1] #{:0>16x} decrypted and authenticated, verb: {} ({:0>2x})",
|
||||
|
@ -891,7 +887,7 @@ fn v1_proto_try_aead_decrypt(
|
|||
if cipher == v1::CIPHER_SALSA2012_POLY1305 {
|
||||
salsa.crypt_in_place(payload.as_bytes_mut());
|
||||
Some(message_id)
|
||||
} else if (payload.u8_at(0).unwrap_or(0) & v1::VERB_MASK) == message_type::VL1_HELLO {
|
||||
} else if (payload.u8_at(0).unwrap_or(0) & MESSAGE_TYPE_MASK) == message_type::VL1_HELLO {
|
||||
Some(message_id)
|
||||
} else {
|
||||
// SECURITY: fail if there is no encryption and the message is not HELLO. No other types are allowed
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use zerotier_utils::blob::Blob;
|
||||
use zerotier_utils::flatsortedmap::FlatSortedMap;
|
||||
|
@ -51,16 +50,6 @@ pub struct Topology<'a> {
|
|||
pub members: FlatSortedMap<'a, Blob<IDENTITY_FINGERPRINT_SIZE>, Member<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Topology<'a> {
|
||||
pub fn new_from_bytes(b: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
|
||||
rmp_serde::from_slice(b)
|
||||
}
|
||||
|
||||
pub fn write_bytes_to<W: Write>(&self, w: &mut W) {
|
||||
rmp_serde::encode::write_named(w, self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn u64_zero(i: &u64) -> bool {
|
||||
*i == 0
|
||||
|
|
20
utils/src/base64.rs
Normal file
20
utils/src/base64.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
const BASE64_URL_SAFE_NO_PAD_ENGINE: base64::engine::fast_portable::FastPortable =
|
||||
base64::engine::fast_portable::FastPortable::from(&base64::alphabet::URL_SAFE, base64::engine::fast_portable::NO_PAD);
|
||||
|
||||
/// Encode base64 using URL-safe alphabet and no padding.
|
||||
pub fn encode_url_nopad(bytes: &[u8]) -> String {
|
||||
base64::encode_engine(bytes, &BASE64_URL_SAFE_NO_PAD_ENGINE)
|
||||
}
|
||||
|
||||
/// Decode base64 using URL-safe alphabet and no padding, or None on error.
|
||||
pub fn decode_url_nopad(b64: &str) -> Option<Vec<u8>> {
|
||||
base64::decode_engine(b64, &BASE64_URL_SAFE_NO_PAD_ENGINE).ok()
|
||||
}
|
|
@ -14,7 +14,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|||
|
||||
use crate::hex;
|
||||
|
||||
/// Fixed size byte array with Serde serializer/deserializer for sizes over 32 elements and hex to_string().
|
||||
/// Fixed size Serde serializable byte array.
|
||||
/// This makes it easier to deal with blobs larger than 32 bytes (due to serde array limitations)
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Blob<const L: usize>([u8; L]);
|
||||
|
|
|
@ -27,7 +27,6 @@ impl Display for OutOfBoundsError {
|
|||
}
|
||||
|
||||
impl Debug for OutOfBoundsError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
pub mod arrayvec;
|
||||
pub mod base64;
|
||||
pub mod blob;
|
||||
pub mod buffer;
|
||||
pub mod cast;
|
||||
|
@ -36,20 +37,7 @@ pub use tokio;
|
|||
pub use futures_util;
|
||||
|
||||
/// Initial value that should be used for monotonic tick time variables.
|
||||
pub const NEVER_HAPPENED_TICKS: i64 = 0;
|
||||
|
||||
const BASE64_URL_SAFE_NO_PAD_ENGINE: base64::engine::fast_portable::FastPortable =
|
||||
base64::engine::fast_portable::FastPortable::from(&base64::alphabet::URL_SAFE, base64::engine::fast_portable::NO_PAD);
|
||||
|
||||
/// Encode base64 using URL-safe alphabet and no padding.
|
||||
pub fn base64_encode_url_nopad(bytes: &[u8]) -> String {
|
||||
base64::encode_engine(bytes, &BASE64_URL_SAFE_NO_PAD_ENGINE)
|
||||
}
|
||||
|
||||
/// Decode base64 using URL-safe alphabet and no padding, or None on error.
|
||||
pub fn base64_decode_url_nopad(b64: &str) -> Option<Vec<u8>> {
|
||||
base64::decode_engine(b64, &BASE64_URL_SAFE_NO_PAD_ENGINE).ok()
|
||||
}
|
||||
pub const NEVER_HAPPENED_TICKS: i64 = i64::MIN;
|
||||
|
||||
/// Get milliseconds since unix epoch.
|
||||
#[inline]
|
||||
|
|
Loading…
Add table
Reference in a new issue