mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-03 19:13:43 +02:00
Reorg some stuff, build out send_hello()
This commit is contained in:
parent
0395943d31
commit
209aeefac3
9 changed files with 350 additions and 158 deletions
|
@ -66,7 +66,6 @@ NOP, as the name suggests, does nothing. Any payload is ignored.
|
|||
| [2] u16 | Length of encrypted Dictionary in bytes |
|
||||
| Dictionary | Key/value dictionary containing additional fields |
|
||||
| -- | -- END of AES-256-CTR encrypted section -- |
|
||||
| [48] [u8; 48] | HMAC-SHA384 extended strength MAC |
|
||||
|
||||
HELLO establishes a full session with another peer and carries information such as protocol and software versions, the full identity of the peer, and ephemeral keys for forward secrecy. Without a HELLO exchange only limited communication with the most conservative assumptions is possible, and communication without a session may be completely removed in the future. (It's only allowed now for backward compatibility with ZeroTier 1.x, and must be disabled in FIPS mode.)
|
||||
|
||||
|
@ -91,9 +90,6 @@ OK(HELLO) response payload, which must be sent if the HELLO receipient wishes to
|
|||
| [2] u16 | *(reserved)* (set to zero for legacy reasons) |
|
||||
| [2] u16 | Length of encrypted Dictionary in bytes |
|
||||
| Dictionary | Key/value dictionary containing additional fields |
|
||||
| [48] [u8; 48] | HMAC-SHA384 extended strength MAC |
|
||||
|
||||
HMAC-SHA384 authentication is computed over the payload of HELLO and OK(HELLO). For HELLO it is computed after AES-256-CTR encryption is applied to the dictionary section. and is checked before anything is done with a payload. For OK(HELLO) it is computed prior to normal packet armoring and is itself included in the encrypted payload.
|
||||
|
||||
Recommended dictionary fields in both HELLO and OK(HELLO):
|
||||
|
||||
|
@ -118,13 +114,16 @@ Optional dictionary fields that can be included in either HELLO or OK(HELLO):
|
|||
| -------------------- | --- | ------------ | ------------------------------------------------------------ |
|
||||
| SYS_ARCH | `Sa` | string | Host architecture (e.g. x86_64, aarch64) |
|
||||
| SYS_BITS | `Sb` | u64 | sizeof(pointer), e.g. 32 or 64 |
|
||||
| OS_NAME | `On` | string | Name of host operating system |
|
||||
| OS_VERSION | `Ov` | string | Operating system version |
|
||||
| OS_NAME | `So` | string | Name of host operating system |
|
||||
| OS_VERSION | `Sv` | string | Operating system version |
|
||||
| OS_VARIANT | `St` | string | Operating system variant (e.g. Linux distribution) |
|
||||
| VENDOR | `V` | string | Node software vendor if not ZeroTier, Inc. |
|
||||
| FLAGS | `+` | string | Flags (see below) |
|
||||
|
||||
FLAGS is a string that can contain the following boolean flags: `F` to indicate that the node is running in FIPS compliant mode, and `w` to indicate that the node is a "wimp." "Wimpy" nodes are things like mobile phones, and this flag can be used to exempt these devices from selection for any intensive role (such as use in VL2 to propagate multicasts).
|
||||
|
||||
System information such as OS_NAME is currently only sent to roots and not to any other node. This allows roots to collect a bit of very generic statistical and diagnostic telemtry about the nodes using them.
|
||||
|
||||
#### 0x02 / ERROR
|
||||
|
||||
| [Size] Type | Description |
|
|
@ -27,10 +27,4 @@ impl Poly1305 {
|
|||
let _ = self.0.get_mac(&mut mac);
|
||||
mac
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn finish_into(&mut self, mac: &mut [u8]) {
|
||||
debug_assert_eq!(mac.len(), 16);
|
||||
let _ = self.0.get_mac(mac);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,3 +8,46 @@ pub const VERSION_MAJOR: u8 = 1;
|
|||
pub const VERSION_MINOR: u8 = 99;
|
||||
pub const VERSION_REVISION: u8 = 1;
|
||||
pub const VERSION_STR: &'static str = "1.99.1";
|
||||
|
||||
/*
|
||||
* Protocol versions
|
||||
*
|
||||
* 1 - 0.2.0 ... 0.2.5
|
||||
* 2 - 0.3.0 ... 0.4.5
|
||||
* + Added signature and originating peer to multicast frame
|
||||
* + Double size of multicast frame bloom filter
|
||||
* 3 - 0.5.0 ... 0.6.0
|
||||
* + Yet another multicast redesign
|
||||
* + New crypto completely changes key agreement cipher
|
||||
* 4 - 0.6.0 ... 1.0.6
|
||||
* + BREAKING CHANGE: New identity format based on hashcash design
|
||||
* 5 - 1.1.0 ... 1.1.5
|
||||
* + Supports echo
|
||||
* + Supports in-band world (root server definition) updates
|
||||
* + Clustering! (Though this will work with protocol v4 clients.)
|
||||
* + Otherwise backward compatible with protocol v4
|
||||
* 6 - 1.1.5 ... 1.1.10
|
||||
* + Network configuration format revisions including binary values
|
||||
* 7 - 1.1.10 ... 1.1.17
|
||||
* + Introduce trusted paths for local SDN use
|
||||
* 8 - 1.1.17 ... 1.2.0
|
||||
* + Multipart network configurations for large network configs
|
||||
* + Tags and Capabilities
|
||||
* + inline push of CertificateOfMembership deprecated
|
||||
* 9 - 1.2.0 ... 1.2.14
|
||||
* 10 - 1.4.0 ... 1.4.6
|
||||
* + Contained early pre-alpha versions of multipath, which are deprecated
|
||||
* 11 - 1.6.0 ... 2.0.0
|
||||
* + Supports AES-GMAC-SIV symmetric crypto, backported from v2 tree.
|
||||
* 20 - 2.0.0 ... CURRENT
|
||||
* + New more WAN-efficient P2P-assisted multicast algorithm
|
||||
* + HELLO and OK(HELLO) include an extra HMAC to harden authentication
|
||||
* + HELLO and OK(HELLO) carry meta-data in a dictionary that's encrypted
|
||||
* + Forward secrecy, key lifetime management
|
||||
* + Old planet/moon stuff is DEAD! Independent roots are easier.
|
||||
* + AES encryption with the SIV construction AES-GMAC-SIV
|
||||
* + New combined Curve25519/NIST P-384 identity type (type 1)
|
||||
* + Short probe packets to reduce probe bandwidth
|
||||
* + More aggressive NAT traversal techniques for IPv4 symmetric NATs
|
||||
*/
|
||||
pub const VERSION_PROTO: u8 = 20;
|
||||
|
|
|
@ -68,7 +68,9 @@ impl<const L: usize> Buffer<L> {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, L); }
|
||||
let prev_len = self.0;
|
||||
self.0 = 0;
|
||||
self.1[0..prev_len].fill(0);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -38,56 +38,6 @@ pub const PACKET_SIZE_MIN: usize = PACKET_HEADER_SIZE + 1;
|
|||
/// Maximum size of an entire packet.
|
||||
pub const PACKET_SIZE_MAX: usize = PACKET_HEADER_SIZE + PACKET_PAYLOAD_SIZE_MAX;
|
||||
|
||||
/// Index of packet verb after header.
|
||||
pub const PACKET_VERB_INDEX: usize = 27;
|
||||
|
||||
/// Index of destination in both fragment and full packet headers.
|
||||
pub const PACKET_DESTINATION_INDEX: usize = 8;
|
||||
|
||||
/// Mask to select cipher from header flags field.
|
||||
pub const HEADER_FLAGS_FIELD_MASK_CIPHER: u8 = 0x30;
|
||||
|
||||
/// Mask to select packet hops from header flags field.
|
||||
pub const HEADER_FLAGS_FIELD_MASK_HOPS: u8 = 0x07;
|
||||
|
||||
/// Mask to select packet hops from header flags field.
|
||||
pub const HEADER_FLAGS_FIELD_MASK_HIDE_HOPS: u8 = 0xf8;
|
||||
|
||||
/// Index of hops/flags field
|
||||
pub const HEADER_FLAGS_FIELD_INDEX: usize = 18;
|
||||
|
||||
/// Packet is not encrypted but contains a Poly1305 MAC of the plaintext.
|
||||
/// Poly1305 is initialized with Salsa20/12 in the same manner as SALSA2012_POLY1305.
|
||||
pub const CIPHER_NOCRYPT_POLY1305: u8 = 0x00;
|
||||
|
||||
/// Packet is encrypted and authenticated with Salsa20/12 and Poly1305.
|
||||
/// Construction is the same as that which is used in the NaCl secret box functions.
|
||||
pub const CIPHER_SALSA2012_POLY1305: u8 = 0x10;
|
||||
|
||||
/// Formerly 'NONE' which is deprecated; reserved for future use.
|
||||
pub const CIPHER_RESERVED: u8 = 0x20;
|
||||
|
||||
/// Packet is encrypted and authenticated with AES-GMAC-SIV (AES-256).
|
||||
pub const CIPHER_AES_GMAC_SIV: u8 = 0x30;
|
||||
|
||||
/// Header (outer) flag indicating that this packet has additional fragments.
|
||||
pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40;
|
||||
|
||||
/// Minimum size of a fragment.
|
||||
pub const FRAGMENT_SIZE_MIN: usize = 16;
|
||||
|
||||
/// Size of fragment header after which data begins.
|
||||
pub const FRAGMENT_HEADER_SIZE: usize = 16;
|
||||
|
||||
/// Maximum allowed number of fragments.
|
||||
pub const FRAGMENT_COUNT_MAX: usize = 8;
|
||||
|
||||
/// Index of packet fragment indicator byte to detect fragments.
|
||||
pub const FRAGMENT_INDICATOR_INDEX: usize = 13;
|
||||
|
||||
/// Byte found at FRAGMENT_INDICATOR_INDEX to indicate a fragment.
|
||||
pub const FRAGMENT_INDICATOR: u8 = 0xff;
|
||||
|
||||
/// Maximum number of inbound fragmented packets to handle at once per path.
|
||||
/// This is a sanity limit to prevent memory exhaustion due to DOS attacks or broken peers.
|
||||
pub const FRAGMENT_MAX_INBOUND_PACKETS_PER_PATH: usize = 256;
|
||||
|
@ -95,21 +45,6 @@ pub const FRAGMENT_MAX_INBOUND_PACKETS_PER_PATH: usize = 256;
|
|||
/// Time after which an incomplete fragmented packet expires.
|
||||
pub const FRAGMENT_EXPIRATION: i64 = 1500;
|
||||
|
||||
/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed.
|
||||
pub const VERB_FLAG_COMPRESSED: u8 = 0x80;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Maximum number of packet hops allowed by the protocol.
|
||||
pub const PROTOCOL_MAX_HOPS: u8 = 7;
|
||||
|
||||
/// Maximum number of hops to allow.
|
||||
pub const FORWARD_MAX_HOPS: u8 = 3;
|
||||
|
||||
/// Frequency for WHOIS retries
|
||||
pub const WHOIS_RETRY_INTERVAL: i64 = 1000;
|
||||
|
||||
|
|
|
@ -149,6 +149,15 @@ impl Locator {
|
|||
signature: signature.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Option<Vec<u8>> {
|
||||
let mut buf: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new();
|
||||
if self.marshal_internal(&mut buf, false).is_ok() {
|
||||
Some(buf.as_bytes().to_vec())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Locator {
|
||||
|
|
|
@ -43,11 +43,11 @@ pub trait VL1CallerInterface {
|
|||
fn event_user_message(&self, source: &Identity, message_type: u64, message: &[u8]);
|
||||
|
||||
/// Load this node's identity from the data store.
|
||||
fn load_identity(&self) -> Option<&[u8]>;
|
||||
fn load_node_identity(&self) -> Option<&[u8]>;
|
||||
|
||||
/// Save this node's identity.
|
||||
/// Note that this is only called on first startup (after up) and after identity_changed.
|
||||
fn save_identity(&self, id: &Identity, public: &[u8], secret: &[u8]);
|
||||
fn save_node_identity(&self, id: &Identity, public: &[u8], secret: &[u8]);
|
||||
|
||||
/// Load this node's latest locator.
|
||||
fn load_locator(&self) -> Option<&[u8]>;
|
||||
|
@ -55,21 +55,6 @@ pub trait VL1CallerInterface {
|
|||
/// Save this node's latest locator.
|
||||
fn save_locator(&self, locator: &[u8]);
|
||||
|
||||
/// Load a peer's latest saved state. (A remote peer, not this one.)
|
||||
fn load_peer(&self, address: Address) -> Option<&[u8]>;
|
||||
|
||||
/// Save a peer's state.
|
||||
///
|
||||
/// The state contains the identity, so there's no need to save that separately.
|
||||
/// It's just supplied for the address and if the external code wants it.
|
||||
fn save_peer(&self, id: &Identity, peer: &[u8]);
|
||||
|
||||
/// Load network configuration.
|
||||
fn load_network_config(&self, id: u64) -> Option<&[u8]>;
|
||||
|
||||
/// Save network configuration.
|
||||
fn save_network_config(&self, id: u64, config: &[u8]);
|
||||
|
||||
/// Called to send a packet over the physical network (virtual -> physical).
|
||||
///
|
||||
/// This may return false if the send definitely failed, and may return true if the send
|
||||
|
@ -124,16 +109,18 @@ struct BackgroundTaskIntervals {
|
|||
}
|
||||
|
||||
pub struct Node {
|
||||
instance_id: u64,
|
||||
pub(crate) instance_id: u64,
|
||||
identity: Identity,
|
||||
intervals: Mutex<BackgroundTaskIntervals>,
|
||||
locator: Mutex<Option<Locator>>,
|
||||
locator: Mutex<Option<Arc<Locator>>>,
|
||||
paths: DashMap<Endpoint, Arc<Path>>,
|
||||
peers: DashMap<Address, Arc<Peer>>,
|
||||
roots: Mutex<Vec<Arc<Peer>>>,
|
||||
whois: WhoisQueue,
|
||||
buffer_pool: Pool<Buffer<{ PACKET_SIZE_MAX }>, PooledBufferFactory<{ PACKET_SIZE_MAX }>>,
|
||||
secure_prng: SecureRandom,
|
||||
pub(crate) fips_mode: bool,
|
||||
pub(crate) wimp: bool,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
|
@ -143,13 +130,13 @@ impl Node {
|
|||
/// no identity is currently stored in the data store.
|
||||
pub fn new<CI: VL1CallerInterface>(ci: &CI, auto_generate_identity_type: Option<crate::vl1::identity::Type>) -> Result<Self, InvalidParameterError> {
|
||||
let id = {
|
||||
let id_str = ci.load_identity();
|
||||
let id_str = ci.load_node_identity();
|
||||
if id_str.is_none() {
|
||||
if auto_generate_identity_type.is_none() {
|
||||
return Err(InvalidParameterError("no identity found and auto-generate not specified"));
|
||||
return Err(InvalidParameterError("no identity found and auto-generate not enabled"));
|
||||
} else {
|
||||
let id = Identity::generate(auto_generate_identity_type.unwrap());
|
||||
ci.save_identity(&id, id.to_string().as_bytes(), id.to_secret_string().as_bytes());
|
||||
ci.save_node_identity(&id, id.to_string().as_bytes(), id.to_secret_string().as_bytes());
|
||||
id
|
||||
}
|
||||
} else {
|
||||
|
@ -174,6 +161,8 @@ impl Node {
|
|||
whois: WhoisQueue::new(),
|
||||
buffer_pool: Pool::new(64, PooledBufferFactory),
|
||||
secure_prng: SecureRandom::get(),
|
||||
fips_mode: false,
|
||||
wimp: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -189,6 +178,12 @@ impl Node {
|
|||
&self.identity
|
||||
}
|
||||
|
||||
/// Get this node's current locator or None if no locator created.
|
||||
#[inline(always)]
|
||||
pub fn locator(&self) -> Option<Arc<Locator>> {
|
||||
self.locator.lock().clone()
|
||||
}
|
||||
|
||||
/// Get a reusable packet buffer.
|
||||
/// The buffer will automatically be returned to the pool if it is dropped.
|
||||
pub fn get_packet_buffer(&self) -> PacketBuffer {
|
||||
|
@ -210,6 +205,17 @@ impl Node {
|
|||
v
|
||||
}
|
||||
|
||||
/// Get the current best root peer that we should use for WHOIS, relaying, etc.
|
||||
pub(crate) fn root(&self) -> Option<Arc<Peer>> {
|
||||
self.roots.lock().first().map(|p| p.clone())
|
||||
}
|
||||
|
||||
/// Determine if a given peer is a root.
|
||||
pub(crate) fn is_root(&self, peer: &Peer) -> bool {
|
||||
let pptr = peer as *const Peer;
|
||||
self.roots.lock().iter().any(|p| Arc::as_ptr(p) == pptr)
|
||||
}
|
||||
|
||||
/// Run background tasks and return desired delay until next call in milliseconds.
|
||||
///
|
||||
/// This should only be called once at a time. It technically won't hurt anything to
|
||||
|
@ -309,11 +315,6 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the current best root peer that we should use for WHOIS, relaying, etc.
|
||||
pub(crate) fn root(&self) -> Option<Arc<Peer>> {
|
||||
self.roots.lock().first().map(|p| p.clone())
|
||||
}
|
||||
|
||||
/// Get the canonical Path object for a given endpoint and local socket information.
|
||||
///
|
||||
/// This is a canonicalizing function that returns a unique path object for every tuple
|
||||
|
|
|
@ -12,11 +12,13 @@ use crate::crypto::random::next_u64_secure;
|
|||
use crate::crypto::salsa::Salsa;
|
||||
use crate::crypto::secret::Secret;
|
||||
use crate::util::pool::{Pool, PoolFactory};
|
||||
use crate::vl1::{Identity, Path, Endpoint};
|
||||
use crate::vl1::{Identity, Path, Endpoint, InetAddress, Dictionary};
|
||||
use crate::vl1::buffer::Buffer;
|
||||
use crate::vl1::constants::*;
|
||||
use crate::vl1::node::*;
|
||||
use crate::vl1::protocol::*;
|
||||
use crate::{VERSION_PROTO, VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
|
||||
use crate::crypto::hash::SHA384;
|
||||
|
||||
struct AesGmacSivPoolFactory(Secret<48>, Secret<48>);
|
||||
|
||||
|
@ -86,6 +88,9 @@ pub struct Peer {
|
|||
// Paths sorted in ascending order of quality / preference.
|
||||
paths: Mutex<Vec<Arc<Path>>>,
|
||||
|
||||
// Local external address most recently reported by this peer (IP transport only).
|
||||
reported_local_ip: Mutex<Option<InetAddress>>,
|
||||
|
||||
// Statistics and times of events.
|
||||
last_send_time_ticks: AtomicI64,
|
||||
last_receive_time_ticks: AtomicI64,
|
||||
|
@ -124,6 +129,16 @@ fn salsa_derive_per_packet_key(key: &Secret<48>, header: &PacketHeader, packet_s
|
|||
k
|
||||
}
|
||||
|
||||
/// Create initialized instances of Salsa20/12 and Poly1305 for a packet.
|
||||
fn salsa_poly_create(secret: &PeerSecret, header: &PacketHeader, packet_size: usize) -> (Salsa, Poly1305) {
|
||||
let key = salsa_derive_per_packet_key(&secret.secret, header, payload.len());
|
||||
let mut salsa = Salsa::new(&key.0[0..32], header.id_bytes(), true).unwrap();
|
||||
let mut poly1305_key = [0_u8; 32];
|
||||
salsa.crypt_in_place(&mut poly1305_key);
|
||||
let mut poly = Poly1305::new(&poly1305_key).unwrap();
|
||||
(salsa, poly)
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
pub(crate) const INTERVAL: i64 = PEER_SERVICE_INTERVAL;
|
||||
|
||||
|
@ -150,6 +165,7 @@ impl Peer {
|
|||
ephemeral_secret: Mutex::new(None),
|
||||
ephemeral_pair: Mutex::new(None),
|
||||
paths: Mutex::new(Vec::new()),
|
||||
reported_local_ip: Mutex::new(None),
|
||||
last_send_time_ticks: AtomicI64::new(0),
|
||||
last_receive_time_ticks: AtomicI64::new(0),
|
||||
last_forward_time_ticks: AtomicI64::new(0),
|
||||
|
@ -165,6 +181,16 @@ impl Peer {
|
|||
})
|
||||
}
|
||||
|
||||
/// Get the next packet initialization vector.
|
||||
///
|
||||
/// For Salsa20/12 with Poly1305 this is the packet ID. For AES-GMAC-SIV the packet ID is
|
||||
/// not known until the packet is encrypted, since it's the first 64 bits of the GMAC-SIV
|
||||
/// tag.
|
||||
#[inline(always)]
|
||||
pub(crate) fn next_packet_iv(&self) -> PacketID {
|
||||
self.packet_iv_counter.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Receive, decrypt, authenticate, and process an incoming packet from this peer.
|
||||
/// If the packet comes in multiple fragments, the fragments slice should contain all
|
||||
/// those fragments after the main packet header and first chunk.
|
||||
|
@ -185,12 +211,9 @@ impl Peer {
|
|||
}
|
||||
|
||||
// FIPS note: for FIPS purposes the HMAC-SHA384 tag at the end of V2 HELLOs
|
||||
// will be considered the "real" handshake authentication.
|
||||
let key = salsa_derive_per_packet_key(&secret.secret, header, payload.len());
|
||||
let mut salsa = Salsa::new(&key.0[0..32], header.id_bytes(), true).unwrap();
|
||||
let mut poly1305_key = [0_u8; 32];
|
||||
salsa.crypt_in_place(&mut poly1305_key);
|
||||
let mut poly = Poly1305::new(&poly1305_key).unwrap();
|
||||
// will be considered the "real" handshake authentication. This authentication
|
||||
// is technically deprecated in V2.
|
||||
let (_, mut poly) = salsa_poly_create(secret, header, packet.len());
|
||||
poly.update(payload.as_bytes());
|
||||
|
||||
if poly.finish()[0..8].eq(&header.message_auth) {
|
||||
|
@ -203,12 +226,7 @@ impl Peer {
|
|||
}
|
||||
|
||||
CIPHER_SALSA2012_POLY1305 => {
|
||||
let key = salsa_derive_per_packet_key(&secret.secret, header, payload.len());
|
||||
let mut salsa = Salsa::new(&key.0[0..32], header.id_bytes(), true).unwrap();
|
||||
let mut poly1305_key = [0_u8; 32];
|
||||
salsa.crypt_in_place(&mut poly1305_key);
|
||||
let mut poly = Poly1305::new(&poly1305_key).unwrap();
|
||||
|
||||
let (mut salsa, mut poly) = salsa_poly_create(secret, header, packet.len());
|
||||
poly.update(packet_frag0_payload_bytes);
|
||||
let _ = payload.append_and_init_bytes(packet_frag0_payload_bytes.len(), |b| salsa.crypt(packet_frag0_payload_bytes, b));
|
||||
for f in fragments.iter() {
|
||||
|
@ -217,7 +235,6 @@ impl Peer {
|
|||
let _ = payload.append_and_init_bytes(f.len(), |b| salsa.crypt(f, b));
|
||||
}));
|
||||
}
|
||||
|
||||
if poly.finish()[0..8].eq(&header.message_auth) {
|
||||
break;
|
||||
}
|
||||
|
@ -227,12 +244,10 @@ impl Peer {
|
|||
let mut aes = secret.aes.get();
|
||||
aes.decrypt_init(&header.aes_gmac_siv_tag());
|
||||
aes.decrypt_set_aad(&header.aad_bytes());
|
||||
|
||||
let _ = payload.append_and_init_bytes(packet_frag0_payload_bytes.len(), |b| aes.decrypt(packet_frag0_payload_bytes, b));
|
||||
for f in fragments.iter() {
|
||||
let _ = f.as_ref().map(|f| f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| payload.append_and_init_bytes(f.len(), |b| aes.decrypt(f, b))));
|
||||
}
|
||||
|
||||
if aes.decrypt_finish() {
|
||||
break;
|
||||
}
|
||||
|
@ -283,6 +298,12 @@ impl Peer {
|
|||
});
|
||||
}
|
||||
|
||||
/// Get current best path or None if there are no direct paths to this peer.
|
||||
#[inline(always)]
|
||||
pub(crate) fn best_path(&self) -> Option<Arc<Path>> {
|
||||
self.paths.lock().last().map(|p| p.clone())
|
||||
}
|
||||
|
||||
/// Send a packet to this peer.
|
||||
///
|
||||
/// This will go directly if there is an active path, or otherwise indirectly
|
||||
|
@ -290,6 +311,7 @@ impl Peer {
|
|||
pub(crate) fn send<CI: VL1CallerInterface>(&self, ci: &CI, time_ticks: i64, data: PacketBuffer) {
|
||||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||
let _ = self.total_bytes_sent.fetch_add(data.len() as u64, Ordering::Relaxed);
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Forward a packet to this peer.
|
||||
|
@ -305,6 +327,123 @@ impl Peer {
|
|||
todo!()
|
||||
}
|
||||
|
||||
/// Send a HELLO to this peer.
|
||||
///
|
||||
/// If try_new_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.
|
||||
pub(crate) fn send_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, try_new_endpoint: Option<Endpoint>) {
|
||||
let (endpoint, path) = try_new_endpoint.as_ref().map_or_else(|| {
|
||||
self.best_path().map_or_else(|| {
|
||||
node.root().map_or_else(|| (None, None), |root| {
|
||||
root.best_path().map_or_else(|| (None, None), |bp| (Some(&bp.endpoint), Some(bp)))
|
||||
})
|
||||
}, |bp| (Some(&bp.endpoint), Some(bp)))
|
||||
}, |ep| (Some(ep), None));
|
||||
let _ = endpoint.map(|endpoint| {
|
||||
let mut buf = node.get_packet_buffer();
|
||||
let this_peer_is_root = node.is_root(self);
|
||||
|
||||
debug_assert!(buf.append_and_init_struct(|header: &mut PacketHeader| {
|
||||
header.id = self.next_packet_iv();
|
||||
header.dest = self.identity.address().to_bytes();
|
||||
header.src = node.address().to_bytes();
|
||||
header.flags_cipher_hops = CIPHER_NOCRYPT_POLY1305;
|
||||
}).is_ok());
|
||||
debug_assert!(buf.append_and_init_struct(|header: &mut message_component_structs::HelloFixedHeaderFields| {
|
||||
header.verb = VERB_VL1_HELLO | VERB_FLAG_HMAC;
|
||||
header.version_proto = VERSION_PROTO;
|
||||
header.version_major = VERSION_MAJOR;
|
||||
header.version_minor = VERSION_MINOR;
|
||||
header.version_revision = (VERSION_REVISION as u16).to_be();
|
||||
header.timestamp = (ci.time_ticks() as u64).to_be();
|
||||
}).is_ok());
|
||||
|
||||
debug_assert!(self.identity.marshal(&mut buf, false).is_ok());
|
||||
debug_assert!(endpoint.marshal(&mut buf).is_ok());
|
||||
|
||||
let aes_ctr_iv_position = buf.len();
|
||||
debug_assert!(buf.append_and_init_bytes_fixed(|iv: &mut [u8; 18]| {
|
||||
crate::crypto::random::fill_bytes_secure(&mut buf[0..12]);
|
||||
todo!()
|
||||
}));
|
||||
let dictionary_position = buf.len();
|
||||
let mut dict = Dictionary::new();
|
||||
dict.set_u64(HELLO_DICT_KEY_INSTANCE_ID, node.instance_id);
|
||||
dict.set_u64(HELLO_DICT_KEY_CLOCK, ci.time_clock() as u64);
|
||||
let _ = node.locator().map(|loc| loc.to_bytes().map(|loc| dict.set_bytes(HELLO_DICT_KEY_LOCATOR, loc)));
|
||||
let _ = self.ephemeral_pair.lock().map(|ephemeral_pair| {
|
||||
dict.set_bytes(HELLO_DICT_KEY_EPHEMERAL_C25519, ephemeral_pair.c25519.public_bytes().to_vec());
|
||||
dict.set_bytes(HELLO_DICT_KEY_EPHEMERAL_P521, ephemeral_pair.p521.public_key_bytes().to_vec());
|
||||
});
|
||||
if this_peer_is_root {
|
||||
// If the peer is a root we include some extra information for diagnostic and statistics
|
||||
// purposes such as the CPU type, bits, and OS info. This is not sent to other peers.
|
||||
dict.set_str(HELLO_DICT_KEY_SYS_ARCH, std::env::consts::ARCH);
|
||||
dict.set_u64(HELLO_DICT_KEY_SYS_BITS, (std::mem::size_of::<*const ()>() * 8) as u64);
|
||||
dict.set_str(HELLO_DICT_KEY_OS_NAME, std::env::consts::OS);
|
||||
}
|
||||
let mut flags = String::new();
|
||||
if node.fips_mode {
|
||||
flags.push('F');
|
||||
}
|
||||
if node.wimp {
|
||||
flags.push('w');
|
||||
}
|
||||
dict.set_str(HELLO_DICT_KEY_FLAGS, flags.as_str());
|
||||
assert!(dict.write_to(&mut buf).is_ok());
|
||||
|
||||
let mut dict_aes = self.static_secret_hello_dictionary.lock();
|
||||
dict_aes.init(&buf.as_bytes()[aes_ctr_iv_position..aes_ctr_iv_position + 12]);
|
||||
dict_aes.crypt_in_place(&mut buf.as_bytes_mut()[dictionary_position..]);
|
||||
drop(dict_aes);
|
||||
|
||||
assert!(buf.append_bytes_fixed(&SHA384::hmac(self.static_secret_packet_hmac.as_ref(), &buf.as_bytes()[PACKET_HEADER_SIZE + 1..])).is_ok());
|
||||
|
||||
let (_, mut poly) = salsa_poly_create(secret, header, packet.len());
|
||||
poly.update(buf.as_bytes_starting_at(PACKET_HEADER_SIZE).unwrap());
|
||||
buf.as_bytes_mut()[HEADER_MAC_FIELD_INDEX..HEADER_MAC_FIELD_INDEX + 8].copy_from_slice(&poly.finish()[0..8]);
|
||||
|
||||
ci.wire_send(endpoint, path.map(|p| p.local_socket), path.map(|p| p.local_interface), buf, 0);
|
||||
});
|
||||
}
|
||||
|
||||
/// Called every INTERVAL during background tasks.
|
||||
#[inline(always)]
|
||||
pub(crate) fn on_interval<CI: VL1CallerInterface>(&self, ct: &CI, time_ticks: i64) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_error<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_ok<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_whois<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_rendezvous<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_echo<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_push_direct_paths<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_user_message<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
/// Get the remote version of this peer: major, minor, revision, and build.
|
||||
/// Returns None if it's not yet known.
|
||||
pub fn version(&self) -> Option<[u16; 4]> {
|
||||
|
@ -325,41 +464,4 @@ impl Peer {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Called every INTERVAL during background tasks.
|
||||
#[inline(always)]
|
||||
pub fn on_interval<CI: VL1CallerInterface>(&self, ct: &CI, time_ticks: i64) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_error<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_ok<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_whois<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_rendezvous<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_echo<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_push_direct_paths<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_user_message<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, packet: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,92 @@ pub const VERB_VL1_ECHO: u8 = 0x08;
|
|||
pub const VERB_VL1_PUSH_DIRECT_PATHS: u8 = 0x10;
|
||||
pub const VERB_VL1_USER_MESSAGE: u8 = 0x14;
|
||||
|
||||
pub(crate) const HELLO_DICT_KEY_INSTANCE_ID: &'static str = "I";
|
||||
pub(crate) const HELLO_DICT_KEY_CLOCK: &'static str = "C";
|
||||
pub(crate) const HELLO_DICT_KEY_LOCATOR: &'static str = "L";
|
||||
pub(crate) const HELLO_DICT_KEY_EPHEMERAL_C25519: &'static str = "E0";
|
||||
pub(crate) const HELLO_DICT_KEY_EPHEMERAL_P521: &'static str = "E1";
|
||||
pub(crate) const HELLO_DICT_KEY_EPHEMERAL_ACK: &'static str = "e";
|
||||
pub(crate) const HELLO_DICT_KEY_HELLO_ORIGIN: &'static str = "@";
|
||||
pub(crate) const HELLO_DICT_KEY_SYS_ARCH: &'static str = "Sa";
|
||||
pub(crate) const HELLO_DICT_KEY_SYS_BITS: &'static str = "Sb";
|
||||
pub(crate) const HELLO_DICT_KEY_OS_NAME: &'static str = "So";
|
||||
pub(crate) const HELLO_DICT_KEY_OS_VERSION: &'static str = "Sv";
|
||||
pub(crate) const HELLO_DICT_KEY_OS_VARIANT: &'static str = "St";
|
||||
pub(crate) const HELLO_DICT_KEY_VENDOR: &'static str = "V";
|
||||
pub(crate) const HELLO_DICT_KEY_FLAGS: &'static str = "+";
|
||||
|
||||
/// Index of packet verb after header.
|
||||
pub const PACKET_VERB_INDEX: usize = 27;
|
||||
|
||||
/// Index of destination in both fragment and full packet headers.
|
||||
pub const PACKET_DESTINATION_INDEX: usize = 8;
|
||||
|
||||
/// Index of 8-byte MAC field in packet header.
|
||||
pub const HEADER_MAC_FIELD_INDEX: usize = 19;
|
||||
|
||||
/// Mask to select cipher from header flags field.
|
||||
pub const HEADER_FLAGS_FIELD_MASK_CIPHER: u8 = 0x30;
|
||||
|
||||
/// Mask to select packet hops from header flags field.
|
||||
pub const HEADER_FLAGS_FIELD_MASK_HOPS: u8 = 0x07;
|
||||
|
||||
/// Mask to select packet hops from header flags field.
|
||||
pub const HEADER_FLAGS_FIELD_MASK_HIDE_HOPS: u8 = 0xf8;
|
||||
|
||||
/// Index of hops/flags field
|
||||
pub const HEADER_FLAGS_FIELD_INDEX: usize = 18;
|
||||
|
||||
/// Packet is not encrypted but contains a Poly1305 MAC of the plaintext.
|
||||
/// Poly1305 is initialized with Salsa20/12 in the same manner as SALSA2012_POLY1305.
|
||||
pub const CIPHER_NOCRYPT_POLY1305: u8 = 0x00;
|
||||
|
||||
/// Packet is encrypted and authenticated with Salsa20/12 and Poly1305.
|
||||
/// Construction is the same as that which is used in the NaCl secret box functions.
|
||||
pub const CIPHER_SALSA2012_POLY1305: u8 = 0x10;
|
||||
|
||||
/// Formerly 'NONE' which is deprecated; reserved for future use.
|
||||
pub const CIPHER_RESERVED: u8 = 0x20;
|
||||
|
||||
/// Packet is encrypted and authenticated with AES-GMAC-SIV (AES-256).
|
||||
pub const CIPHER_AES_GMAC_SIV: u8 = 0x30;
|
||||
|
||||
/// Header (outer) flag indicating that this packet has additional fragments.
|
||||
pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40;
|
||||
|
||||
/// Minimum size of a fragment.
|
||||
pub const FRAGMENT_SIZE_MIN: usize = 16;
|
||||
|
||||
/// Size of fragment header after which data begins.
|
||||
pub const FRAGMENT_HEADER_SIZE: usize = 16;
|
||||
|
||||
/// Maximum allowed number of fragments.
|
||||
pub const FRAGMENT_COUNT_MAX: usize = 8;
|
||||
|
||||
/// Index of packet fragment indicator byte to detect fragments.
|
||||
pub const FRAGMENT_INDICATOR_INDEX: usize = 13;
|
||||
|
||||
/// 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 after verb is authenticated with HMAC-SHA384.
|
||||
pub const VERB_FLAG_HMAC: 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;
|
||||
|
||||
/// Maximum number of packet hops allowed by the protocol.
|
||||
pub const PROTOCOL_MAX_HOPS: u8 = 7;
|
||||
|
||||
/// Maximum number of hops to allow.
|
||||
pub const FORWARD_MAX_HOPS: u8 = 3;
|
||||
|
||||
/// A unique packet identifier, also the cryptographic nonce.
|
||||
///
|
||||
/// Packet IDs are stored as u64s for efficiency but they should be treated as
|
||||
|
@ -151,6 +237,27 @@ impl FragmentHeader {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) mod message_component_structs {
|
||||
#[repr(packed)]
|
||||
pub struct HelloFixedHeaderFields {
|
||||
pub verb: u8,
|
||||
pub version_proto: u8,
|
||||
pub version_major: u8,
|
||||
pub version_minor: u8,
|
||||
pub version_revision: u16,
|
||||
pub timestamp: u64,
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct OkHelloFixedHeaderFields {
|
||||
pub timestamp_echo: u64,
|
||||
pub version_proto: u8,
|
||||
pub version_major: u8,
|
||||
pub version_minor: u8,
|
||||
pub version_revision: u16,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::size_of;
|
||||
|
|
Loading…
Add table
Reference in a new issue