mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-22 14:23:59 -07:00
A ton of ZSSP work, and put MPL on ZSSP.
This commit is contained in:
parent
1b114ad9c2
commit
e218ba0741
11 changed files with 717 additions and 823 deletions
|
@ -1,12 +0,0 @@
|
|||
[package]
|
||||
authors = ["Adam Ierymenko <adam.ierymenko@zerotier.com>"]
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
name = "pp384"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
sha2 = "^0"
|
||||
hex-literal = "^0"
|
|
@ -1 +0,0 @@
|
|||
../rustfmt.toml
|
|
@ -1,99 +0,0 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
const ECC_BYTES: usize = 48;
|
||||
const ECC_QUADS: usize = ECC_BYTES / 8;
|
||||
const MAX_TRIES: usize = 4096;
|
||||
|
||||
const P384_P_48: [u64; ECC_QUADS] = [
|
||||
0x00000000FFFFFFFF,
|
||||
0xFFFFFFFF00000000,
|
||||
0xFFFFFFFFFFFFFFFE,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
];
|
||||
const P384_B_48: [u64; ECC_QUADS] = [
|
||||
0x2A85C8EDD3EC2AEF,
|
||||
0xC656398D8A2ED19D,
|
||||
0x0314088F5013875A,
|
||||
0x181D9C6EFE814112,
|
||||
0x988E056BE3F82D19,
|
||||
0xB3312FA7E23EE7E4,
|
||||
];
|
||||
const P384_G_48: [[u64; ECC_QUADS]; 2] = [
|
||||
[
|
||||
0x3A545E3872760AB7,
|
||||
0x5502F25DBF55296C,
|
||||
0x59F741E082542A38,
|
||||
0x6E1D3B628BA79B98,
|
||||
0x8EB1C71EF320AD74,
|
||||
0xAA87CA22BE8B0537,
|
||||
],
|
||||
[
|
||||
0x7A431D7C90EA0E5F,
|
||||
0x0A60B1CE1D7E819D,
|
||||
0xE9DA3113B5F0B8C0,
|
||||
0xF8F41DBD289A147C,
|
||||
0x5D9E98BF9292DC29,
|
||||
0x3617DE4A96262C6F,
|
||||
],
|
||||
];
|
||||
const P384_N_48: [u64; ECC_QUADS] = [
|
||||
0xECEC196ACCC52973,
|
||||
0x581A0DB248B0A77A,
|
||||
0xC7634D81F4372DDF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
];
|
||||
|
||||
fn pvli_clear<const P: usize>(pvli: &mut [[u64; ECC_QUADS]; P]) {
|
||||
pvli.iter_mut().for_each(|vli| vli.fill(0));
|
||||
}
|
||||
|
||||
fn pvli_is_zero<const P: usize>(pvli: &[[u64; ECC_QUADS]; P]) -> [bool; P] {
|
||||
pvli.map(|vli| vli.iter().all(|i| *i == 0))
|
||||
}
|
||||
|
||||
fn pvli_test_bit<const P: usize>(pvli: &[[u64; ECC_QUADS]; P], bit: u32) -> [bool; P] {
|
||||
pvli.map(|vli| (vli[(bit as usize) / 64] & 1u64.wrapping_shl(bit % 64)) != 0)
|
||||
}
|
||||
|
||||
fn pvli_num_digits<const P: usize>(pvli: &[[u64; ECC_QUADS]; P]) -> [usize; P] {
|
||||
pvli.map(|vli| {
|
||||
let mut i = (ECC_QUADS as isize) - 1;
|
||||
while i >= 0 && vli[i as usize] == 0 {
|
||||
i -= 1;
|
||||
}
|
||||
(i + 1) as usize
|
||||
})
|
||||
}
|
||||
|
||||
fn pvli_num_bits<const P: usize>(pvli: &[[u64; ECC_QUADS]; P]) -> [usize; P] {
|
||||
pvli.map(|vli| {
|
||||
let mut bits = 0;
|
||||
for i in 0..ECC_QUADS {
|
||||
bits += vli[i].count_ones();
|
||||
}
|
||||
bits as usize
|
||||
})
|
||||
}
|
||||
|
||||
fn pvli_cmp<const P: usize>(left: &[[u64; ECC_QUADS]; P], right: &[[u64; ECC_QUADS]; P]) -> [Ordering; P] {
|
||||
let mut r = [Ordering::Equal; P];
|
||||
for p in 0..P {
|
||||
let mut i = (ECC_QUADS as isize) - 1;
|
||||
while i >= 0 {
|
||||
if left[p][i as usize] > right[p][i as usize] {
|
||||
r[p] = Ordering::Greater;
|
||||
break;
|
||||
}
|
||||
if left[p][i as usize] < right[p][i as usize] {
|
||||
r[p] = Ordering::Less;
|
||||
break;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
use std::ops::Deref;
|
||||
/* 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 zerotier_crypto::{
|
||||
p384::{P384KeyPair, P384PublicKey},
|
||||
secret::Secret,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
sessionid::SessionId,
|
||||
zssp::{ReceiveContext, Session},
|
||||
};
|
||||
use zerotier_crypto::p384::{P384KeyPair, P384PublicKey};
|
||||
|
||||
/// Trait to implement to integrate the session into an application.
|
||||
///
|
||||
|
@ -18,18 +16,12 @@ pub trait ApplicationLayer: Sized {
|
|||
/// Arbitrary opaque object associated with a session, such as a connection state object.
|
||||
type Data;
|
||||
|
||||
/// Arbitrary object that dereferences to the session, such as Arc<Session<Self>>.
|
||||
type SessionRef<'a>: Deref<Target = Session<Self>>;
|
||||
|
||||
/// A buffer containing data read from the network that can be cached.
|
||||
///
|
||||
/// This can be e.g. a pooled buffer that automatically returns itself to the pool when dropped.
|
||||
/// It can also just be a Vec<u8> or Box<[u8]> or something like that.
|
||||
type IncomingPacketBuffer: AsRef<[u8]> + AsMut<[u8]>;
|
||||
|
||||
/// Remote physical address on whatever transport this session is using.
|
||||
type RemoteAddress;
|
||||
|
||||
/// Rate limit for attempts to rekey existing sessions in milliseconds (default: 2000).
|
||||
const REKEY_RATE_LIMIT_MS: i64 = 2000;
|
||||
|
||||
|
@ -38,7 +30,7 @@ pub trait ApplicationLayer: Sized {
|
|||
/// This is called to parse the static public key blob from the other end and extract its NIST P-384 public
|
||||
/// key. SECURITY NOTE: the information supplied here is from the wire so care must be taken to parse it
|
||||
/// safely and fail on any error or corruption.
|
||||
fn extract_s_public_from_raw(static_public: &[u8]) -> Option<P384PublicKey>;
|
||||
fn extract_s_public_from_static_public_blob(static_public: &[u8]) -> Option<P384PublicKey>;
|
||||
|
||||
/// Get a reference to this host's static public key blob.
|
||||
///
|
||||
|
@ -46,36 +38,8 @@ pub trait ApplicationLayer: Sized {
|
|||
/// is a byte serialized identity. It could just be a naked NIST P-384 key if that's all you need.
|
||||
fn get_local_s_public_blob(&self) -> &[u8];
|
||||
|
||||
/// Get SHA384(this host's static public key blob).
|
||||
///
|
||||
/// This allows us to avoid computing SHA384(public key blob) over and over again.
|
||||
fn get_local_s_public_blob_hash(&self) -> &[u8; 48];
|
||||
|
||||
/// Get a reference to this hosts' static public key's NIST P-384 secret key pair.
|
||||
/// Get a reference to this host's static public key's NIST P-384 secret key pair.
|
||||
///
|
||||
/// This must return the NIST P-384 public key that is contained within the static public key blob.
|
||||
fn get_local_s_keypair(&self) -> &P384KeyPair;
|
||||
|
||||
/// Look up a local session by local session ID or return None if not found.
|
||||
fn lookup_session<'a>(&self, local_session_id: SessionId) -> Option<Self::SessionRef<'a>>;
|
||||
|
||||
/// Rate limit and check an attempted new session.
|
||||
fn prescreen_new_session(&self, rc: &ReceiveContext<Self>, remote_address: &Self::RemoteAddress) -> bool;
|
||||
|
||||
/// Check whether a new session should be accepted.
|
||||
///
|
||||
/// This is called in the final phase of Noise_XK once Alice's identity is known. If it should be
|
||||
/// accepted then this should return a session ID, a PSK or all zeroes if there is none, and application
|
||||
/// specific data to attach to the session.
|
||||
///
|
||||
/// This doesn't guarantee acceptance until the new session is returned from the receive call, since
|
||||
/// something can still go wrong during final authentication and session setup. The session ID supplied
|
||||
/// here should not be assumed to be in use.
|
||||
fn accept_new_session(
|
||||
&self,
|
||||
receive_context: &ReceiveContext<Self>,
|
||||
remote_address: &Self::RemoteAddress,
|
||||
remote_static_public: &[u8],
|
||||
remote_metadata: &[u8],
|
||||
) -> Option<(SessionId, Secret<64>, Self::Data)>;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
/// Minimum size of a valid physical ZSSP packet or packet fragment.
|
||||
/* 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 crate::proto::{AES_GCM_TAG_SIZE, HEADER_SIZE};
|
||||
|
||||
/// Minimum size of a valid physical ZSSP packet of any type. Anything smaller is discarded.
|
||||
pub const MIN_PACKET_SIZE: usize = HEADER_SIZE + AES_GCM_TAG_SIZE;
|
||||
|
||||
/// Minimum physical MTU for ZSSP to function.
|
||||
pub const MIN_TRANSPORT_MTU: usize = 64;
|
||||
pub const MIN_TRANSPORT_MTU: usize = 128;
|
||||
|
||||
/// Minimum recommended interval between calls to service() on each session, in milliseconds.
|
||||
pub const SERVICE_INTERVAL: u64 = 10000;
|
||||
|
||||
/// Maximum number of fragments for data packets.
|
||||
pub(crate) const MAX_FRAGMENTS: usize = 48; // hard protocol max: 63
|
||||
|
||||
/// Maximum number of fragments for key exchange packets (can be smaller to save memory, only a few needed)
|
||||
pub(crate) const KEY_EXCHANGE_MAX_FRAGMENTS: usize = 2; // enough room for p384 + ZT identity + kyber1024 + tag/hmac/etc.
|
||||
/// Maximum size of init meta-data objects.
|
||||
pub const MAX_METADATA_SIZE: usize = 256;
|
||||
|
||||
/// Start attempting to rekey after a key has been used to send packets this many times.
|
||||
/// This is 1/4 the recommended NIST limit for AES-GCM key lifetimes under most conditions.
|
||||
|
@ -30,66 +34,8 @@ pub(crate) const REKEY_AFTER_TIME_MS: i64 = 1000 * 60 * 60; // 1 hour
|
|||
/// Maximum random jitter to add to rekey-after time.
|
||||
pub(crate) const REKEY_AFTER_TIME_MS_MAX_JITTER: u32 = 1000 * 60 * 10; // 10 minutes
|
||||
|
||||
/// Version 0: AES-256-GCM + NIST P-384 + optional Kyber1024 PQ forward secrecy
|
||||
pub(crate) const SESSION_PROTOCOL_VERSION: u8 = 0x00;
|
||||
/// Timeout for incoming sessions in incomplete state in milliseconds.
|
||||
pub(crate) const INCOMPLETE_SESSION_TIMEOUT: i64 = 1000;
|
||||
|
||||
/// Secondary key type: none, use only P-384 for forward secrecy.
|
||||
pub(crate) const HYBRID_KEY_TYPE_NONE: u8 = 0;
|
||||
|
||||
/// Secondary key type: Kyber1024, PQ forward secrecy enabled.
|
||||
pub(crate) const HYBRID_KEY_TYPE_KYBER1024: u8 = 1;
|
||||
|
||||
/// Size of packet header
|
||||
pub(crate) const HEADER_SIZE: usize = 16;
|
||||
|
||||
/// Start of single block AES encryption of a portion of the header (and some data).
|
||||
pub(crate) const HEADER_CHECK_ENCRYPT_START: usize = 6;
|
||||
|
||||
/// End of single block AES encryption of a portion of the header (and some data).
|
||||
pub(crate) const HEADER_CHECK_ENCRYPT_END: usize = 22;
|
||||
|
||||
pub(crate) const AES_KEY_SIZE: usize = 32;
|
||||
pub(crate) const AES_HEADER_CHECK_KEY_SIZE: usize = 16;
|
||||
pub(crate) const AES_GCM_TAG_SIZE: usize = 16;
|
||||
pub(crate) const AES_GCM_NONCE_SIZE: usize = 12;
|
||||
pub(crate) const AES_CTR_NONCE_SIZE: usize = 12;
|
||||
|
||||
/// Size of a session ID, which behaves a bit like a TCP port number.
|
||||
///
|
||||
/// This is large since some ZeroTier nodes handle huge numbers of links, like roots and controllers.
|
||||
pub(crate) const SESSION_ID_SIZE: usize = 6;
|
||||
|
||||
/// Timeout for a "note to self" that must be returned by a session initiator.
|
||||
pub(crate) const BOB_NOTE_TO_SELF_TIMEOUT_MS: i64 = 500;
|
||||
|
||||
/// Maximum difference between out-of-order incoming packet counters, and size of deduplication buffer.
|
||||
pub(crate) const COUNTER_WINDOW_MAX_OOO: usize = 16;
|
||||
|
||||
/// Maximum skip-ahead for counter.
|
||||
///
|
||||
/// This is huge (2^24) because its real purpose is to filter out bad packets where decryption of
|
||||
/// the counter yields an invalid value.
|
||||
pub(crate) const COUNTER_WINDOW_MAX_SKIP_AHEAD: u64 = 16777216;
|
||||
|
||||
// Key usage labels for sub-key derivation using NIST-style KBKDF (basically just HMAC KDF).
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION: u8 = b'X'; // intermediate keys used in key exchanges
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION: u8 = b'x'; // intermediate keys used in key exchanges
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_HEADER_CHECK: u8 = b'H'; // AES-based header check code generation
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB: u8 = b'A'; // AES-GCM in A->B direction
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE: u8 = b'B'; // AES-GCM in B->A direction
|
||||
|
||||
// AES key size for header check code generation
|
||||
pub(crate) const HEADER_CHECK_AES_KEY_SIZE: usize = 16;
|
||||
|
||||
/// Aribitrary starting value for master key derivation.
|
||||
///
|
||||
/// It doesn't matter very much what this is but it's good for it to be unique. It should
|
||||
/// be changed if this code is changed in any cryptographically meaningful way like changing
|
||||
/// the primary algorithm from NIST P-384 or the transport cipher from AES-GCM.
|
||||
pub(crate) const INITIAL_KEY: [u8; 64] = [
|
||||
// macOS command line to generate:
|
||||
// echo -n 'ZSSP_Noiselike_XKpsk2_NISTP384_?KYBER1024_AESGCM_SHA512' | shasum -a 512 | cut -d ' ' -f 1 | xxd -r -p | xxd -i
|
||||
0xc7, 0xde, 0xa3, 0xbe, 0x84, 0xe5, 0x91, 0x25, 0x30, 0x59, 0xc1, 0xc9, 0x5d, 0x22, 0xf5, 0x5a, 0xd0, 0x67, 0x9e, 0xf9, 0xf6, 0xbb,
|
||||
0xc0, 0x2a, 0x7f, 0xd0, 0x12, 0xb2, 0x0f, 0xed, 0x64, 0x7a, 0x86, 0x9f, 0x82, 0x19, 0xca, 0x84, 0xad, 0xf6, 0x61, 0xda, 0x59, 0xcc,
|
||||
0x40, 0xcf, 0x57, 0x68, 0x3e, 0xe4, 0xd6, 0xe7, 0xd1, 0xad, 0xe9, 0x56, 0x50, 0xf2, 0x38, 0x22, 0x88, 0xa3, 0x5c, 0x7f,
|
||||
];
|
||||
/// Maximum number of pending incomplete sessions.
|
||||
pub(crate) const INCOMPLETE_SESSION_MAX_QUEUE_SIZE: usize = 256;
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/* 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 crate::sessionid::SessionId;
|
||||
|
||||
pub enum Error {
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/* 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/
|
||||
*/
|
||||
|
||||
mod applicationlayer;
|
||||
mod error;
|
||||
mod proto;
|
||||
|
@ -10,4 +18,4 @@ pub mod constants;
|
|||
pub use crate::applicationlayer::ApplicationLayer;
|
||||
pub use crate::error::Error;
|
||||
pub use crate::sessionid::SessionId;
|
||||
pub use crate::zssp::{ReceiveContext, ReceiveResult, Session};
|
||||
pub use crate::zssp::{Context, ReceiveResult, Session};
|
||||
|
|
|
@ -1,96 +1,100 @@
|
|||
/* 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::mem::size_of;
|
||||
|
||||
use pqc_kyber::{KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES};
|
||||
use zerotier_crypto::hash::{HMAC_SHA384_SIZE, SHA384_HASH_SIZE};
|
||||
use zerotier_crypto::p384::{P384_PUBLIC_KEY_SIZE, P384_SECRET_KEY_SIZE};
|
||||
use zerotier_crypto::p384::P384_PUBLIC_KEY_SIZE;
|
||||
|
||||
use crate::applicationlayer::ApplicationLayer;
|
||||
use crate::constants::{AES_GCM_TAG_SIZE, HEADER_SIZE, MIN_PACKET_SIZE, SESSION_ID_SIZE};
|
||||
use crate::constants::*;
|
||||
use crate::error::Error;
|
||||
use crate::sessionid::SessionId;
|
||||
|
||||
pub(crate) const SESSION_PROTOCOL_VERSION: u8 = 0x00;
|
||||
|
||||
pub(crate) const COUNTER_WINDOW_MAX_OOO: usize = 16;
|
||||
pub(crate) const COUNTER_WINDOW_MAX_SKIP_AHEAD: u64 = 16777216;
|
||||
|
||||
pub(crate) const NOISE_MAX_HANDSHAKE_PACKET_SIZE: usize = 2048;
|
||||
|
||||
pub(crate) const PACKET_TYPE_DATA: u8 = 0;
|
||||
pub(crate) const PACKET_TYPE_ALICE_EPHEMERAL_OFFER: u8 = 1;
|
||||
pub(crate) const PACKET_TYPE_BOB_EPHEMERAL_COUNTER_OFFER: u8 = 2;
|
||||
pub(crate) const PACKET_TYPE_ALICE_STATIC_ACK: u8 = 3;
|
||||
pub(crate) const PACKET_TYPE_ALICE_NOISE_XK_INIT: u8 = 1;
|
||||
pub(crate) const PACKET_TYPE_BOB_NOISE_XK_ACK: u8 = 2;
|
||||
pub(crate) const PACKET_TYPE_ALICE_NOISE_XK_ACK: u8 = 3;
|
||||
pub(crate) const PACKET_TYPE_ALICE_REKEY_INIT: u8 = 4;
|
||||
pub(crate) const PACKET_TYPE_BOB_REKEY_ACK: u8 = 5;
|
||||
|
||||
pub(crate) const HEADER_SIZE: usize = 16;
|
||||
pub(crate) const HEADER_CHECK_ENCRYPT_START: usize = 6;
|
||||
pub(crate) const HEADER_CHECK_ENCRYPT_END: usize = 22;
|
||||
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION: u8 = b'X'; // intermediate keys used in key exchanges
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION: u8 = b'x'; // intermediate keys used in key exchanges
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB: u8 = b'A'; // AES-GCM in A->B direction
|
||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE: u8 = b'B'; // AES-GCM in B->A direction
|
||||
|
||||
pub(crate) const MAX_FRAGMENTS: usize = 48; // hard protocol max: 63
|
||||
pub(crate) const KEY_EXCHANGE_MAX_FRAGMENTS: usize = 8; // enough room for p384 + ZT identity + kyber1024 + tag/hmac/etc.
|
||||
|
||||
pub(crate) const AES_KEY_SIZE: usize = 32;
|
||||
pub(crate) const AES_HEADER_CHECK_KEY_SIZE: usize = 16;
|
||||
pub(crate) const AES_GCM_TAG_SIZE: usize = 16;
|
||||
pub(crate) const AES_GCM_NONCE_SIZE: usize = 12;
|
||||
pub(crate) const AES_CTR_NONCE_SIZE: usize = 12;
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(C, packed)]
|
||||
pub(crate) struct NoiseXKAliceEphemeralOffer {
|
||||
pub(crate) struct AliceNoiseXKInit {
|
||||
pub header: [u8; HEADER_SIZE],
|
||||
pub session_protocol_version: u8,
|
||||
pub reserved: [u8; 8],
|
||||
pub alice_noise_e: [u8; P384_PUBLIC_KEY_SIZE],
|
||||
// -- start AES-CTR(es) encrypted section (IV is first 12 bytes of SHA384(alice_noise_e))
|
||||
pub alice_session_id: [u8; SESSION_ID_SIZE],
|
||||
// -- start AES-CTR(es) encrypted section (IV is last 12 bytes of alice_noise_e))
|
||||
pub alice_session_id: [u8; SessionId::SIZE],
|
||||
pub alice_hk_public: [u8; KYBER_PUBLICKEYBYTES],
|
||||
pub salt: [u8; 8],
|
||||
pub header_check_cipher_key: [u8; AES_HEADER_CHECK_KEY_SIZE],
|
||||
// -- end encrypted section
|
||||
pub hmac_es: [u8; HMAC_SHA384_SIZE],
|
||||
}
|
||||
|
||||
impl NoiseXKAliceEphemeralOffer {
|
||||
pub const ENC_START: usize = HEADER_SIZE + 1 + 8 + P384_PUBLIC_KEY_SIZE;
|
||||
pub const AUTH_START: usize = size_of::<NoiseXKAliceEphemeralOffer>() - HMAC_SHA384_SIZE;
|
||||
impl AliceNoiseXKInit {
|
||||
pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
|
||||
pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_PUBLICKEYBYTES + AES_HEADER_CHECK_KEY_SIZE;
|
||||
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(C, packed)]
|
||||
pub(crate) struct NoiseXKBobEphemeralCounterOffer {
|
||||
pub(crate) struct BobNoiseXKAck {
|
||||
pub header: [u8; HEADER_SIZE],
|
||||
pub session_protocol_version: u8,
|
||||
pub bob_noise_e: [u8; P384_PUBLIC_KEY_SIZE],
|
||||
// -- start AES-CTR(es_ee) encrypted section (IV is first 12 bytes of SHA384(bob_noise_e))
|
||||
// -- start AES-CTR(es_ee) encrypted section (IV is last 12 bytes of bob_noise_e)
|
||||
pub bob_session_id: [u8; SessionId::SIZE],
|
||||
pub bob_hk_ciphertext: [u8; KYBER_CIPHERTEXTBYTES],
|
||||
pub bob_note_to_self: [u8; size_of::<BobNoteToSelf>()],
|
||||
// -- end encrypted sectiion
|
||||
pub hmac_es_ee: [u8; HMAC_SHA384_SIZE],
|
||||
pub hmac_es_ee_se_hk_psk: [u8; HMAC_SHA384_SIZE],
|
||||
}
|
||||
|
||||
impl NoiseXKBobEphemeralCounterOffer {
|
||||
impl BobNoiseXKAck {
|
||||
pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
|
||||
pub const AUTH_START: usize = size_of::<NoiseXKBobEphemeralCounterOffer>() - HMAC_SHA384_SIZE;
|
||||
pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_CIPHERTEXTBYTES;
|
||||
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE + HMAC_SHA384_SIZE;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(C, packed)]
|
||||
pub(crate) struct BobNoteToSelf {
|
||||
pub iv: [u8; 16],
|
||||
// -- start AES-GCM encrypted section using ephemeral secret known only to Bob
|
||||
pub timestamp: [u8; 8],
|
||||
pub alice_session_id: [u8; SESSION_ID_SIZE],
|
||||
pub bob_noise_e: [u8; P384_PUBLIC_KEY_SIZE],
|
||||
pub bob_noise_e_secret: [u8; P384_SECRET_KEY_SIZE],
|
||||
pub hk: [u8; 32],
|
||||
pub noise_es_ee: [u8; 64],
|
||||
// -- end encrypted sectiion
|
||||
pub gcm_mac: [u8; AES_GCM_TAG_SIZE],
|
||||
}
|
||||
|
||||
impl BobNoteToSelf {
|
||||
pub const IV_SIZE: usize = 16;
|
||||
pub const ENC_START: usize = Self::IV_SIZE;
|
||||
pub const AUTH_START: usize = size_of::<BobNoteToSelf>() - AES_GCM_TAG_SIZE;
|
||||
}
|
||||
|
||||
// These are variable length and so they're only here for documentation purposes.
|
||||
|
||||
pub(crate) const NOISE_XK_ALICE_STATIC_ACK_BOB_NOTE_TO_SELF_START: usize = HEADER_SIZE + 1;
|
||||
pub(crate) const NOISE_XK_ALICE_STATIC_ACK_BOB_NOTE_TO_SELF_END: usize = HEADER_SIZE + 1 + size_of::<BobNoteToSelf>();
|
||||
pub(crate) const NOISE_XK_ALICE_STATIC_ACK_ENCRYPTED_SECTION_START: usize = NOISE_XK_ALICE_STATIC_ACK_BOB_NOTE_TO_SELF_END;
|
||||
pub(crate) const NOISE_XK_ALICE_STATIC_ACK_MIN_SIZE: usize =
|
||||
NOISE_XK_ALICE_STATIC_ACK_BOB_NOTE_TO_SELF_END + 2 + 2 + HMAC_SHA384_SIZE + HMAC_SHA384_SIZE;
|
||||
|
||||
/*
|
||||
#[allow(unused)]
|
||||
#[repr(C, packed)]
|
||||
pub(crate) struct NoiseXKAliceStaticAck {
|
||||
pub(crate) struct AliceNoiseXKAck {
|
||||
pub header: [u8; HEADER_SIZE],
|
||||
pub session_protocol_version: u8,
|
||||
pub bob_note_to_self: [u8; size_of::<BobNoteToSelf>()],
|
||||
// -- start AES-CTR(es_ee) encrypted section (IV is first 12 bytes of SHA384(hk))
|
||||
pub alice_static_blob_length: [u8; 2],
|
||||
pub alice_static_blob: [u8; ???],
|
||||
|
@ -148,9 +152,8 @@ pub(crate) fn assemble_fragments_into<A: ApplicationLayer>(fragments: &[A::Incom
|
|||
// are packed flat buffers containing only byte or byte array fields, making them safe to treat
|
||||
// this way even on architectures that require type size aligned access.
|
||||
pub(crate) trait ProtocolFlatBuffer {}
|
||||
impl ProtocolFlatBuffer for NoiseXKAliceEphemeralOffer {}
|
||||
impl ProtocolFlatBuffer for NoiseXKBobEphemeralCounterOffer {}
|
||||
impl ProtocolFlatBuffer for BobNoteToSelf {}
|
||||
impl ProtocolFlatBuffer for AliceNoiseXKInit {}
|
||||
impl ProtocolFlatBuffer for BobNoiseXKAck {}
|
||||
//impl ProtocolFlatBuffer for NoiseXKAliceStaticAck {}
|
||||
impl ProtocolFlatBuffer for AliceRekeyInit {}
|
||||
impl ProtocolFlatBuffer for BobRekeyAck {}
|
||||
|
@ -172,3 +175,14 @@ pub(crate) fn byte_array_as_proto_buffer_mut<B: ProtocolFlatBuffer>(b: &mut [u8]
|
|||
Err(Error::InvalidPacket)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn check_packed_struct_sizing() {
|
||||
assert_eq!(size_of::<AliceNoiseXKInit>(), AliceNoiseXKInit::SIZE);
|
||||
assert_eq!(size_of::<BobNoiseXKAck>(), BobNoiseXKAck::SIZE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
/* 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::fmt::Display;
|
||||
use std::num::NonZeroU64;
|
||||
|
||||
use zerotier_crypto::random;
|
||||
use zerotier_utils::memory::{array_range, as_byte_array};
|
||||
|
||||
use crate::constants::SESSION_ID_SIZE;
|
||||
|
||||
/// 48-bit session ID (most significant 16 bits of u64 are unused)
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct SessionId(NonZeroU64); // stored little endian internally
|
||||
|
||||
const SESSION_ID_SIZE_BYTES: usize = 6;
|
||||
|
||||
impl SessionId {
|
||||
pub const SIZE: usize = SESSION_ID_SIZE_BYTES;
|
||||
pub const NONE: u64 = 0;
|
||||
pub const MAX: u64 = 0xffffffffffff;
|
||||
|
||||
|
@ -21,14 +30,14 @@ impl SessionId {
|
|||
Self(NonZeroU64::new(i.to_le()).unwrap())
|
||||
}
|
||||
|
||||
/// Create a new random session ID (non-cryptographic PRNG)
|
||||
/// Create a new random (non-zero) session ID (non-cryptographic PRNG)
|
||||
pub fn random() -> Self {
|
||||
Self(NonZeroU64::new(((random::xorshift64_random() % (Self::MAX - 1)) + 1).to_le()).unwrap())
|
||||
}
|
||||
|
||||
pub(crate) fn new_from_bytes(b: &[u8; SESSION_ID_SIZE]) -> Option<SessionId> {
|
||||
pub(crate) fn new_from_bytes(b: &[u8; Self::SIZE]) -> Option<SessionId> {
|
||||
let mut tmp = [0u8; 8];
|
||||
tmp[..SESSION_ID_SIZE].copy_from_slice(b);
|
||||
tmp[..SESSION_ID_SIZE_BYTES].copy_from_slice(b);
|
||||
Self::new_from_u64_le(u64::from_ne_bytes(tmp))
|
||||
}
|
||||
|
||||
|
@ -40,8 +49,8 @@ impl SessionId {
|
|||
|
||||
/// Get this session ID as a little-endian byte array.
|
||||
#[inline(always)]
|
||||
pub(crate) fn as_bytes(&self) -> &[u8; SESSION_ID_SIZE] {
|
||||
array_range::<u8, 8, 0, SESSION_ID_SIZE>(as_byte_array(&self.0))
|
||||
pub(crate) fn as_bytes(&self) -> &[u8; Self::SIZE] {
|
||||
array_range::<u8, 8, 0, SESSION_ID_SIZE_BYTES>(as_byte_array(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
/* 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -214,3 +223,4 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
1113
zssp/src/zssp.rs
1113
zssp/src/zssp.rs
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue