mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-03 19:13:43 +02:00
Moar rust.
This commit is contained in:
parent
5515be2e25
commit
d70d958b5c
18 changed files with 911 additions and 84 deletions
7
network-hypervisor/Cargo.lock
generated
7
network-hypervisor/Cargo.lock
generated
|
@ -9,6 +9,12 @@ dependencies = [
|
|||
"gcrypt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
|
@ -413,6 +419,7 @@ name = "zerotier-network-hypervisor"
|
|||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"aes-gmac-siv",
|
||||
"base64",
|
||||
"ed25519-dalek",
|
||||
"gcrypt",
|
||||
"rand_core",
|
||||
|
|
|
@ -9,3 +9,4 @@ aes-gmac-siv = { path = "../aes-gmac-siv" }
|
|||
x25519-dalek = "^1"
|
||||
ed25519-dalek = "^1"
|
||||
gcrypt = "^0"
|
||||
base64 = "^0"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::convert::TryInto;
|
||||
use ed25519_dalek::Digest;
|
||||
use std::io::Write;
|
||||
|
||||
use ed25519_dalek::Digest;
|
||||
|
||||
pub const C25519_PUBLIC_KEY_SIZE: usize = 32;
|
||||
pub const C25519_SECRET_KEY_SIZE: usize = 32;
|
||||
pub const C25519_SHARED_SECRET_SIZE: usize = 32;
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::convert::TryInto;
|
|||
use std::io::Write;
|
||||
|
||||
pub const SHA512_HASH_SIZE: usize = 64;
|
||||
pub const SHA384_HASH_SIZE: usize = 64;
|
||||
pub const SHA384_HASH_SIZE: usize = 48;
|
||||
|
||||
pub struct SHA512(gcrypt::digest::MessageDigest);
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ impl P521KeyPair {
|
|||
}
|
||||
|
||||
/// Create an ECDSA signature of the input message.
|
||||
/// Message data does not need to be pre-hashed.
|
||||
pub fn sign(&self, msg: &[u8]) -> Option<[u8; P521_ECDSA_SIGNATURE_SIZE]> {
|
||||
let data = SExpression::from_str(unsafe { std::str::from_utf8_unchecked(&hash_to_data_sexp(msg)) }).unwrap();
|
||||
gcrypt::pkey::sign(&self.secret_key_for_ecdsa, &data).map_or(None, |sig| {
|
||||
|
@ -171,6 +172,8 @@ impl P521PublicKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// Verify a signature.
|
||||
/// Message data does not need to be pre-hashed.
|
||||
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> bool {
|
||||
if signature.len() == P521_ECDSA_SIGNATURE_SIZE {
|
||||
let data = SExpression::from_str(unsafe { std::str::from_utf8_unchecked(&hash_to_data_sexp(msg)) }).unwrap();
|
||||
|
|
|
@ -2,3 +2,4 @@ pub mod crypto;
|
|||
pub mod vl1;
|
||||
pub mod util;
|
||||
pub mod error;
|
||||
pub mod vl2;
|
||||
|
|
|
@ -1 +1,42 @@
|
|||
pub mod hex;
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_store_be_u16(i: u16, d: &mut [u8]) {
|
||||
d[0] = (i >> 8) as u8;
|
||||
d[1] = i as u8;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_store_be_u32(i: u32, d: &mut [u8]) {
|
||||
d[0] = (i >> 24) as u8;
|
||||
d[1] = (i >> 16) as u8;
|
||||
d[2] = (i >> 8) as u8;
|
||||
d[3] = i as u8;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_store_be_u64(i: u64, d: &mut [u8]) {
|
||||
d[0] = (i >> 56) as u8;
|
||||
d[1] = (i >> 48) as u8;
|
||||
d[2] = (i >> 40) as u8;
|
||||
d[3] = (i >> 32) as u8;
|
||||
d[4] = (i >> 24) as u8;
|
||||
d[5] = (i >> 16) as u8;
|
||||
d[6] = (i >> 8) as u8;
|
||||
d[7] = i as u8;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_load_be_u16(d: &[u8]) -> u16 {
|
||||
(d[0] as u16) << 8 | (d[1] as u16)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_load_be_u32(d: &[u8]) -> u32 {
|
||||
(d[0] as u32) << 24 | (d[1] as u32) << 16 | (d[2] as u32) << 8 | (d[3] as u32)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn integer_load_be_u64(d: &[u8]) -> u64 {
|
||||
(d[0] as u64) << 56 | (d[1] as u64) << 48 | (d[2] as u64) << 40 | (d[3] as u64) << 32 | (d[4] as u64) << 24 | (d[5] as u64) << 16 | (d[6] as u64) << 8 | (d[7] as u64)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ impl Address {
|
|||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Address, InvalidFormatError> {
|
||||
if b.len() >= 5 {
|
||||
Ok(Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 as u64 | b[4] as u64))
|
||||
Ok(Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64))
|
||||
} else {
|
||||
Err(InvalidFormatError("invalid ZeroTier address"))
|
||||
}
|
||||
|
@ -25,13 +25,13 @@ impl Address {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.0 != 0 && !self.is_reserved()
|
||||
pub fn is_nil(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
self.0 == 0
|
||||
pub fn is_valid(&self) -> bool {
|
||||
!self.is_nil() && !self.is_reserved()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -46,7 +46,6 @@ impl Address {
|
|||
}
|
||||
|
||||
impl ToString for Address {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
let mut v = self.0 << 24;
|
||||
let mut s = String::new();
|
||||
|
@ -62,7 +61,6 @@ impl ToString for Address {
|
|||
impl FromStr for Address {
|
||||
type Err = InvalidFormatError;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Address::from_bytes(crate::util::hex::from_string(s).as_slice())
|
||||
}
|
||||
|
@ -85,7 +83,7 @@ impl Hash for Address {
|
|||
impl From<&[u8; 5]> for Address {
|
||||
#[inline(always)]
|
||||
fn from(b: &[u8; 5]) -> Address {
|
||||
Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 as u64 | b[4] as u64)
|
||||
Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use std::mem::size_of;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
const FAULT_BIT: usize = 1_usize << ((size_of::<usize>() * 8) - 1);
|
||||
const FAULT_CLEAR_MASK: usize = !FAULT_BIT;
|
||||
use std::io::Write;
|
||||
|
||||
/// Annotates a type as containing only primitive types like integers and arrays.
|
||||
/// This means it's safe to abuse with raw copy, raw zero, or "type punning."
|
||||
|
@ -19,13 +17,6 @@ unsafe impl RawObject for NoHeader {}
|
|||
/// This also supports a generic header that must be a RawObject and will always be
|
||||
/// placed at the beginning of the buffer. When you construct or clear() a buffer
|
||||
/// space will be maintained for the header. Use NoHeader if you don't want a header.
|
||||
///
|
||||
/// If a write overflow occurs during append operations, the operations fail silently
|
||||
/// without increasing the buffer's size and an internal fault bit is set. The
|
||||
/// check_overflow() method must be used before the buffer is actually complete to
|
||||
/// ensure that no write overflows occurred. If this check isn't performed a buffer
|
||||
/// could be used with incomplete or corrupt data, but no crash or memory errors will
|
||||
/// occur.
|
||||
#[derive(Clone)]
|
||||
pub struct Buffer<H: RawObject, const L: usize>(usize, [u8; L], PhantomData<H>);
|
||||
|
||||
|
@ -34,7 +25,6 @@ unsafe impl<H: RawObject, const L: usize> RawObject for Buffer<H, L> {}
|
|||
impl<H: RawObject, const L: usize> Default for Buffer<H, L> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
assert!(size_of::<H>() <= L);
|
||||
Buffer(size_of::<H>(), [0_u8; L], PhantomData::default())
|
||||
}
|
||||
}
|
||||
|
@ -45,125 +35,285 @@ impl<H: RawObject, const L: usize> Buffer<H, L> {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
/// Returns true if there has been a write overflow.
|
||||
#[inline(always)]
|
||||
pub fn check_overflow(&self) -> bool {
|
||||
(self.0 & FAULT_BIT) != 0
|
||||
}
|
||||
|
||||
/// Get a slice containing the entire buffer in raw form including the header.
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.1[0..(self.0 & FAULT_CLEAR_MASK)]
|
||||
&self.1[0..self.0]
|
||||
}
|
||||
|
||||
/// Erase contents and reset size to the size of the header.
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
self.1[0..self.0].fill(0);
|
||||
self.0 = size_of::<H>();
|
||||
self.1.fill(0);
|
||||
}
|
||||
|
||||
/// Get the length of this buffer (including header, if any).
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0 & FAULT_CLEAR_MASK
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Get a reference to the header (in place).
|
||||
#[inline(always)]
|
||||
pub fn header(&self) -> &H {
|
||||
debug_assert!(size_of::<H>() <= L);
|
||||
unsafe { &*self.1.as_ptr().cast::<H>() }
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the header (in place).
|
||||
#[inline(always)]
|
||||
pub fn header_mut(&mut self) -> &mut H {
|
||||
debug_assert!(size_of::<H>() <= L);
|
||||
unsafe { &mut *self.1.as_mut_ptr().cast::<H>() }
|
||||
}
|
||||
|
||||
/// Append a packed structure and initializing it in place via the supplied function.
|
||||
///
|
||||
/// If an overflow occurs the overflow fault bit is set internally (see check_overflow())
|
||||
/// and the supplied function will never be called.
|
||||
/// Append a packed structure and call a function to initialize it in place.
|
||||
/// Anything not initialized will be zero.
|
||||
#[inline(always)]
|
||||
pub fn append_and_init_struct<T: RawObject, F: FnOnce(&mut T)>(&mut self, initializer: F) {
|
||||
let bl = self.0;
|
||||
let s = bl + size_of::<T>();
|
||||
if s <= L {
|
||||
pub fn append_and_init_struct<T: RawObject, R, F: FnOnce(&mut T) -> R>(&mut self, initializer: F) -> std::io::Result<R> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + size_of::<T>();
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
unsafe {
|
||||
self.0 = s;
|
||||
initializer(&mut *self.1.as_mut_ptr().cast::<u8>().offset(bl as isize).cast::<T>());
|
||||
Ok(initializer(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()))
|
||||
}
|
||||
} else {
|
||||
self.0 = bl | FAULT_BIT;
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Append and initialize a byte array with a fixed size set at compile time.
|
||||
///
|
||||
/// This is more efficient than setting a size at runtime as it may allow the compiler to
|
||||
/// skip some bounds checking.
|
||||
///
|
||||
/// If an overflow occurs the overflow fault bit is set internally (see check_overflow())
|
||||
/// and the supplied function will never be called.
|
||||
/// skip some bounds checking. Any bytes not initialized will be zero.
|
||||
#[inline(always)]
|
||||
pub fn append_and_init_bytes_fixed<F: FnOnce(&mut [u8; N]), const N: usize>(&mut self, initializer: F) {
|
||||
let bl = self.0;
|
||||
let s = bl + N;
|
||||
if s <= L {
|
||||
pub fn append_and_init_bytes_fixed<R, F: FnOnce(&mut [u8; N]) -> R, const N: usize>(&mut self, initializer: F) -> std::io::Result<R> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + N;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
unsafe {
|
||||
let ptr = self.1.as_mut_ptr().cast::<u8>().offset(bl as isize);
|
||||
self.0 = s;
|
||||
initializer(&mut *ptr.cast::<[u8; N]>());
|
||||
Ok(initializer(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<[u8; N]>()))
|
||||
}
|
||||
} else {
|
||||
self.0 = bl | FAULT_BIT;
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Append and initialize a slice with a size that is set at runtime.
|
||||
///
|
||||
/// If an overflow occurs the overflow fault bit is set internally (see check_overflow())
|
||||
/// and the supplied function will never be called.
|
||||
/// Any bytes not initialized will be zero.
|
||||
#[inline(always)]
|
||||
pub fn append_and_init_bytes<F: FnOnce(&mut [u8])>(&mut self, l: usize, initializer: F) {
|
||||
let bl = self.0;
|
||||
let s = bl + l;
|
||||
if s <= L {
|
||||
self.0 = s;
|
||||
initializer(&mut self.1[bl..s]);
|
||||
pub fn append_and_init_bytes<R, F: FnOnce(&mut [u8]) -> R>(&mut self, l: usize, initializer: F) -> std::io::Result<R> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + l;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
Ok(initializer(&mut self.1[ptr..end]))
|
||||
} else {
|
||||
self.0 = bl | FAULT_BIT;
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_payload(&self) -> Reader<H, L> {
|
||||
Reader {
|
||||
buffer: self,
|
||||
ptr: size_of::<H>(),
|
||||
/// Append a dynamic byte slice (copy into buffer).
|
||||
/// Use append_and_init_ functions if possible as these avoid extra copies.
|
||||
#[inline(always)]
|
||||
fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + buf.len();
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
self.1[ptr..end].copy_from_slice(buf);
|
||||
Ok(())
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reader<'a, H: RawObject, const L: usize> {
|
||||
ptr: usize,
|
||||
buffer: &'a Buffer<H, L>,
|
||||
}
|
||||
/// Append a fixed length byte array (copy into buffer).
|
||||
/// Use append_and_init_ functions if possible as these avoid extra copies.
|
||||
#[inline(always)]
|
||||
fn append_bytes_fixed<const S: usize>(&mut self, buf: &[u8; S]) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + S;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
self.1[ptr..end].copy_from_slice(buf);
|
||||
Ok(())
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H: RawObject, const L: usize> Reader<'a, H, L> {
|
||||
pub fn read_struct<T: RawObject, R, F: FnOnce(&T, &mut Self) -> bool>(&mut self, visitor: F) -> bool {
|
||||
let rl = self.ptr;
|
||||
let s = rl + size_of::<T>();
|
||||
if s <= L {
|
||||
/// Append a byte
|
||||
#[inline(always)]
|
||||
fn append_u8(&mut self, i: u8) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
if ptr < L {
|
||||
self.0 = ptr + 1;
|
||||
self.1[ptr] = i;
|
||||
Ok(())
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a 16-bit integer (in big-endian form)
|
||||
#[inline(always)]
|
||||
fn append_u16(&mut self, i: u16) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + 2;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
crate::util::integer_store_be_u16(i, &mut self.1[ptr..end]);
|
||||
Ok(())
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a 32-bit integer (in big-endian form)
|
||||
#[inline(always)]
|
||||
fn append_u32(&mut self, i: u32) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + 4;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
crate::util::integer_store_be_u32(i, &mut self.1[ptr..end]);
|
||||
Ok(())
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a 64-bit integer (in big-endian form)
|
||||
#[inline(always)]
|
||||
fn append_u64(&mut self, i: u64) -> std::io::Result<()> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + 8;
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
crate::util::integer_store_be_u64(i, &mut self.1[ptr..end]);
|
||||
Ok(())
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a structure at a given position in the buffer and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn get_struct<T: RawObject>(&self, cursor: &mut usize) -> std::io::Result<&T> {
|
||||
let ptr = *cursor;
|
||||
let end = ptr + size_of::<T>();
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
unsafe {
|
||||
self.ptr = s;
|
||||
visitor(&*self.buffer.1.as_ptr().cast::<u8>().offset(rl as isize).cast::<T>(), self)
|
||||
Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<T>())
|
||||
}
|
||||
} else {
|
||||
false
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a fixed length byte array and advance the cursor.
|
||||
/// This is slightly more efficient than reading a runtime sized byte slice.
|
||||
#[inline(always)]
|
||||
pub fn get_bytes_fixed<const S: usize>(&self, cursor: &mut usize) -> std::io::Result<&[u8; S]> {
|
||||
let ptr = *cursor;
|
||||
let end = ptr + S;
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
unsafe {
|
||||
Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<[u8; S]>())
|
||||
}
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a runtime specified length byte slice and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn get_bytes(&self, l: usize, cursor: &mut usize) -> std::io::Result<&[u8]> {
|
||||
let ptr = *cursor;
|
||||
let end = ptr + l;
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
Ok(&self.1[ptr..end])
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the next u8 and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn get_u8(&self, cursor: &mut usize) -> std::io::Result<u8> {
|
||||
let ptr = *cursor;
|
||||
if ptr < self.0 {
|
||||
*cursor = ptr + 1;
|
||||
Ok(self.1[ptr])
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the next u16 and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn get_u16(&self, cursor: &mut usize) -> std::io::Result<u16> {
|
||||
let ptr = *cursor;
|
||||
let end = ptr + 2;
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
Ok(crate::util::integer_load_be_u16(&self.1[ptr..end]))
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the next u32 and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn get_u32(&self, cursor: &mut usize) -> std::io::Result<u32> {
|
||||
let ptr = *cursor;
|
||||
let end = ptr + 4;
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
Ok(crate::util::integer_load_be_u32(&self.1[ptr..end]))
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the next u64 and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn get_u64(&self, cursor: &mut usize) -> std::io::Result<u64> {
|
||||
let ptr = *cursor;
|
||||
let end = ptr + 8;
|
||||
if end <= self.0 {
|
||||
*cursor = end;
|
||||
Ok(crate::util::integer_load_be_u64(&self.1[ptr..end]))
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: RawObject, const L: usize> Write for Buffer<H, L> {
|
||||
#[inline(always)]
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let ptr = self.0;
|
||||
let end = ptr + buf.len();
|
||||
if end <= L {
|
||||
self.0 = end;
|
||||
self.1[ptr..end].copy_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
133
network-hypervisor/src/vl1/identity.rs
Normal file
133
network-hypervisor/src/vl1/identity.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
use std::io::Write;
|
||||
|
||||
use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair};
|
||||
use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE};
|
||||
use crate::vl1::Address;
|
||||
use crate::crypto::hash::SHA384;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum Type {
|
||||
/// Curve25519 / Ed25519 identity (type 0)
|
||||
C25519 = 0,
|
||||
/// NIST P-521 ECDH / ECDSA identity (also has c25519/ed25519 keys for backward compability) (type 1)
|
||||
P521 = 1
|
||||
}
|
||||
|
||||
struct IdentitySecrets {
|
||||
c25519: C25519KeyPair,
|
||||
ed25519: Ed25519KeyPair,
|
||||
p521: Option<(P521KeyPair, P521KeyPair)>, // ecdh key, ecdsa key
|
||||
}
|
||||
|
||||
pub struct Identity {
|
||||
address: Address,
|
||||
c25519_public: [u8; C25519_PUBLIC_KEY_SIZE],
|
||||
ed25519_public: [u8; ED25519_PUBLIC_KEY_SIZE],
|
||||
p521_public: Option<(P521PublicKey, P521PublicKey, [u8; P521_ECDSA_SIGNATURE_SIZE])>, // ecdh key, ecdsa key, ecdsa signature of all keys
|
||||
secrets: Option<IdentitySecrets>,
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
fn generate_c25519() {
|
||||
}
|
||||
|
||||
fn generate_p521() {
|
||||
}
|
||||
|
||||
/// Generate a new identity.
|
||||
/// This is time consuming due to the one-time anti-collision proof of work required
|
||||
/// to generate an address corresponding with a set of identity keys. V0 identities
|
||||
/// take tens to hundreds of milliseconds on a typical 2020 system, while V1 identites
|
||||
/// take about 500ms. Generation can take a lot longer on low power devices, but only
|
||||
/// has to be done once.
|
||||
pub fn generate(id_type: Type) {
|
||||
match id_type {
|
||||
Type::C25519 => Self::generate_c25519(),
|
||||
Type::P521 => Self::generate_p521()
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute ECDH key agreement and return SHA384(shared secret).
|
||||
/// If both keys are type 1, key agreement is done with NIST P-521. Otherwise it's done
|
||||
/// with Curve25519. None is returned if there is an error such as this identity missing
|
||||
/// its secrets or a key being invalid.
|
||||
pub fn agree(&self, other_identity: &Identity) -> Option<[u8; 48]> {
|
||||
self.secrets.as_ref().map_or(None, |secrets| {
|
||||
secrets.p521.as_ref().map_or_else(|| {
|
||||
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public)))
|
||||
}, |p521_secret| {
|
||||
other_identity.p521_public.as_ref().map_or_else(|| {
|
||||
Some(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519_public)))
|
||||
}, |other_p521_public| {
|
||||
p521_secret.0.agree(&other_p521_public.0).map_or(None, |secret| Some(SHA384::hash(&secret)))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Sign this message with this identity.
|
||||
/// Signature is performed using ed25519 EDDSA or NIST P-521 ECDSA depending on the identity
|
||||
/// type. None is returned if this identity lacks secret keys or another error occurs.
|
||||
pub fn sign(&self, msg: &[u8]) -> Option<Vec<u8>> {
|
||||
self.secrets.as_ref().map_or(None, |secrets| {
|
||||
secrets.p521.as_ref().map_or_else(|| {
|
||||
Some(secrets.ed25519.sign(msg).to_vec())
|
||||
}, |p521_secret| {
|
||||
p521_secret.1.sign(msg).map_or(None, |sig| Some(sig.to_vec()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get this identity's type.
|
||||
#[inline(always)]
|
||||
pub fn id_type(&self) -> Type {
|
||||
if self.p521_public.is_some() {
|
||||
Type::P521
|
||||
} else {
|
||||
Type::C25519
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this identity also holds its secret keys.
|
||||
#[inline(always)]
|
||||
pub fn has_secrets(&self) -> bool {
|
||||
self.secrets.is_some()
|
||||
}
|
||||
|
||||
/// Get this identity in string format, including its secret keys.
|
||||
pub fn to_secret_string(&self) -> String {
|
||||
self.secrets.as_ref().map_or_else(|| {
|
||||
self.to_string()
|
||||
}, |secrets| {
|
||||
secrets.p521.as_ref().map_or_else(|| {
|
||||
format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(&secrets.c25519.secret_bytes()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes()))
|
||||
}, |p521_secret| {
|
||||
let mut secret_key_blob: Vec<u8> = Vec::new();
|
||||
secret_key_blob.reserve(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE);
|
||||
let _ = secret_key_blob.write_all(&secrets.c25519.secret_bytes());
|
||||
let _ = secret_key_blob.write_all(&secrets.ed25519.secret_bytes());
|
||||
let _ = secret_key_blob.write_all(p521_secret.0.secret_key_bytes());
|
||||
let _ = secret_key_blob.write_all(p521_secret.1.secret_key_bytes());
|
||||
format!("{}:{}", self.to_string(), base64::encode_config(secret_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Identity {
|
||||
fn to_string(&self) -> String {
|
||||
self.p521_public.as_ref().map_or_else(|| {
|
||||
format!("{:0>10x}:0:{}{}", self.address.to_u64(), crate::util::hex::to_string(&self.c25519_public), crate::util::hex::to_string(&self.ed25519_public))
|
||||
}, |p521_public| {
|
||||
let mut public_key_blob: Vec<u8> = Vec::new();
|
||||
public_key_blob.reserve(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE);
|
||||
let _ = public_key_blob.write_all(&self.c25519_public);
|
||||
let _ = public_key_blob.write_all(&self.ed25519_public);
|
||||
let _ = public_key_blob.write_all(p521_public.0.public_key_bytes());
|
||||
let _ = public_key_blob.write_all(p521_public.1.public_key_bytes());
|
||||
let _ = public_key_blob.write_all(&p521_public.2);
|
||||
format!("{:0>10x}:1:{}", self.address.to_u64(), base64::encode_config(public_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
pub mod protocol;
|
||||
pub mod packet;
|
||||
pub mod buffer;
|
||||
pub mod node;
|
||||
mod address;
|
||||
mod mac;
|
||||
mod identity;
|
||||
|
||||
pub use address::Address;
|
||||
pub use mac::MAC;
|
||||
|
|
59
network-hypervisor/src/vl1/node.rs
Normal file
59
network-hypervisor/src/vl1/node.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
//use crate::vl1::Address;
|
||||
|
||||
/*
|
||||
/// Handler for events generated by the node that pertain to VL1.
|
||||
pub trait VL1NodeEventHandler: Sync + Send {
|
||||
/// Called when a core ZeroTier event occurs.
|
||||
fn event(&self, event: Event, event_data: &[u8]);
|
||||
|
||||
/// Called to store an object into the object store.
|
||||
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()>;
|
||||
|
||||
/// Called to retrieve an object from the object store.
|
||||
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result<Vec<u8>>;
|
||||
|
||||
/// Called to send a packet over the physical network (virtual -> physical).
|
||||
fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32;
|
||||
|
||||
/// Called to check and see if a physical address should be used for ZeroTier traffic.
|
||||
fn path_check(&self, address: Address, id: &Identity, local_socket: i64, sock_addr: &InetAddress) -> bool;
|
||||
|
||||
/// Called to look up a path to a known node, allowing out of band lookup methods for physical paths to nodes.
|
||||
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress>;
|
||||
}
|
||||
|
||||
pub struct Node<H: VL1NodeEventHandler> {
|
||||
handler: H,
|
||||
}
|
||||
|
||||
impl<H: VL1NodeEventHandler> Node<H> {
|
||||
pub fn new(handler: H) -> Self {
|
||||
Self {
|
||||
handler,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handler(&self) -> &H {
|
||||
&self.handler
|
||||
}
|
||||
|
||||
pub fn handler_mut(&mut self) -> &mut H {
|
||||
&mut self.handler
|
||||
}
|
||||
|
||||
/// Perform periodic background tasks.
|
||||
/// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds
|
||||
/// since the node was created, and after this runs it returns the amount of time the caller
|
||||
/// should wait before calling it again.
|
||||
#[inline(always)]
|
||||
pub fn process_background_tasks(&self, clock: i64, ticks: i64) -> i64 {
|
||||
0
|
||||
}
|
||||
|
||||
/// Get the address of this node.
|
||||
#[inline(always)]
|
||||
pub fn address(&self) -> Address {
|
||||
Address::default()
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -51,7 +51,6 @@ impl Header {
|
|||
}
|
||||
}
|
||||
|
||||
/// Packet is a Buffer with the packet Header and the packet max payload size.
|
||||
pub type Packet = Buffer<Header, { crate::vl1::protocol::PACKET_SIZE_MAX }>;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -83,6 +82,8 @@ impl FragmentHeader {
|
|||
}
|
||||
}
|
||||
|
||||
type Fragment = Buffer<FragmentHeader, { crate::vl1::protocol::PACKET_SIZE_MAX }>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::size_of;
|
||||
|
|
3
network-hypervisor/src/vl2/mod.rs
Normal file
3
network-hypervisor/src/vl2/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod mac;
|
||||
|
||||
pub use mac::MAC;
|
7
vli/Cargo.lock
generated
Normal file
7
vli/Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "vli"
|
||||
version = "0.1.0"
|
11
vli/Cargo.toml
Normal file
11
vli/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "vli"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[profile.test]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
411
vli/src/lib.rs
Normal file
411
vli/src/lib.rs
Normal file
|
@ -0,0 +1,411 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, AddAssign, Sub, Shl, Shr, SubAssign, ShlAssign, ShrAssign};
|
||||
use std::io::Write;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
const HEX_CHARS: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
||||
|
||||
/// Arbitrarily large unsigned integer supporting basic and modular arithmetic.
|
||||
///
|
||||
/// LIMBS is the number of 64-bit "limbs" (large digits) in this VLI. The number of bits is
|
||||
/// 64 times LIMBS, so for 1024 bits use 16 LIMBS. We don't have a generic BITS instead
|
||||
/// because of current limitations on Rust const generics and what can be done with them.
|
||||
/// This also means the size of a VLI must be a multiple of 64 bits. Note that the actual
|
||||
/// integer in it need not be exactly that length, just the capacity of the container.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct VLI<const LIMBS: usize> {
|
||||
n: [u64; LIMBS]
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> Default for VLI<LIMBS> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self { n: [0_u64; LIMBS ]}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> VLI<LIMBS> {
|
||||
/// Create a new zero VLI.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self { n: [0_u64; LIMBS ]}
|
||||
}
|
||||
|
||||
/// Set to zero.
|
||||
#[inline(always)]
|
||||
pub fn zero(&mut self) {
|
||||
self.n.fill(0)
|
||||
}
|
||||
|
||||
/// Test whether bit is set (numbered from right to left).
|
||||
/// This will panic if bit is out of range.
|
||||
#[inline(always)]
|
||||
pub fn test_bit(&self, bit: usize) -> bool {
|
||||
(self.n[bit >> 6] & (1_u64 << (bit & 63))) != 0
|
||||
}
|
||||
|
||||
/// Count the number of non-zero bits.
|
||||
pub fn count_ones(&self) -> u32 {
|
||||
let mut ones = 0_u32;
|
||||
for n in self.n {
|
||||
ones += n.count_ones();
|
||||
}
|
||||
ones
|
||||
}
|
||||
|
||||
/// Count the number of zero bits.
|
||||
#[inline(always)]
|
||||
pub fn count_zeros(&self) -> u32 {
|
||||
let mut zeros = 0_u32;
|
||||
for n in self.n {
|
||||
zeros += n.count_zeros();
|
||||
}
|
||||
zeros
|
||||
}
|
||||
|
||||
/// Returns true if this integer is zero.
|
||||
#[inline(always)]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
for n in self.n {
|
||||
if n != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns true if this is an odd number.
|
||||
#[inline(always)]
|
||||
pub fn is_odd(&self) -> bool {
|
||||
(self.n[0] & 1) != 0
|
||||
}
|
||||
|
||||
/// Returns true if this is an even number.
|
||||
#[inline(always)]
|
||||
pub fn is_even(&self) -> bool {
|
||||
(self.n[0] & 1) == 0
|
||||
}
|
||||
|
||||
/// Add to this integer and return any overflow carry bits.
|
||||
pub fn add_assign_carry(&mut self, rhs: &Self) -> u64 {
|
||||
let mut carry = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
let left = *left_ptr;
|
||||
let sum = left + *unsafe { rhs.n.get_unchecked(i) } + carry;
|
||||
carry = (sum < left) as u64;
|
||||
*left_ptr = sum;
|
||||
}
|
||||
carry
|
||||
}
|
||||
|
||||
/// Multiply two inputs half the size of this integer to yield a full size result in this integer..
|
||||
/// The multiplicand sizes MULT_LIMBS must be one half the LIMBS size of this integer.
|
||||
/// This is checked with an assertion. This isn't computed with the type system due
|
||||
/// to current limitations in const generics.
|
||||
pub fn mul_extend_assign<const MULT_LIMBS: usize>(&mut self, lhs: &VLI<{ MULT_LIMBS }>, rhs: &VLI<{ MULT_LIMBS }>) {
|
||||
assert_eq!(MULT_LIMBS, LIMBS / 2);
|
||||
let mut r01 = 0_u128;
|
||||
let mut r2 = 0_u64;
|
||||
let mut k = 0_usize;
|
||||
while k < MULT_LIMBS {
|
||||
for i in 0..k {
|
||||
let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128);
|
||||
r01 += l_product;
|
||||
r2 += (r01 < l_product) as u64;
|
||||
}
|
||||
*unsafe { self.n.get_unchecked_mut(k) } = r01 as u64;
|
||||
r01 += (r01 >> 64) | ((r2 as u128) << 64);
|
||||
r2 = 0;
|
||||
k += 1;
|
||||
}
|
||||
while k < (LIMBS - 1) {
|
||||
let mut i = (k + 1) - MULT_LIMBS;
|
||||
while i < k && i < MULT_LIMBS {
|
||||
let l_product = (*unsafe { lhs.get_unchecked(i) } as u128) * (*unsafe { rhs.get_unchecked(k - i) } as u128);
|
||||
r01 += l_product;
|
||||
r2 += (r01 < l_product) as u64;
|
||||
i += 1;
|
||||
}
|
||||
*unsafe { self.n.get_unchecked_mut(k) } = r01 as u64;
|
||||
r01 += (r01 >> 64) | ((r2 as u128) << 64);
|
||||
r2 = 0;
|
||||
k += 1;
|
||||
}
|
||||
*unsafe { self.n.get_unchecked_mut(LIMBS - 1) } = r01 as u64;
|
||||
}
|
||||
|
||||
/// Get this integer as a big-endian byte array.
|
||||
/// If skip_leading_zeroes is true the returned byte vector will be the minimum size
|
||||
/// needed to hold the integer, or empty if it is zero. Otherwise it will always be
|
||||
/// LIMBS * 8 bytes in length.
|
||||
pub fn to_vec(&self, skip_leading_zeroes: bool) -> Vec<u8> {
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
bytes.reserve(LIMBS * 8);
|
||||
|
||||
let mut i = LIMBS as isize - 1;
|
||||
if skip_leading_zeroes {
|
||||
while i >= 0 {
|
||||
let x: u64 = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
if x != 0 {
|
||||
let x = x.to_be_bytes();
|
||||
for j in 0..8 {
|
||||
if x[j] != 0 {
|
||||
let _ = bytes.write_all(&x[j..8]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
while i >= 0 {
|
||||
let _ = bytes.write_all(&(unsafe { self.n.get_unchecked(i as usize) }.to_be_bytes()));
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Get this integer as a hex string.
|
||||
/// If skip_leading_zeroes is true, the returned string will not be left padded with zeroes
|
||||
/// to the size it would be if the integer's bits were saturated.
|
||||
pub fn to_hex_string(&self, skip_leading_zeroes: bool) -> String {
|
||||
let mut s = String::new();
|
||||
s.reserve(LIMBS * 16);
|
||||
|
||||
let mut i = LIMBS as isize - 1;
|
||||
if skip_leading_zeroes {
|
||||
while i >= 0 {
|
||||
let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
if x != 0 {
|
||||
let mut j = 0;
|
||||
while j < 16 {
|
||||
if (x >> 60) != 0 {
|
||||
break;
|
||||
}
|
||||
x <<= 4;
|
||||
j += 1;
|
||||
}
|
||||
while j < 16 {
|
||||
s.push(HEX_CHARS[(x >> 60) as usize]);
|
||||
x <<= 4;
|
||||
j += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
while i >= 0 {
|
||||
let mut x: u64 = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
for _ in 0..16 {
|
||||
s.push(HEX_CHARS[(x >> 60) as usize]);
|
||||
x <<= 4;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if s.is_empty() {
|
||||
s.push('0');
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> Add<&Self> for VLI<LIMBS> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(mut self, rhs: &Self) -> Self::Output {
|
||||
self.add_assign(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> AddAssign<&Self> for VLI<LIMBS> {
|
||||
fn add_assign(&mut self, rhs: &Self) {
|
||||
let mut carry = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
let left = *left_ptr;
|
||||
let sum = left + *unsafe { rhs.n.get_unchecked(i) } + carry;
|
||||
carry = (sum < left) as u64;
|
||||
*left_ptr = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> Sub<&Self> for VLI<LIMBS> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn sub(mut self, rhs: &Self) -> Self::Output {
|
||||
self.sub_assign(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> SubAssign<&Self> for VLI<LIMBS> {
|
||||
fn sub_assign(&mut self, rhs: &Self) {
|
||||
let mut borrow = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let left_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
let left = *left_ptr;
|
||||
let diff = left - *unsafe { rhs.n.get_unchecked(i) } - borrow;
|
||||
borrow = (diff > left) as u64;
|
||||
*left_ptr = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> Shl<usize> for VLI<LIMBS> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn shl(mut self, rhs: usize) -> Self::Output {
|
||||
self.shl_assign(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> ShlAssign<usize> for VLI<LIMBS> {
|
||||
fn shl_assign(&mut self, rhs: usize) {
|
||||
if rhs != 0 {
|
||||
if rhs < 64 {
|
||||
let mut carry = 0_u64;
|
||||
for i in 0..LIMBS {
|
||||
let x_ptr = unsafe { self.n.get_unchecked_mut(i) };
|
||||
let x = *x_ptr;
|
||||
*x_ptr = (x << rhs) | carry;
|
||||
carry = x >> (64 - rhs);
|
||||
}
|
||||
} else {
|
||||
self.zero();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> Shr<usize> for VLI<LIMBS> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn shr(mut self, rhs: usize) -> Self::Output {
|
||||
self.shr_assign(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> ShrAssign<usize> for VLI<LIMBS> {
|
||||
fn shr_assign(&mut self, rhs: usize) {
|
||||
if rhs != 0 {
|
||||
if rhs < 64 {
|
||||
let mut carry = 0_u64;
|
||||
let mut i = LIMBS as isize - 1;
|
||||
while i >= 0 {
|
||||
let x_ptr = unsafe { self.n.get_unchecked_mut(i as usize) };
|
||||
let x = *x_ptr;
|
||||
*x_ptr = (x >> rhs) | carry;
|
||||
carry = x << (64 - rhs);
|
||||
i -= 1;
|
||||
}
|
||||
} else {
|
||||
self.zero();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> PartialOrd for VLI<LIMBS> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
|
||||
fn lt(&self, other: &Self) -> bool {
|
||||
let mut i = LIMBS as isize - 1;
|
||||
while i >= 0 {
|
||||
let a = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let b = *unsafe { other.n.get_unchecked(i as usize) };
|
||||
if a > b {
|
||||
return false;
|
||||
} else if a < b {
|
||||
return true;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn le(&self, other: &Self) -> bool {
|
||||
let mut i = LIMBS as isize - 1;
|
||||
while i >= 0 {
|
||||
let a = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let b = *unsafe { other.n.get_unchecked(i as usize) };
|
||||
if a > b {
|
||||
return false;
|
||||
} else if a < b {
|
||||
return true;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn gt(&self, other: &Self) -> bool {
|
||||
let mut i = LIMBS as isize - 1;
|
||||
while i >= 0 {
|
||||
let a = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let b = *unsafe { other.n.get_unchecked(i as usize) };
|
||||
if a > b {
|
||||
return true;
|
||||
} else if a < b {
|
||||
return false;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn ge(&self, other: &Self) -> bool {
|
||||
let mut i = LIMBS as isize - 1;
|
||||
while i >= 0 {
|
||||
let a = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let b = *unsafe { other.n.get_unchecked(i as usize) };
|
||||
if a > b {
|
||||
return true;
|
||||
} else if a < b {
|
||||
return false;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> Ord for VLI<LIMBS> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let mut i = LIMBS as isize - 1;
|
||||
while i >= 0 {
|
||||
let a = *unsafe { self.n.get_unchecked(i as usize) };
|
||||
let b = *unsafe { other.n.get_unchecked(i as usize) };
|
||||
if a > b {
|
||||
return Ordering::Greater;
|
||||
} else if a < b {
|
||||
return Ordering::Less;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn arithmetic() {
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue