diff --git a/README.md b/README.md index 9714530..f4f3f76 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -ZeroTier Secure Session Protocol +ZeroTier Secure Sessions Protocol ====== *NOTICE: ZSSP has not yet completed peer review or code audit, so use at your own risk for now. This will be updated as the project matures.* ## Introduction -ZeroTier Secure Session Protocol (ZSSP) is a [Noise](http://noiseprotocol.org) protocol implementation using NIST/FIPS/CfSC compliant cryptographic primitives plus post-quantum forward secrecy via [Kyber1024](https://pq-crystals.org/kyber/). It also includes built-in support for fragmentation and defragmentation of large messages with the fragmentation protocol being hardened against the usual denial of service attacks that plague most packet fragmentation and re-assembly protocols. +ZeroTier Secure Sessions Protocol (ZSSP) is a [Noise](http://noiseprotocol.org) protocol implementation using NIST/FIPS/CfSC compliant cryptographic primitives plus post-quantum forward secrecy via [Kyber1024](https://pq-crystals.org/kyber/). It also includes built-in support for fragmentation and defragmentation of large messages with the fragmentation protocol being hardened against the usual denial of service attacks that plague most packet fragmentation and re-assembly protocols. ZSSP implements the [Noise XK](http://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental) interactive handshake pattern which provides strong forward secrecy not only for data but for the identities of the two participants in the session. The XK pattern was chosen instead of the IK pattern used in most Noise implementations (e.g. Wireguard) due to ZeroTier identities being long lived and potentially tied to the real world identity of the user. As a result a Noise pattern providing identity forward secrecy was considered preferable as it offers some level of deniability for recorded traffic even after secret key compromise. Post-quantum forward secrecy is negotiated alongside Noise XK using a [hybrid forward secrecy model suggested by the Noise protocol authors](https://github.com/noiseprotocol/noise_wiki/wiki/Hybrid-Forward-Secrecy). @@ -19,7 +19,7 @@ ZSSP was designed for use in [ZeroTier](https://www.zerotier.com/) but is payloa This repository includes both a simpler [reference](reference/) implementation that follows the whitepaper very explicitly and a more complex [high performance](performance/) implementation designed for high throughput or use in systems that will manage very large numbers of ZSSP sessions. -See the [ZSSP whitepaper](whitepaper/zssp.pdf) for extensive documentation and proofs of security. +See the [ZSSP whitepaper](whitepaper/zssp.pdf) for extensive documentation of the protocol. ## Cryptographic Primitives Used in ZSSP @@ -29,43 +29,3 @@ See the [ZSSP whitepaper](whitepaper/zssp.pdf) for extensive documentation and p - **KBKDF**: Key mixing, sub-key derivation - **AES-256**: Single block encryption of header to harden packet fragmentation protocol - **AES-256-GCM**: Authenticated encryption - -## Comparison With A Few Other Protocols - -*Note that ZSSP can be used in two modes: persistent and opportunistic. Persistent mode persists the key ratcheting state of sessions while opportunistic mode will automatically reset if key ratcheting information is lost. The latter is designed for cases where persistent storage is unavailable or unreliable or when the user wishes to prioritize unattended reliability over the additional security provided by persistent mode.* - -| | Persistent ZSSP | Opportunistic ZSSP| WireGuard | ZeroTier Legacy Transport | -| --- | --- | --- | --- | --- | -|**Construction**|Noise\_XKhfs+psk2|Noise\_XKhfs+psk2|Noise\_IKpsk2|Static Diffie-Helman| -|**Perfect Forward Secrecy**|Yes|Yes|Yes|No| -|**Forward Secret Identity Hiding**|Yes|Yes|No|No| -|**Quantum Forward Secret**|Yes|Yes|No|No| -|**Ratcheted Forward Secrecy**|Yes|Yes|No|No| -|**Silence is a Virtue**|Yes|No|Yes|No| -|**Key-Compromise Impersonation**|Resistant|Resistant|Resistant|Vulnerable| -|**Compromise-and-Impersonate**|Resistant|Detectable|Vulnerable|Vulnerable| -|**Single Key-Compromise MitM**|Resistant|Resistant|Resistant|Vulnerable| -|**Double Key-Compromise MitM**|Resistant|Detectable|Vulnerable|Vulnerable| -|**DOS Mitigation**|Yes|Yes|Yes|No| -|**Supports Fragmentation**|Yes|Yes|No|Yes| -|**FIPS Compliant**|Yes|Yes|No|No| -|**Small Code Footprint**|Yes|Yes|Yes|No| -|**RTT**|2|2|1|Stateless| - -## Definitions - -* **Construction**: The mathematical construction the protocol is based upon. -* **Perfect Forward Secrecy**: An attacker with the static private keys of both party cannot decrypt recordings of messages sent between those parties. -* **Forward Secret Identity Hiding**: An attacker with the static private key of one or more parties cannot determine the identity of everyone they have previously communicated with. -* **Quantum Forward Secret**: A quantum computer powerful enough to break Elliptic-curve cryptography is not sufficient in order to decrypt recordings of messages sent between parties. -* **Ratcheted Forward Secrecy**: In order to break forward secrecy an attacker must record and break every single key exchange two parties perform, in order, starting from the first time they began communicating. Improves secrecy under weak or compromised RNG. -* **Silence is a Virtue**: A server running the protocol can be configured in such a way that it will not respond to an unauthenticated, anonymous or replayed message. -* **Key-Compromise Impersonation**: The attacker has a memory image of a single party, and attempts to create a brand new session with that party, pretending to be someone else. -* **Compromise-and-Impersonate**: The attacker has a memory image of a single party, and attempts to impersonate them on a brand new session with the other party. -* **Single Key-Compromise MitM**: The attacker has a memory image of a single party, and attempts to become a Man-in-the-Middle between them and any other party. -* **Double Key-Compromise MitM**: The attacker has a memory image of both parties, and attempts to become a Man-in-the-Middle between them. -* **Supports Fragmentation**: Transmission data can be fragmented into smaller units to support small physical MTUs. -* **FIPS Compliant**: The cryptographic algorithms used are compliant with NIST/FIPS-140 requirements. -* **CSfC**: The cryptographic algorithms used are compliant with the [NSA Commercial Solutions for Classified (CSfC)](https://www.nsa.gov/Resources/Commercial-Solutions-for-Classified-Program/) program. -* **Small Code Footprint**: The code implementing the protocol is separate from other concerns, is concise, and is therefore easy to audit. -* **RTT**: "Round-Trip-Time" - How many round trips from initiator to responder it takes to establish a session. diff --git a/performance/src/application.rs b/performance/src/application.rs index 8c595fb..5a1a00b 100644 --- a/performance/src/application.rs +++ b/performance/src/application.rs @@ -96,24 +96,44 @@ pub trait CryptoLayer: Sized { /// the protocol will tend to default to the smaller constants. const SETTINGS: Settings = Settings::new_ms(); + /// The random number generator that ZSSP should use. + /// It is used infrequently, but should still be cryptographically secure. + /// + /// FIPS compliance requires use of a FIPS certified implementation. type Rng: CryptoRng + RngCore; - /// The implementation of AES-256 Encryption that ZSSP should use. + /// The implementation of AES-256 block encryption that ZSSP should use. /// /// FIPS compliance requires use of a FIPS certified implementation. type PrpEnc: Aes256Enc; - /// The implementation of AES-256 Decryption that ZSSP should use. + /// The implementation of AES-256 block decryption that ZSSP should use. /// /// FIPS compliance requires use of a FIPS certified implementation. type PrpDec: Aes256Dec; + /// An implementation of AES-GCM-256 that ZSSP should use, with a simpler interface and lower + /// throughput requirements. + /// This is used for one-off encryption or decryption using one of the extremely short-lived + /// temporary keys within Noise. The Noise encryption keys are never used with this. + /// + /// FIPS compliance requires a FIPS certified implementation. type Aead: LowThroughputAesGcm; + /// The primary implementation of AES-GCM-256 that ZSSP should use. + /// One of these is created every time a new pair of Noise encryption keys is generated, and it + /// handles all encryption and decryption for data packets. + /// The efficiency and security of ZSSP is very closely tied to the efficiency and security of + /// this implementation. + /// + /// FIPS compliance requires a FIPS certified implementation. type AeadPool: HighThroughputAesGcmPool; /// The implementation of SHA-512 that ZSSP should use. /// /// FIPS compliance requires use of a FIPS certified implementation. type Hash: Sha512Hash; + /// The implementation of HMAC-SHA-512 that ZSSP should use. + /// + /// FIPS compliance requires use of a FIPS certified implementation. type Hmac: Sha512Hmac; /// The implementation of P-384 public keys that ZSSP should use. /// @@ -142,6 +162,10 @@ pub trait CryptoLayer: Sized { type IncomingPacketBuffer: AsRef<[u8]> + AsMut<[u8]>; } +/// Trait to implement to integrate ZSSP into an application. +/// +/// Templating ZSSP on this trait lets the code here be almost entirely transport, OS, +/// and use case independent. pub trait ApplicationLayer: Sized { /// Should return the current time in milliseconds. Does not have to be monotonic, nor synced /// with remote peers (although both of these properties would help reliability slightly). @@ -307,6 +331,11 @@ pub trait Sender { /// /// Is implemented by `FnMut(&Arc>) -> Option<(Sender, usize)>` closures. pub trait SendTo { + /// The `Sender` implementation that this `SendTo` implementation will return. + /// + /// It is allowed to have a lifetime that is borrowed from the `SendTo` instance + /// which created it. This allows it to potentially contain references or lock guards + /// in situations where that is more efficient. type Sender<'a>: Sender where Crypto: 'a, diff --git a/performance/src/crypto/aes.rs b/performance/src/crypto/aes.rs index 7921c30..fb0dbd2 100644 --- a/performance/src/crypto/aes.rs +++ b/performance/src/crypto/aes.rs @@ -1,8 +1,11 @@ -// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md. +/// The specified size of an AES-256 key. pub const AES_256_KEY_SIZE: usize = 32; +/// The specified size of an AES block. pub const AES_256_BLOCK_SIZE: usize = 16; +/// The specified size of an AES-GCM authentication tag. pub const AES_GCM_TAG_SIZE: usize = 16; +/// The specified size of an AES-GCM nonce. pub const AES_GCM_NONCE_SIZE: usize = 12; /// A trait for encrypting individual blocks of plaintext using AES-256. @@ -11,6 +14,7 @@ pub const AES_GCM_NONCE_SIZE: usize = 12; /// /// Instances must securely delete their keys when dropped or reset. pub trait Aes256Enc: Sized + Send + Sync { + /// Create a new instance of this trait that uses the given key for encryption. fn new(key: &[u8; AES_256_KEY_SIZE]) -> Self; /// Change the encryption key to `key` so that all future encryption is performed with it. @@ -19,9 +23,9 @@ pub trait Aes256Enc: Sized + Send + Sync { *self = Self::new(key); } - /// Decrypt the given `block` of plaintext directly using the AES block cipher + /// Encrypt the given `block` of plaintext directly using the AES block cipher /// (i.e. AES-256 in zero-padding ECB mode). - /// The ciphertext should be written directly back out to `block`. + /// The ciphertext should be written directly back to `block`. fn encrypt_in_place(&self, block: &mut [u8; AES_256_BLOCK_SIZE]); } @@ -29,6 +33,7 @@ pub trait Aes256Enc: Sized + Send + Sync { /// /// Instances must securely delete their keys when dropped or reset. pub trait Aes256Dec: Sized + Send + Sync { + /// Create a new instance of this trait that uses the given key for decryption. fn new(key: &[u8; AES_256_KEY_SIZE]) -> Self; /// Change the decryption key to `key` so that all future decryption is performed with it. @@ -56,6 +61,11 @@ pub trait AesGcmDecContext { fn finish(self, tag: &[u8; AES_GCM_TAG_SIZE]) -> bool; } +/// A trait for implementing AES-GCM-256 in a way that allows for extremely high throughput. +/// One instance of this trait is created whenever a new pair of noise keys are created, +/// and it handles all data encryption that passes through that session. +/// +/// Instances must securely delete their keys when dropped. pub trait HighThroughputAesGcmPool: Send + Sync { type EncContext<'a>: AesGcmEncContext where @@ -64,12 +74,23 @@ pub trait HighThroughputAesGcmPool: Send + Sync { where Self: 'a; + /// Create a new instance of this trait. + /// `encrypt_key` must be used as the encryption key. + /// `decrypt_key` must be used as the decryption key. fn new(encrypt_key: &[u8; AES_256_KEY_SIZE], decrypt_key: &[u8; AES_256_KEY_SIZE]) -> Self; + /// Borrow an encryption context to be used to stream encrypt a message. + /// `nonce` must be set as the AEAD nonce. + /// There is no additional associated data to be used. fn start_enc<'a>(&'a self, nonce: &[u8; AES_GCM_NONCE_SIZE]) -> Self::EncContext<'a>; + /// Borrow a decryption context to be used to stream decrypt a message. + /// `nonce` must be set as the AEAD nonce. + /// There is no additional associated data to be used. fn start_dec<'a>(&'a self, nonce: &[u8; AES_GCM_NONCE_SIZE]) -> Self::DecContext<'a>; } +/// A trait for implementing AES-GCM-256 to handle the more varied, but much lower throughput +/// requirements of a Noise handshake. pub trait LowThroughputAesGcm { fn encrypt_in_place( key: &[u8; AES_256_KEY_SIZE], diff --git a/performance/src/crypto/mod.rs b/performance/src/crypto/mod.rs index 2b6ea78..ef81a1c 100644 --- a/performance/src/crypto/mod.rs +++ b/performance/src/crypto/mod.rs @@ -1,3 +1,10 @@ +/* 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 aes; pub use self::aes::*; diff --git a/performance/src/crypto/sha512.rs b/performance/src/crypto/sha512.rs index 4e064d5..2d88fab 100644 --- a/performance/src/crypto/sha512.rs +++ b/performance/src/crypto/sha512.rs @@ -1,8 +1,10 @@ -// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md. +/// The size of a SHA512 hash, which is always 64 bytes pub const SHA512_HASH_SIZE: usize = 64; /// A SHA-512 implementation. +/// Its interface was designed to make this ZSSP implementation reasonably efficient. +/// Does not need to be threadsafe. pub trait Sha512Hash { /// Create a new instance of SHA-512 for streaming data to. fn new() -> Self; @@ -10,10 +12,14 @@ pub trait Sha512Hash { /// This must update the state of SHA-512 as if `data` was appended to the previous input. fn update(&mut self, data: &[u8]); /// Finish streaming input and output the final hash. + /// The hash must be written to `output`. + /// + /// This instance should be reset so that a new, independent hash can be generated. fn finish_and_reset(&mut self, output: &mut [u8; SHA512_HASH_SIZE]); } -/// Opaque HMAC-SHA-512 implementation. +/// A HMAC-SHA-512 implementation. +/// Its interface was designed to make this ZSSP implementation reasonably efficient. /// Does not need to be threadsafe. pub trait Sha512Hmac { /// Allocate space on the stack or heap for repeated Hmac invocations. diff --git a/performance/src/crypto_impl/mod.rs b/performance/src/crypto_impl/mod.rs index 8aa0ceb..96254a5 100644 --- a/performance/src/crypto_impl/mod.rs +++ b/performance/src/crypto_impl/mod.rs @@ -1,3 +1,10 @@ +/* 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/ + */ #[cfg(feature = "pqc_kyber")] mod kyber1024; #[cfg(feature = "pqc_kyber")] diff --git a/performance/src/indexed_heap.rs b/performance/src/indexed_heap.rs index a13167f..53e03a2 100644 --- a/performance/src/indexed_heap.rs +++ b/performance/src/indexed_heap.rs @@ -1,4 +1,10 @@ -// By Monica +/* 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/ + */ /// A generational index into an `IndexedBinaryHeap`. /// Used to perform direct interactions with specific items contained within the binary heap. diff --git a/performance/src/lib.rs b/performance/src/lib.rs index 1ba81e3..a002b35 100644 --- a/performance/src/lib.rs +++ b/performance/src/lib.rs @@ -5,16 +5,52 @@ * (c) ZeroTier, Inc. * https://www.zerotier.com/ */ -//#![warn(missing_docs, rust_2018_idioms)] +//! ZeroTier Secure Sessions Protocol +//! ====== +//! +//! *NOTICE: ZSSP has not yet completed peer review or code audit, so use at your own risk for now. This will be updated as the project matures.* +//! +//! ## Introduction +//! +//! ZeroTier Secure Sessions Protocol (ZSSP) is a [Noise](http://noiseprotocol.org) protocol implementation using NIST/FIPS/CfSC compliant cryptographic primitives plus post-quantum forward secrecy via [Kyber1024](https://pq-crystals.org/kyber/). It also includes built-in support for fragmentation and defragmentation of large messages with the fragmentation protocol being hardened against the usual denial of service attacks that plague most packet fragmentation and re-assembly protocols. +//! +//! ZSSP implements the [Noise XK](http://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental) interactive handshake pattern which provides strong forward secrecy not only for data but for the identities of the two participants in the session. The XK pattern was chosen instead of the IK pattern used in most Noise implementations (e.g. Wireguard) due to ZeroTier identities being long lived and potentially tied to the real world identity of the user. As a result a Noise pattern providing identity forward secrecy was considered preferable as it offers some level of deniability for recorded traffic even after secret key compromise. Post-quantum forward secrecy is negotiated alongside Noise XK using a [hybrid forward secrecy model suggested by the Noise protocol authors](https://github.com/noiseprotocol/noise_wiki/wiki/Hybrid-Forward-Secrecy). +//! +//! Periodic session re-keying uses the [Noise KK](http://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental) pattern with key ratcheting based in part on the methods used by the [Signal protocol](https://signal.org/docs/specifications/doubleratchet/). Unlike Signal ratcheting is performed only on re-key events and not on every message as this would be prohibitively costly for a protocol designed for high throughput applications. +//! +//! Re-keying does not employ a hybrid exchange. Post-quantum forward secrecy is negotiated only on session startup since the threat model underpinning its use is to protect against very long term data storage and future decryption with quantum computers. Ratcheting causes the result of the initial ephemeral PQ exchange to be mixed into all subsequent session keys, protecting the entire session against a future attacker able to break elliptic curve cryptography. +//! +//! An in-depth guide to the full protocol specification can be found in the [protocol whitepaper](whitepaper/zssp.pdf) provided in this repository. +//! +//! ZSSP was designed for use in [ZeroTier](https://www.zerotier.com/) but is payload agnostic and open source and can easily be used by other projects. The implementations here are based around generic cryptographic traits that a user can implement in terms of any cryptographic library of API they wish to use. Default implementations in terms of popular Rust cryptography crates are included but can be disabled via feature selection if alternatives are to be used. +//! +//! This repository includes both a simpler [reference](reference/) implementation that follows the whitepaper very explicitly and a more complex [high performance](performance/) implementation designed for high throughput or use in systems that will manage very large numbers of ZSSP sessions. +//! +//! See the [ZSSP whitepaper](whitepaper/zssp.pdf) for extensive documentation of the protocol. +//! +//! ## Cryptographic Primitives Used in ZSSP +//! +//! - **NIST P-384 ECDH**: Elliptic curve used in initial Noise XK and subsequent Noise KK key exchanges +//! - **Kyber1024**: Quantum attack resistant lattice-based key exchange during initial handshake +//! - **SHA-512**: Used to construct KBKDF, also used in a proof of work and IP ownership DOS mitigation scheme +//! - **KBKDF**: Key mixing, sub-key derivation +//! - **AES-256**: Single block encryption of header to harden packet fragmentation protocol +//! - **AES-256-GCM**: Authenticated encryption +#![warn(missing_docs, rust_2018_idioms)] pub mod crypto; pub mod crypto_impl; +/// A module that implements a priority queue using a binary heap. +/// Generational indexing is used to improve performance and simplify lifetime management. +/// +/// This module is used by this implementation of ZSSP, but it isn't a core component of the protocol. +/// Rather, it is a reuseable component that you may find useful on its own. +pub mod indexed_heap; mod antireplay; mod challenge; mod frag_cache; mod fragged; mod handshake_cache; -pub mod indexed_heap; mod log_event; mod ratchet_state; mod symmetric_state; diff --git a/performance/src/log_event.rs b/performance/src/log_event.rs index 3671abd..dd14326 100644 --- a/performance/src/log_event.rs +++ b/performance/src/log_event.rs @@ -4,6 +4,7 @@ use crate::application::CryptoLayer; use crate::zeta::Session; /// ZSSP events that might be interesting to log or aggregate into metrics. +#[allow(missing_docs)] pub enum LogEvent<'a, Crypto: CryptoLayer> { ResentX1(&'a Arc>), TimeoutX1(&'a Arc>), diff --git a/performance/src/zeta.rs b/performance/src/zeta.rs index 727e465..e649136 100644 --- a/performance/src/zeta.rs +++ b/performance/src/zeta.rs @@ -27,9 +27,12 @@ use crate::LogEvent::*; /// Corresponds to the Zeta State Machine found in Section 4.1. pub struct Session { ctx: Weak>, - /// An arbitrary application defined object associated with each session. + /// An arbitrary, application defined object allocated with each session. + /// + /// Users of ZSSP are encouraged to use this field extensively to associate ZSSP sessions with + /// whatever your application's notion of a "remote peer" is. pub session_data: Crypto::SessionData, - /// Is true if the local peer acted as Bob, the responder in the initial key exchange. + /// This field is true if the local peer acted as Bob, the responder in the initial key exchange. pub was_bob: bool, queue_idx: BinaryHeapIndex, @@ -37,7 +40,7 @@ pub struct Session { send_counter: AtomicU64, session_has_expired: AtomicBool, - pub window: Window, + pub(crate) window: Window, pub(crate) defrag: [Mutex>; SESSION_MAX_FRAGMENTS_OOO], /// `session_queue -> state_machine_lock -> state -> session_map` diff --git a/performance/src/zssp.rs b/performance/src/zssp.rs index 605dcde..f633f5e 100644 --- a/performance/src/zssp.rs +++ b/performance/src/zssp.rs @@ -48,9 +48,13 @@ pub(crate) type SessionMap = RwLock = IndexedBinaryHeap>, Reverse>; +/// The internal memory of the ZSSP context. +/// One of these is allocated as an `Arc` to initialize this implementation of ZSSP. +/// See `Context::new`. pub struct ContextInner { + /// The `CryptoRng` instance that was passed to ZSSP when this context was created. pub rng: Mutex, - pub next_service_time: AtomicI64, + pub(crate) next_service_time: AtomicI64, pub(crate) s_secret: Crypto::KeyPair, /// `session_queue -> state_machine_lock -> state -> session_map` pub(crate) session_queue: Mutex>, diff --git a/reference/src/application.rs b/reference/src/application.rs index 53af098..5ee301e 100644 --- a/reference/src/application.rs +++ b/reference/src/application.rs @@ -94,6 +94,7 @@ pub trait CryptoLayer: Sized { const SETTINGS: Settings = Settings::new_ms(); /// The random number generator that ZSSP should use. + /// It is used infrequently, but should still be cryptographically secure. /// /// FIPS compliance requires use of a FIPS certified implementation. type Rng: CryptoRng + RngCore; diff --git a/reference/src/crypto/mod.rs b/reference/src/crypto/mod.rs index 16279f0..b1db8fb 100644 --- a/reference/src/crypto/mod.rs +++ b/reference/src/crypto/mod.rs @@ -1,3 +1,10 @@ +/* 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 aes; pub use self::aes::*; diff --git a/reference/src/crypto_impl/mod.rs b/reference/src/crypto_impl/mod.rs index 921af90..56cb86d 100644 --- a/reference/src/crypto_impl/mod.rs +++ b/reference/src/crypto_impl/mod.rs @@ -1,3 +1,10 @@ +/* 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/ + */ #[cfg(feature = "aes-gcm")] mod aes_impl; #[cfg(feature = "aes-gcm")] diff --git a/reference/src/lib.rs b/reference/src/lib.rs index 347e5d1..415fe06 100644 --- a/reference/src/lib.rs +++ b/reference/src/lib.rs @@ -8,27 +8,33 @@ //! ZeroTier Secure Sessions Protocol //! ====== //! +//! *NOTICE: ZSSP has not yet completed peer review or code audit, so use at your own risk for now. This will be updated as the project matures.* +//! //! ## Introduction //! -//! An in-depth guide to the full protocol specification can be found in the [protocol whitepaper](whitepaper/zssp.pdf) provided in this repo. This implementation references it heavily. +//! ZeroTier Secure Sessions Protocol (ZSSP) is a [Noise](http://noiseprotocol.org) protocol implementation using NIST/FIPS/CfSC compliant cryptographic primitives plus post-quantum forward secrecy via [Kyber1024](https://pq-crystals.org/kyber/). It also includes built-in support for fragmentation and defragmentation of large messages with the fragmentation protocol being hardened against the usual denial of service attacks that plague most packet fragmentation and re-assembly protocols. //! -//! ZeroTier Secure Socket Protocol (ZSSP) is a [Noise](http://noiseprotocol.org) protocol implementation using NIST/FIPS/CfSC compliant cryptographic primitives plus post-quantum forward secrecy via [Kyber1024](https://pq-crystals.org/kyber/). It also includes built-in support for fragmentation and defragmentation of large messages with strong resistance against denial of service attacks targeted against the fragmentation protocol. +//! ZSSP implements the [Noise XK](http://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental) interactive handshake pattern which provides strong forward secrecy not only for data but for the identities of the two participants in the session. The XK pattern was chosen instead of the IK pattern used in most Noise implementations (e.g. Wireguard) due to ZeroTier identities being long lived and potentially tied to the real world identity of the user. As a result a Noise pattern providing identity forward secrecy was considered preferable as it offers some level of deniability for recorded traffic even after secret key compromise. Post-quantum forward secrecy is negotiated alongside Noise XK using a [hybrid forward secrecy model suggested by the Noise protocol authors](https://github.com/noiseprotocol/noise_wiki/wiki/Hybrid-Forward-Secrecy). //! -//! Specifically ZSSP implements the [Noise XK](http://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental) interactive handshake pattern which provides strong forward secrecy not only for data but for the identities of the two participants in the session. The XK pattern was chosen instead of the more popular IK pattern used in popular Noise implementations like Wireguard due to ZeroTier identities being long lived and potentially tied to the real world identity of the user. As a result a Noise pattern providing identity forward secrecy was considered preferable as it offers some level of deniability for recorded traffic even after secret key compromise. +//! Periodic session re-keying uses the [Noise KK](http://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental) pattern with key ratcheting based in part on the methods used by the [Signal protocol](https://signal.org/docs/specifications/doubleratchet/). Unlike Signal ratcheting is performed only on re-key events and not on every message as this would be prohibitively costly for a protocol designed for high throughput applications. //! -//! Hybrid post-quantum forward secrecy using Kyber1024 is performed alongside Noise with the result being mixed in alongside an optional pre-shared key at the end of session negotiation. +//! Re-keying does not employ a hybrid exchange. Post-quantum forward secrecy is negotiated only on session startup since the threat model underpinning its use is to protect against very long term data storage and future decryption with quantum computers. Ratcheting causes the result of the initial ephemeral PQ exchange to be mixed into all subsequent session keys, protecting the entire session against a future attacker able to break elliptic curve cryptography. //! -//! ZSSP is designed for use in ZeroTier but is payload-agnostic and could easily be adapted for use in other projects. +//! An in-depth guide to the full protocol specification can be found in the [protocol whitepaper](whitepaper/zssp.pdf) provided in this repository. //! -//! Further information can be found in the ZSSP whitepaper [protocol whitepaper](whitepaper/zssp.pdf). +//! ZSSP was designed for use in [ZeroTier](https://www.zerotier.com/) but is payload agnostic and open source and can easily be used by other projects. The implementations here are based around generic cryptographic traits that a user can implement in terms of any cryptographic library of API they wish to use. Default implementations in terms of popular Rust cryptography crates are included but can be disabled via feature selection if alternatives are to be used. //! -//! ## Cryptographic Primitives Used +//! This repository includes both a simpler [reference](reference/) implementation that follows the whitepaper very explicitly and a more complex [high performance](performance/) implementation designed for high throughput or use in systems that will manage very large numbers of ZSSP sessions. //! -//! - **NIST P-384 ECDH**: Elliptic curve key exchange during initial handshake and for periodic re-keying during the session +//! See the [ZSSP whitepaper](whitepaper/zssp.pdf) for extensive documentation of the protocol. +//! +//! ## Cryptographic Primitives Used in ZSSP +//! +//! - **NIST P-384 ECDH**: Elliptic curve used in initial Noise XK and subsequent Noise KK key exchanges //! - **Kyber1024**: Quantum attack resistant lattice-based key exchange during initial handshake //! - **SHA-512**: Used to construct KBKDF, also used in a proof of work and IP ownership DOS mitigation scheme //! - **KBKDF**: Key mixing, sub-key derivation -//! - **AES-256**: 128-bit PRP for AES-256-GCM and for authenticated encryption of header to harden fragmentation against DOS (see section on header protection) +//! - **AES-256**: Single block encryption of header to harden packet fragmentation protocol //! - **AES-256-GCM**: Authenticated encryption #![warn(missing_docs, rust_2018_idioms)] diff --git a/whitepaper/comparison.md b/whitepaper/comparison.md new file mode 100644 index 0000000..0189983 --- /dev/null +++ b/whitepaper/comparison.md @@ -0,0 +1,40 @@ + +## Comparison With A Few Other Protocols + +*Note that ZSSP can be used in two modes: persistent and opportunistic. Persistent mode persists the key ratcheting state of sessions while opportunistic mode will automatically reset if key ratcheting information is lost. The latter is designed for cases where persistent storage is unavailable or unreliable or when the user wishes to prioritize unattended reliability over the additional security provided by persistent mode.* + +| | Persistent ZSSP | Opportunistic ZSSP| WireGuard | ZeroTier Legacy Transport | +| --- | --- | --- | --- | --- | +|**Construction**|Noise\_XKhfs+psk2|Noise\_XKhfs+psk2|Noise\_IKpsk2|Static Diffie-Helman| +|**Perfect Forward Secrecy**|Yes|Yes|Yes|No| +|**Forward Secret Identity Hiding**|Yes|Yes|No|No| +|**Quantum Forward Secret**|Yes|Yes|No|No| +|**Ratcheted Forward Secrecy**|Yes|Yes|No|No| +|**Silence is a Virtue**|Yes|No|Yes|No| +|**Key-Compromise Impersonation**|Resistant|Resistant|Resistant|Vulnerable| +|**Compromise-and-Impersonate**|Resistant|Detectable|Vulnerable|Vulnerable| +|**Single Key-Compromise MitM**|Resistant|Resistant|Resistant|Vulnerable| +|**Double Key-Compromise MitM**|Resistant|Detectable|Vulnerable|Vulnerable| +|**DOS Mitigation**|Yes|Yes|Yes|No| +|**Supports Fragmentation**|Yes|Yes|No|Yes| +|**FIPS Compliant**|Yes|Yes|No|No| +|**Small Code Footprint**|Yes|Yes|Yes|No| +|**RTT**|2|2|1|Stateless| + +## Definitions + +* **Construction**: The mathematical construction the protocol is based upon. +* **Perfect Forward Secrecy**: An attacker with the static private keys of both party cannot decrypt recordings of messages sent between those parties. +* **Forward Secret Identity Hiding**: An attacker with the static private key of one or more parties cannot determine the identity of everyone they have previously communicated with. +* **Quantum Forward Secret**: A quantum computer powerful enough to break Elliptic-curve cryptography is not sufficient in order to decrypt recordings of messages sent between parties. +* **Ratcheted Forward Secrecy**: In order to break forward secrecy an attacker must record and break every single key exchange two parties perform, in order, starting from the first time they began communicating. Improves secrecy under weak or compromised RNG. +* **Silence is a Virtue**: A server running the protocol can be configured in such a way that it will not respond to an unauthenticated, anonymous or replayed message. +* **Key-Compromise Impersonation**: The attacker has a memory image of a single party, and attempts to create a brand new session with that party, pretending to be someone else. +* **Compromise-and-Impersonate**: The attacker has a memory image of a single party, and attempts to impersonate them on a brand new session with the other party. +* **Single Key-Compromise MitM**: The attacker has a memory image of a single party, and attempts to become a Man-in-the-Middle between them and any other party. +* **Double Key-Compromise MitM**: The attacker has a memory image of both parties, and attempts to become a Man-in-the-Middle between them. +* **Supports Fragmentation**: Transmission data can be fragmented into smaller units to support small physical MTUs. +* **FIPS Compliant**: The cryptographic algorithms used are compliant with NIST/FIPS-140 requirements. +* **CSfC**: The cryptographic algorithms used are compliant with the [NSA Commercial Solutions for Classified (CSfC)](https://www.nsa.gov/Resources/Commercial-Solutions-for-Classified-Program/) program. +* **Small Code Footprint**: The code implementing the protocol is separate from other concerns, is concise, and is therefore easy to audit. +* **RTT**: "Round-Trip-Time" - How many round trips from initiator to responder it takes to establish a session.