mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 08:57:26 +02:00
Tetanus noise xk (#1881) * Noise XK work in progress. * A whole lot more Noise_XK work... exchange almost done. * Delete a bunch of commented out old Noise_IK code. * Add back in send() and a few other things to Noise_XK ZSSP. * Some p384 experiment in attic * A ton of ZSSP work, and put MPL on ZSSP. * updated kbkdf512 to use the modern nist standard * Parameterize KBKDF on resulting output key size the way NIST likes. * updated variable comment * Make the label a const parameter on kbkdf. * updated variable comment * Add MPL to utils and other stuff. * layout tweak * Some more ZSSP work and a VDF we may use. * warning removal * More ZSSP work, add benchmarks for mimcvdf. * Almost ready to test... * Build fix. * Add automatic retransmission in the earliest stages of session init. * Just about ready to test... wow. * It opens a session. * ZSSP basically works... --------- Co-authored-by: mamoniot <mamoniot@protonmail.com> Warning removal. remove old docs Remove old tests from ZSSP, new test in main() can also be made into a unit test in the future. Add key_info() to get key information. Rekeying is now tested and works. Show key fingerprint. Factor out memory:: stuff, does not appear to have any real performance benefit. Rework defragmentation, and it now tolerates very poor link quality pretty well. Circuit breaker for incoming defrag queue, and ZSSP now works very well even under very poor network conditions. Format tweak. ZSSP API updates. Just a bit of final ZSSP cleanup before moving to another thing.
161 lines
5.5 KiB
Rust
161 lines
5.5 KiB
Rust
/* 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/
|
|
*/
|
|
|
|
use std::error::Error;
|
|
use std::fmt::{Debug, Display};
|
|
use std::io::Write;
|
|
|
|
use crate::buffer::Buffer;
|
|
|
|
/// Must be larger than any object we want to use with to_bytes() or from_bytes().
|
|
/// This hack can go away once Rust allows us to reference trait consts as generics.
|
|
const TEMP_BUF_SIZE: usize = 8192;
|
|
|
|
/// A super-lightweight zero-allocation serialization interface.
|
|
pub trait Marshalable: Sized {
|
|
const MAX_MARSHAL_SIZE: usize;
|
|
|
|
/// Write this object into a buffer.
|
|
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError>;
|
|
|
|
/// Read this object from a buffer.
|
|
///
|
|
/// The supplied cursor is advanced by the number of bytes read. If an Err is returned
|
|
/// the value of the cursor is undefined but likely points to about where the error
|
|
/// occurred. It may also point beyond the buffer, which would indicate an overrun error.
|
|
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> Result<Self, UnmarshalError>;
|
|
|
|
/// Write this marshalable entity into a buffer of the given size.
|
|
///
|
|
/// This will return an Err if the buffer is too small or some other error occurs. It's just
|
|
/// a shortcut to creating a buffer and marshaling into it.
|
|
#[inline]
|
|
fn to_buffer<const BL: usize>(&self) -> Result<Buffer<BL>, UnmarshalError> {
|
|
let mut tmp = Buffer::new();
|
|
self.marshal(&mut tmp)?;
|
|
Ok(tmp)
|
|
}
|
|
|
|
/// Unmarshal this object from a buffer.
|
|
///
|
|
/// This is just a shortcut to calling unmarshal() with a zero cursor and then discarding the cursor.
|
|
#[inline]
|
|
fn from_buffer<const BL: usize>(buf: &Buffer<BL>) -> Result<Self, UnmarshalError> {
|
|
let mut tmp = 0;
|
|
Self::unmarshal(buf, &mut tmp)
|
|
}
|
|
|
|
/// Marshal and convert to a Rust vector.
|
|
#[inline]
|
|
fn to_bytes(&self) -> Vec<u8> {
|
|
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
|
|
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new();
|
|
assert!(self.marshal(&mut tmp).is_ok()); // panics if TEMP_BUF_SIZE is too small
|
|
tmp.as_bytes().to_vec()
|
|
}
|
|
|
|
/// Unmarshal from a raw slice.
|
|
#[inline]
|
|
fn from_bytes(b: &[u8]) -> Result<Self, UnmarshalError> {
|
|
if b.len() <= TEMP_BUF_SIZE {
|
|
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new_boxed();
|
|
assert!(tmp.append_bytes(b).is_ok());
|
|
let mut cursor = 0;
|
|
Self::unmarshal(&tmp, &mut cursor)
|
|
} else {
|
|
Err(UnmarshalError::OutOfBounds)
|
|
}
|
|
}
|
|
|
|
/// Marshal a slice of marshalable objects to a concatenated byte vector.
|
|
#[inline]
|
|
fn marshal_multiple_to_bytes(objects: &[Self]) -> Result<Vec<u8>, UnmarshalError> {
|
|
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
|
|
let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new();
|
|
let mut v: Vec<u8> = Vec::with_capacity(objects.len() * Self::MAX_MARSHAL_SIZE);
|
|
for i in objects.iter() {
|
|
i.marshal(&mut tmp)?;
|
|
let _ = v.write_all(tmp.as_bytes());
|
|
tmp.clear();
|
|
}
|
|
Ok(v)
|
|
}
|
|
|
|
/// Unmarshal a concatenated byte slice of marshalable objects.
|
|
#[inline]
|
|
fn unmarshal_multiple_from_bytes(mut bytes: &[u8]) -> Result<Vec<Self>, UnmarshalError> {
|
|
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
|
|
let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new();
|
|
let mut v: Vec<Self> = Vec::new();
|
|
while bytes.len() > 0 {
|
|
let chunk_size = bytes.len().min(Self::MAX_MARSHAL_SIZE);
|
|
if tmp.append_bytes(&bytes[..chunk_size]).is_err() {
|
|
return Err(UnmarshalError::OutOfBounds);
|
|
}
|
|
let mut cursor = 0;
|
|
v.push(Self::unmarshal(&mut tmp, &mut cursor)?);
|
|
if cursor == 0 {
|
|
return Err(UnmarshalError::InvalidData);
|
|
}
|
|
let _ = tmp.erase_first_n(cursor);
|
|
bytes = &bytes[chunk_size..];
|
|
}
|
|
Ok(v)
|
|
}
|
|
|
|
/// Unmarshal a buffer with a byte slice of marshalable objects.
|
|
#[inline]
|
|
fn unmarshal_multiple<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize, eof: usize) -> Result<Vec<Self>, UnmarshalError> {
|
|
let mut v: Vec<Self> = Vec::new();
|
|
while *cursor < eof {
|
|
v.push(Self::unmarshal(buf, cursor)?);
|
|
}
|
|
Ok(v)
|
|
}
|
|
}
|
|
|
|
pub enum UnmarshalError {
|
|
OutOfBounds,
|
|
InvalidData,
|
|
UnsupportedVersion,
|
|
IoError(std::io::Error),
|
|
}
|
|
|
|
impl Display for UnmarshalError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::OutOfBounds => f.write_str("out of bounds"),
|
|
Self::InvalidData => f.write_str("invalid data"),
|
|
Self::UnsupportedVersion => f.write_str("unsupported version"),
|
|
Self::IoError(e) => f.write_str(e.to_string().as_str()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Debug for UnmarshalError {
|
|
#[inline(always)]
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
Display::fmt(self, f)
|
|
}
|
|
}
|
|
|
|
impl Error for UnmarshalError {}
|
|
|
|
impl From<crate::buffer::OutOfBoundsError> for UnmarshalError {
|
|
#[inline(always)]
|
|
fn from(_: crate::buffer::OutOfBoundsError) -> Self {
|
|
Self::OutOfBounds
|
|
}
|
|
}
|
|
|
|
impl From<std::io::Error> for UnmarshalError {
|
|
#[inline(always)]
|
|
fn from(e: std::io::Error) -> Self {
|
|
Self::IoError(e)
|
|
}
|
|
}
|