Some cleanup and reorg.

This commit is contained in:
Adam Ierymenko 2023-03-14 15:00:29 -04:00
parent 7c561d544d
commit 36b7986a57
9 changed files with 47 additions and 92 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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()
}

View file

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

View file

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

View file

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