Reorg some stuff, build out send_hello()

This commit is contained in:
Adam Ierymenko 2021-08-12 15:56:42 -04:00
parent 0395943d31
commit 209aeefac3
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
9 changed files with 350 additions and 158 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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