A ton of ZSSP work, and put MPL on ZSSP.

This commit is contained in:
Adam Ierymenko 2023-02-22 13:08:19 -05:00
commit e218ba0741
11 changed files with 717 additions and 823 deletions

View file

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

View file

@ -1 +0,0 @@
../rustfmt.toml

View file

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

View file

@ -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::{ use zerotier_crypto::p384::{P384KeyPair, P384PublicKey};
p384::{P384KeyPair, P384PublicKey},
secret::Secret,
};
use crate::{
sessionid::SessionId,
zssp::{ReceiveContext, Session},
};
/// Trait to implement to integrate the session into an application. /// 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. /// Arbitrary opaque object associated with a session, such as a connection state object.
type Data; 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. /// 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. /// 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. /// It can also just be a Vec<u8> or Box<[u8]> or something like that.
type IncomingPacketBuffer: AsRef<[u8]> + AsMut<[u8]>; 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). /// Rate limit for attempts to rekey existing sessions in milliseconds (default: 2000).
const REKEY_RATE_LIMIT_MS: i64 = 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 /// 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 /// 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. /// 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. /// 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. /// 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]; fn get_local_s_public_blob(&self) -> &[u8];
/// Get SHA384(this host's static public key blob). /// Get a reference to this host's static public key's NIST P-384 secret key pair.
///
/// 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.
/// ///
/// This must return the NIST P-384 public key that is contained within the static public key blob. /// 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; 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)>;
} }

View file

@ -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; pub const MIN_PACKET_SIZE: usize = HEADER_SIZE + AES_GCM_TAG_SIZE;
/// Minimum physical MTU for ZSSP to function. /// 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. /// Maximum size of init meta-data objects.
pub const SERVICE_INTERVAL: u64 = 10000; pub const MAX_METADATA_SIZE: usize = 256;
/// 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.
/// Start attempting to rekey after a key has been used to send packets this many times. /// 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. /// 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. /// Maximum random jitter to add to rekey-after time.
pub(crate) const REKEY_AFTER_TIME_MS_MAX_JITTER: u32 = 1000 * 60 * 10; // 10 minutes 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 /// Timeout for incoming sessions in incomplete state in milliseconds.
pub(crate) const SESSION_PROTOCOL_VERSION: u8 = 0x00; pub(crate) const INCOMPLETE_SESSION_TIMEOUT: i64 = 1000;
/// Secondary key type: none, use only P-384 for forward secrecy. /// Maximum number of pending incomplete sessions.
pub(crate) const HYBRID_KEY_TYPE_NONE: u8 = 0; pub(crate) const INCOMPLETE_SESSION_MAX_QUEUE_SIZE: usize = 256;
/// 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,
];

View file

@ -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; use crate::sessionid::SessionId;
pub enum Error { pub enum Error {

View file

@ -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 applicationlayer;
mod error; mod error;
mod proto; mod proto;
@ -10,4 +18,4 @@ pub mod constants;
pub use crate::applicationlayer::ApplicationLayer; pub use crate::applicationlayer::ApplicationLayer;
pub use crate::error::Error; pub use crate::error::Error;
pub use crate::sessionid::SessionId; pub use crate::sessionid::SessionId;
pub use crate::zssp::{ReceiveContext, ReceiveResult, Session}; pub use crate::zssp::{Context, ReceiveResult, Session};

View file

@ -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 std::mem::size_of;
use pqc_kyber::{KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES}; use pqc_kyber::{KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES};
use zerotier_crypto::hash::{HMAC_SHA384_SIZE, SHA384_HASH_SIZE}; 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::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::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 NOISE_MAX_HANDSHAKE_PACKET_SIZE: usize = 2048;
pub(crate) const PACKET_TYPE_DATA: u8 = 0; pub(crate) const PACKET_TYPE_DATA: u8 = 0;
pub(crate) const PACKET_TYPE_ALICE_EPHEMERAL_OFFER: u8 = 1; pub(crate) const PACKET_TYPE_ALICE_NOISE_XK_INIT: u8 = 1;
pub(crate) const PACKET_TYPE_BOB_EPHEMERAL_COUNTER_OFFER: u8 = 2; pub(crate) const PACKET_TYPE_BOB_NOISE_XK_ACK: u8 = 2;
pub(crate) const PACKET_TYPE_ALICE_STATIC_ACK: u8 = 3; 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_ALICE_REKEY_INIT: u8 = 4;
pub(crate) const PACKET_TYPE_BOB_REKEY_ACK: u8 = 5; 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)] #[allow(unused)]
#[repr(C, packed)] #[repr(C, packed)]
pub(crate) struct NoiseXKAliceEphemeralOffer { pub(crate) struct AliceNoiseXKInit {
pub header: [u8; HEADER_SIZE], pub header: [u8; HEADER_SIZE],
pub session_protocol_version: u8, pub session_protocol_version: u8,
pub reserved: [u8; 8],
pub alice_noise_e: [u8; P384_PUBLIC_KEY_SIZE], 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)) // -- start AES-CTR(es) encrypted section (IV is last 12 bytes of alice_noise_e))
pub alice_session_id: [u8; SESSION_ID_SIZE], pub alice_session_id: [u8; SessionId::SIZE],
pub alice_hk_public: [u8; KYBER_PUBLICKEYBYTES], 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 // -- end encrypted section
pub hmac_es: [u8; HMAC_SHA384_SIZE], pub hmac_es: [u8; HMAC_SHA384_SIZE],
} }
impl NoiseXKAliceEphemeralOffer { impl AliceNoiseXKInit {
pub const ENC_START: usize = HEADER_SIZE + 1 + 8 + P384_PUBLIC_KEY_SIZE; pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
pub const AUTH_START: usize = size_of::<NoiseXKAliceEphemeralOffer>() - HMAC_SHA384_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)] #[allow(unused)]
#[repr(C, packed)] #[repr(C, packed)]
pub(crate) struct NoiseXKBobEphemeralCounterOffer { pub(crate) struct BobNoiseXKAck {
pub header: [u8; HEADER_SIZE], pub header: [u8; HEADER_SIZE],
pub session_protocol_version: u8, pub session_protocol_version: u8,
pub bob_noise_e: [u8; P384_PUBLIC_KEY_SIZE], 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_hk_ciphertext: [u8; KYBER_CIPHERTEXTBYTES],
pub bob_note_to_self: [u8; size_of::<BobNoteToSelf>()],
// -- end encrypted sectiion // -- end encrypted sectiion
pub hmac_es_ee: [u8; HMAC_SHA384_SIZE], 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 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)] #[allow(unused)]
#[repr(C, packed)] #[repr(C, packed)]
pub(crate) struct NoiseXKAliceStaticAck { pub(crate) struct AliceNoiseXKAck {
pub header: [u8; HEADER_SIZE], pub header: [u8; HEADER_SIZE],
pub session_protocol_version: u8, 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)) // -- 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_length: [u8; 2],
pub alice_static_blob: [u8; ???], 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 // 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. // this way even on architectures that require type size aligned access.
pub(crate) trait ProtocolFlatBuffer {} pub(crate) trait ProtocolFlatBuffer {}
impl ProtocolFlatBuffer for NoiseXKAliceEphemeralOffer {} impl ProtocolFlatBuffer for AliceNoiseXKInit {}
impl ProtocolFlatBuffer for NoiseXKBobEphemeralCounterOffer {} impl ProtocolFlatBuffer for BobNoiseXKAck {}
impl ProtocolFlatBuffer for BobNoteToSelf {}
//impl ProtocolFlatBuffer for NoiseXKAliceStaticAck {} //impl ProtocolFlatBuffer for NoiseXKAliceStaticAck {}
impl ProtocolFlatBuffer for AliceRekeyInit {} impl ProtocolFlatBuffer for AliceRekeyInit {}
impl ProtocolFlatBuffer for BobRekeyAck {} 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) 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);
}
}

View file

@ -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::fmt::Display;
use std::num::NonZeroU64; use std::num::NonZeroU64;
use zerotier_crypto::random; use zerotier_crypto::random;
use zerotier_utils::memory::{array_range, as_byte_array}; 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) /// 48-bit session ID (most significant 16 bits of u64 are unused)
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]
pub struct SessionId(NonZeroU64); // stored little endian internally pub struct SessionId(NonZeroU64); // stored little endian internally
const SESSION_ID_SIZE_BYTES: usize = 6;
impl SessionId { impl SessionId {
pub const SIZE: usize = SESSION_ID_SIZE_BYTES;
pub const NONE: u64 = 0; pub const NONE: u64 = 0;
pub const MAX: u64 = 0xffffffffffff; pub const MAX: u64 = 0xffffffffffff;
@ -21,14 +30,14 @@ impl SessionId {
Self(NonZeroU64::new(i.to_le()).unwrap()) 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 { pub fn random() -> Self {
Self(NonZeroU64::new(((random::xorshift64_random() % (Self::MAX - 1)) + 1).to_le()).unwrap()) 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]; 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)) 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. /// Get this session ID as a little-endian byte array.
#[inline(always)] #[inline(always)]
pub(crate) fn as_bytes(&self) -> &[u8; SESSION_ID_SIZE] { pub(crate) fn as_bytes(&self) -> &[u8; Self::SIZE] {
array_range::<u8, 8, 0, SESSION_ID_SIZE>(as_byte_array(&self.0)) array_range::<u8, 8, 0, SESSION_ID_SIZE_BYTES>(as_byte_array(&self.0))
} }
} }

View file

@ -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)] #[allow(unused_imports)]
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -214,3 +223,4 @@ mod tests {
} }
} }
} }
*/

File diff suppressed because it is too large Load diff