mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 13:33:44 +02:00
Attic some stuff, cleanup.
This commit is contained in:
parent
4a6e88344c
commit
ca823e5fa3
18 changed files with 78 additions and 355 deletions
|
@ -8,9 +8,10 @@
|
|||
|
||||
use crate::secret::Secret;
|
||||
|
||||
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
||||
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
||||
|
||||
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<48> {
|
||||
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
||||
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
||||
Secret(crate::hash::hmac_sha384(key, &[
|
||||
(iter >> 24) as u8,
|
||||
(iter >> 16) as u8,
|
||||
|
@ -25,8 +26,6 @@ pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Se
|
|||
}
|
||||
|
||||
pub fn zt_kbkdf_hmac_sha512(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<64> {
|
||||
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
||||
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
||||
Secret(crate::hash::hmac_sha512(key, &[
|
||||
(iter >> 24) as u8,
|
||||
(iter >> 16) as u8,
|
||||
|
|
|
@ -16,7 +16,6 @@ pub mod random;
|
|||
pub mod secret;
|
||||
pub mod hex;
|
||||
pub mod varint;
|
||||
pub mod sidhp751;
|
||||
|
||||
pub use aes_gmac_siv;
|
||||
pub use rand_core;
|
||||
|
|
|
@ -6,10 +6,34 @@
|
|||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
use openssl::rand::rand_bytes;
|
||||
|
||||
pub struct SecureRandom;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn next_u32_secure() -> u32 {
|
||||
unsafe {
|
||||
let mut tmp: [u32; 1] = MaybeUninit::uninit().assume_init();
|
||||
assert!(rand_bytes(&mut *(tmp.as_mut_ptr().cast::<[u8; 4]>())).is_ok());
|
||||
tmp[0]
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn next_u64_secure() -> u64 {
|
||||
unsafe {
|
||||
let mut tmp: [u64; 1] = MaybeUninit::uninit().assume_init();
|
||||
assert!(rand_bytes(&mut *(tmp.as_mut_ptr().cast::<[u8; 8]>())).is_ok());
|
||||
tmp[0]
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fill_bytes_secure(dest: &mut [u8]) {
|
||||
assert!(rand_bytes(dest).is_ok());
|
||||
}
|
||||
|
||||
impl SecureRandom {
|
||||
#[inline(always)]
|
||||
pub fn get() -> Self { Self }
|
||||
|
@ -17,23 +41,13 @@ impl SecureRandom {
|
|||
|
||||
impl rand_core::RngCore for SecureRandom {
|
||||
#[inline(always)]
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let mut tmp = 0_u32;
|
||||
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u32).cast::<[u8; 4]>() }).is_ok());
|
||||
tmp
|
||||
}
|
||||
fn next_u32(&mut self) -> u32 { next_u32_secure() }
|
||||
|
||||
#[inline(always)]
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let mut tmp = 0_u64;
|
||||
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u64).cast::<[u8; 8]>() }).is_ok());
|
||||
tmp
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 { next_u64_secure() }
|
||||
|
||||
#[inline(always)]
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
assert!(rand_bytes(dest).is_ok());
|
||||
}
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) { fill_bytes_secure(dest); }
|
||||
|
||||
#[inline(always)]
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
|
@ -43,24 +57,7 @@ impl rand_core::RngCore for SecureRandom {
|
|||
|
||||
impl rand_core::CryptoRng for SecureRandom {}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn next_u32_secure() -> u32 {
|
||||
let mut tmp = 0_u32;
|
||||
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u32).cast::<[u8; 4]>() }).is_ok());
|
||||
tmp
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn next_u64_secure() -> u64 {
|
||||
let mut tmp = 0_u64;
|
||||
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u64).cast::<[u8; 8]>() }).is_ok());
|
||||
tmp
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fill_bytes_secure(dest: &mut [u8]) {
|
||||
assert!(rand_bytes(dest).is_ok());
|
||||
}
|
||||
unsafe impl Send for SecureRandom {}
|
||||
|
||||
static mut XORSHIFT64_STATE: u64 = 0;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::write_volatile;
|
||||
|
||||
/// Container for secrets that clears them on drop.
|
||||
|
@ -18,6 +19,7 @@ use std::ptr::write_volatile;
|
|||
/// but it's still not a bad idea due to things like swap or obscure side channel
|
||||
/// attacks that allow memory to be read.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Secret<const L: usize>(pub [u8; L]);
|
||||
|
||||
impl<const L: usize> Secret<L> {
|
||||
|
@ -30,13 +32,21 @@ impl<const L: usize> Secret<L> {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8; L] { return &self.0 }
|
||||
|
||||
/// Get a clone of the first N bytes of this secret.
|
||||
#[inline(always)]
|
||||
pub fn first_n<const N: usize>(&self) -> Secret<N> {
|
||||
let mut tmp: Secret<N> = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
tmp.0.copy_from_slice(&self.0[..N]);
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> Drop for Secret<L> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
for i in 0..L {
|
||||
write_volatile(self.0.as_mut_ptr().offset(i as isize), 0_u8);
|
||||
write_volatile(self.0.as_mut_ptr().add(i), 0_u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,300 +0,0 @@
|
|||
/* 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)2021 ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::io::Write;
|
||||
|
||||
use zerotier_core_crypto::c25519::{C25519KeyPair, C25519_PUBLIC_KEY_SIZE};
|
||||
use zerotier_core_crypto::hash::*;
|
||||
use zerotier_core_crypto::kbkdf::zt_kbkdf_hmac_sha512;
|
||||
use zerotier_core_crypto::p384::*;
|
||||
use zerotier_core_crypto::random::SecureRandom;
|
||||
use zerotier_core_crypto::secret::Secret;
|
||||
use zerotier_core_crypto::sidhp751::{SIDHPublicKeyAlice, SIDHPublicKeyBob, SIDHSecretKeyAlice, SIDHSecretKeyBob, SIDH_P751_PUBLIC_KEY_SIZE};
|
||||
use zerotier_core_crypto::varint;
|
||||
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::protocol::*;
|
||||
use crate::vl1::symmetricsecret::SymmetricSecret;
|
||||
|
||||
pub const ALGORITHM_C25519: u8 = 0x01;
|
||||
pub const ALGORITHM_NISTP384ECDH: u8 = 0x02;
|
||||
pub const ALGORITHM_SIDHP751: u8 = 0x04;
|
||||
|
||||
/// A set of ephemeral secret key pairs and related information.
|
||||
pub(crate) struct EphemeralKeyPairSet {
|
||||
next_key: Secret<64>,
|
||||
c25519: C25519KeyPair,
|
||||
p384: P384KeyPair,
|
||||
sidhp751: Option<SIDHEphemeralKeyPair>,
|
||||
previous_cumulative_algorithms: u8,
|
||||
}
|
||||
|
||||
impl EphemeralKeyPairSet {
|
||||
/// Create a new ephemeral set of secret/public key pairs.
|
||||
///
|
||||
/// This contains key pairs for the asymmetric key agreement algorithms used and a
|
||||
/// timestamp used to enforce TTL.
|
||||
///
|
||||
/// The key and cumulative algorithms should come from the current in-effect ephemeral secret or
|
||||
/// should be the long-term static secret and zero if there isn't one.
|
||||
///
|
||||
/// SIDH is slow, so it's only included in the exchange if it's never been included in any previous
|
||||
/// exchange. The threat model here is long-term data warehousing in anticipation of QC, so one SIDH
|
||||
/// per ephemeral session is probably good enough for that.
|
||||
pub fn new(local_address: Address, remote_address: Address, key: &SymmetricSecret, cumulative_algorithms: u8) -> Self {
|
||||
Self {
|
||||
next_key: zt_kbkdf_hmac_sha512(key.key.as_bytes(), KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_NEXT_KEY, 0, 0),
|
||||
c25519: C25519KeyPair::generate(),
|
||||
p384: P384KeyPair::generate(),
|
||||
sidhp751: if (cumulative_algorithms & ALGORITHM_SIDHP751) == 0 {
|
||||
Some(SIDHEphemeralKeyPair::generate(local_address, remote_address))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
previous_cumulative_algorithms: cumulative_algorithms
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a public version of this ephemeral secret to share with our counterparty.
|
||||
///
|
||||
/// Note that the public key bundle is NOT self-signed or otherwise self-authenticating. It must
|
||||
/// be transmitted over an authenticated channel.
|
||||
pub fn public_bytes(&self) -> Vec<u8> {
|
||||
let mut b: Vec<u8> = Vec::with_capacity(C25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + SIDH_P751_PUBLIC_KEY_SIZE + 8);
|
||||
|
||||
b.push(ALGORITHM_C25519);
|
||||
let _ = varint::write(&mut b, C25519_PUBLIC_KEY_SIZE as u64);
|
||||
let _ = b.write_all(&self.c25519.public_bytes());
|
||||
|
||||
if let Some(sidhp751) = self.sidhp751.as_ref() {
|
||||
b.push(ALGORITHM_SIDHP751);
|
||||
let _ = varint::write(&mut b, (SIDH_P751_PUBLIC_KEY_SIZE + 1) as u64);
|
||||
b.push(sidhp751.role());
|
||||
let pk = match &sidhp751 {
|
||||
SIDHEphemeralKeyPair::Alice(a, _) => a.to_bytes(),
|
||||
SIDHEphemeralKeyPair::Bob(b, _) => b.to_bytes()
|
||||
};
|
||||
let _ = b.write_all(&pk);
|
||||
}
|
||||
|
||||
// FIPS note: any FIPS compliant ciphers must be last or the exchange will not be FIPS compliant. That's
|
||||
// because we chain/ratchet using KHDF and non-FIPS ciphers are considered "salt" inputs for HKDF from a
|
||||
// FIPS point of view. Final key must be HKDF(salt, a FIPS-compliant algorithm secret). There is zero
|
||||
// actual security implication to the order.
|
||||
|
||||
b.push(ALGORITHM_NISTP384ECDH);
|
||||
let _ = varint::write(&mut b, P384_PUBLIC_KEY_SIZE as u64);
|
||||
let _ = b.write_all(self.p384.public_key_bytes());
|
||||
|
||||
b
|
||||
}
|
||||
|
||||
/// Perform ephemeral key agreement.
|
||||
///
|
||||
/// Since ephemeral secrets should only be used once, this consumes the object.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn agree(self, time_ticks: i64, mut other_public_bytes: &[u8]) -> Option<EphemeralSymmetricSecret> {
|
||||
let mut algorithms_successful: u8 = 0;
|
||||
let mut fips_compliant_exchange = false;
|
||||
let mut key: Secret<64> = self.next_key.clone();
|
||||
|
||||
while !other_public_bytes.is_empty() {
|
||||
let algorithms = other_public_bytes[0];
|
||||
other_public_bytes = &other_public_bytes[1..];
|
||||
let key_len = varint::read(&mut other_public_bytes);
|
||||
if key_len.is_err() {
|
||||
return None;
|
||||
}
|
||||
let key_len = key_len.unwrap().0 as usize;
|
||||
|
||||
if algorithms == ALGORITHM_C25519 {
|
||||
if other_public_bytes.len() < C25519_PUBLIC_KEY_SIZE || key_len != C25519_PUBLIC_KEY_SIZE {
|
||||
return None;
|
||||
}
|
||||
|
||||
let c25519_secret = self.c25519.agree(&other_public_bytes[0..C25519_PUBLIC_KEY_SIZE]);
|
||||
other_public_bytes = &other_public_bytes[C25519_PUBLIC_KEY_SIZE..];
|
||||
|
||||
key.0 = hmac_sha512(&key.0, &c25519_secret.0);
|
||||
algorithms_successful |= ALGORITHM_C25519;
|
||||
fips_compliant_exchange = false;
|
||||
} else if algorithms == ALGORITHM_SIDHP751 {
|
||||
if other_public_bytes.len() < (SIDH_P751_PUBLIC_KEY_SIZE + 1) || key_len != (SIDH_P751_PUBLIC_KEY_SIZE + 1) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let _ = match self.sidhp751.as_ref() {
|
||||
Some(SIDHEphemeralKeyPair::Alice(_, seck)) => {
|
||||
if other_public_bytes[0] != 0 { // Alice can't agree with Alice
|
||||
Some(Secret(seck.shared_secret(&SIDHPublicKeyBob::from_bytes(&other_public_bytes[1..(SIDH_P751_PUBLIC_KEY_SIZE + 1)]))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Some(SIDHEphemeralKeyPair::Bob(_, seck)) => {
|
||||
if other_public_bytes[0] != 1 { // Bob can't agree with Bob
|
||||
Some(Secret(seck.shared_secret(&SIDHPublicKeyAlice::from_bytes(&other_public_bytes[1..(SIDH_P751_PUBLIC_KEY_SIZE + 1)]))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
}.map(|sidh_secret| {
|
||||
key.0 = hmac_sha512(&key.0, &sidh_secret.0);
|
||||
algorithms_successful |= ALGORITHM_SIDHP751;
|
||||
fips_compliant_exchange = false;
|
||||
});
|
||||
other_public_bytes = &other_public_bytes[(SIDH_P751_PUBLIC_KEY_SIZE + 1)..];
|
||||
} else if algorithms == ALGORITHM_NISTP384ECDH {
|
||||
if other_public_bytes.len() < P384_PUBLIC_KEY_SIZE || key_len != P384_PUBLIC_KEY_SIZE {
|
||||
return None;
|
||||
}
|
||||
|
||||
let p384_public = P384PublicKey::from_bytes(&other_public_bytes[0..P384_PUBLIC_KEY_SIZE]);
|
||||
other_public_bytes = &other_public_bytes[P384_PUBLIC_KEY_SIZE..];
|
||||
if p384_public.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let _ = self.p384.agree(p384_public.as_ref().unwrap()).map(|p384_key| {
|
||||
key.0 = hmac_sha512(&key.0, &p384_key.0);
|
||||
algorithms_successful |= ALGORITHM_NISTP384ECDH;
|
||||
fips_compliant_exchange = true;
|
||||
});
|
||||
} else {
|
||||
if other_public_bytes.len() < key_len {
|
||||
return None;
|
||||
}
|
||||
other_public_bytes = &other_public_bytes[key_len..];
|
||||
}
|
||||
}
|
||||
|
||||
return if algorithms_successful != 0 {
|
||||
Some(EphemeralSymmetricSecret {
|
||||
secret: SymmetricSecret::new(key),
|
||||
rekey_time: time_ticks + EPHEMERAL_SECRET_REKEY_AFTER_TIME,
|
||||
expire_time: time_ticks + EPHEMERAL_SECRET_REJECT_AFTER_TIME,
|
||||
encrypt_uses: AtomicU32::new(0),
|
||||
decrypt_uses: AtomicU32::new(0),
|
||||
algorithms: algorithms_successful,
|
||||
cumulative_algorithms: algorithms_successful | self.previous_cumulative_algorithms,
|
||||
fips_compliant_exchange
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// An ephemeral symmetric secret with usage timers and counters.
|
||||
pub(crate) struct EphemeralSymmetricSecret {
|
||||
/// Current ephemeral secret key.
|
||||
pub secret: SymmetricSecret,
|
||||
/// Time at or after which we should start trying to re-key.
|
||||
pub rekey_time: i64,
|
||||
/// Time after which this key is no longer valid.
|
||||
pub expire_time: i64,
|
||||
/// Number of times this secret has been used to encrypt.
|
||||
pub encrypt_uses: AtomicU32,
|
||||
/// Number of times this secret has been used to decrypt.
|
||||
pub decrypt_uses: AtomicU32,
|
||||
/// Algorithms used in this exchange (bit mask).
|
||||
pub algorithms: u8,
|
||||
/// Cumulative algorithm mask including previous exchange algorithms.
|
||||
pub cumulative_algorithms: u8,
|
||||
/// True if most recent key exchange was NIST/FIPS compliant.
|
||||
pub fips_compliant_exchange: bool,
|
||||
}
|
||||
|
||||
impl EphemeralSymmetricSecret {
|
||||
#[inline(always)]
|
||||
pub fn should_rekey(&self, time_ticks: i64) -> bool {
|
||||
time_ticks >= self.rekey_time || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= EPHEMERAL_SECRET_REKEY_AFTER_USES
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn expired(&self, time_ticks: i64) -> bool {
|
||||
time_ticks >= self.expire_time || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= EPHEMERAL_SECRET_REJECT_AFTER_USES
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal container for SIDH key pairs tracking whether this is the "alice" or "bob" side in the SIDH protocol.
|
||||
enum SIDHEphemeralKeyPair {
|
||||
Alice(SIDHPublicKeyAlice, SIDHSecretKeyAlice),
|
||||
Bob(SIDHPublicKeyBob, SIDHSecretKeyBob)
|
||||
}
|
||||
|
||||
impl SIDHEphemeralKeyPair {
|
||||
/// Generate a SIDH key pair.
|
||||
///
|
||||
/// SIDH is weird. A key exchange must involve one participant taking a role
|
||||
/// canonically called Alice and the other wearing the Bob hat, because math.
|
||||
///
|
||||
/// If our local address is less than the remote address, we take the Alice role.
|
||||
/// Otherwise if it's greater or equal we take the Bob role.
|
||||
///
|
||||
/// Everything works as long as the two sides take opposite roles. There is no
|
||||
/// security implication in one side always taking one role.
|
||||
pub fn generate(local_address: Address, remote_address: Address) -> SIDHEphemeralKeyPair {
|
||||
let mut rng = SecureRandom::get();
|
||||
if local_address < remote_address {
|
||||
let (p, s) = zerotier_core_crypto::sidhp751::generate_alice_keypair(&mut rng);
|
||||
SIDHEphemeralKeyPair::Alice(p, s)
|
||||
} else {
|
||||
let (p, s) = zerotier_core_crypto::sidhp751::generate_bob_keypair(&mut rng);
|
||||
SIDHEphemeralKeyPair::Bob(p, s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns 0 if Alice, 1 if Bob.
|
||||
#[inline(always)]
|
||||
pub fn role(&self) -> u8 {
|
||||
match self {
|
||||
Self::Alice(_, _) => 0,
|
||||
Self::Bob(_, _) => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::vl1::ephemeral::EphemeralKeyPairSet;
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::symmetricsecret::SymmetricSecret;
|
||||
use zerotier_core_crypto::secret::Secret;
|
||||
|
||||
#[test]
|
||||
fn ratchet() {
|
||||
let static_secret = SymmetricSecret::new(Secret([1_u8; 64]));
|
||||
let alice_address = Address::from_u64(0xdeadbeef00).unwrap();
|
||||
let bob_address = Address::from_u64(0xbeefdead00).unwrap();
|
||||
let mut alice = EphemeralKeyPairSet::new(alice_address, bob_address, &static_secret, 0);
|
||||
let mut bob = EphemeralKeyPairSet::new(bob_address, alice_address, &static_secret, 0);
|
||||
let ratchets = 16;
|
||||
let mut alice_cumulative_algorithms: u8 = 0;
|
||||
let mut bob_cumulative_algorithms: u8 = 0;
|
||||
for t in 1..ratchets+1 {
|
||||
let alice_public = alice.public_bytes();
|
||||
let bob_public = bob.public_bytes();
|
||||
let alice_key = alice.agree(t, bob_public.as_slice());
|
||||
let bob_key = bob.agree(t, alice_public.as_slice());
|
||||
assert!(alice_key.is_some());
|
||||
assert!(bob_key.is_some());
|
||||
let alice_key = alice_key.unwrap();
|
||||
let bob_key = bob_key.unwrap();
|
||||
alice_cumulative_algorithms |= alice_key.cumulative_algorithms;
|
||||
bob_cumulative_algorithms |= bob_key.cumulative_algorithms;
|
||||
assert_eq!(&alice_key.secret.key.0, &bob_key.secret.key.0);
|
||||
alice = EphemeralKeyPairSet::new(alice_address, bob_address, &alice_key.secret, alice_key.cumulative_algorithms);
|
||||
bob = EphemeralKeyPairSet::new(bob_address, alice_address, &alice_key.secret, alice_key.cumulative_algorithms);
|
||||
}
|
||||
assert_ne!(alice_cumulative_algorithms, 0);
|
||||
assert_ne!(bob_cumulative_algorithms, 0);
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ pub(crate) mod address;
|
|||
pub(crate) mod mac;
|
||||
pub(crate) mod fragmentedpacket;
|
||||
pub(crate) mod whoisqueue;
|
||||
pub(crate) mod ephemeral;
|
||||
pub(crate) mod symmetricsecret;
|
||||
|
||||
pub use address::Address;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
use std::convert::TryInto;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::num::NonZeroI64;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
|
||||
|
||||
|
@ -26,11 +25,10 @@ use crate::{PacketBuffer, VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_R
|
|||
use crate::util::{array_range, u64_as_bytes};
|
||||
use crate::util::buffer::Buffer;
|
||||
use crate::vl1::{Endpoint, Identity, InetAddress, Path};
|
||||
use crate::vl1::ephemeral::EphemeralSymmetricSecret;
|
||||
use crate::vl1::identity::{IDENTITY_ALGORITHM_ALL, IDENTITY_ALGORITHM_X25519};
|
||||
use crate::vl1::node::*;
|
||||
use crate::vl1::protocol::*;
|
||||
use crate::vl1::symmetricsecret::SymmetricSecret;
|
||||
use crate::vl1::symmetricsecret::{EphemeralSymmetricSecret, SymmetricSecret};
|
||||
|
||||
/// A remote peer known to this node.
|
||||
/// Sending-related and receiving-related fields are locked separately since concurrent
|
||||
|
|
|
@ -39,7 +39,7 @@ pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0: u8 = b'0';
|
|||
pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1: u8 = b'1';
|
||||
|
||||
/// KBKDF usage label for the key used to advance the ratchet.
|
||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_NEXT_KEY: u8 = b'e';
|
||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY: u8 = b'e';
|
||||
|
||||
/// Try to re-key ephemeral keys after this time.
|
||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use zerotier_core_crypto::aes_gmac_siv::AesGmacSiv;
|
||||
use zerotier_core_crypto::kbkdf::*;
|
||||
use zerotier_core_crypto::secret::Secret;
|
||||
|
@ -14,11 +15,11 @@ use crate::util::pool::{Pool, PoolFactory};
|
|||
use crate::vl1::protocol::*;
|
||||
|
||||
/// Pool of reusable AES-GMAC-SIV instances.
|
||||
pub(crate) struct AesGmacSivPoolFactory(Secret<48>, Secret<48>);
|
||||
pub(crate) struct AesGmacSivPoolFactory(Secret<32>, Secret<32>);
|
||||
|
||||
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
||||
#[inline(always)]
|
||||
fn create(&self) -> AesGmacSiv { AesGmacSiv::new(&self.0.0[0..32], &self.1.0[0..32]) }
|
||||
fn create(&self) -> AesGmacSiv { AesGmacSiv::new(&self.0.0, &self.1.0) }
|
||||
|
||||
#[inline(always)]
|
||||
fn reset(&self, obj: &mut AesGmacSiv) { obj.reset(); }
|
||||
|
@ -28,13 +29,9 @@ impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
|||
///
|
||||
/// This contains the key and several sub-keys and ciphers keyed with sub-keys.
|
||||
pub(crate) struct SymmetricSecret {
|
||||
/// The root shared symmetric secret from which other keys are derived.
|
||||
pub key: Secret<64>,
|
||||
|
||||
/// Key for adding an HMAC to packets e.g. in v2+ HELLO.
|
||||
pub packet_hmac_key: Secret<64>,
|
||||
|
||||
/// A pool of reusable keyed and initialized AES-GMAC-SIV ciphers.
|
||||
pub ephemeral_ratchet_key: Secret<64>,
|
||||
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
||||
}
|
||||
|
||||
|
@ -47,15 +44,39 @@ impl Eq for SymmetricSecret {}
|
|||
|
||||
impl SymmetricSecret {
|
||||
/// Create a new symmetric secret, deriving all sub-keys and such.
|
||||
pub fn new(base_key: Secret<64>) -> SymmetricSecret {
|
||||
let usage_packet_hmac = zt_kbkdf_hmac_sha512(&base_key.0, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
|
||||
pub fn new(key: Secret<64>) -> SymmetricSecret {
|
||||
let packet_hmac_key = zt_kbkdf_hmac_sha512(&key.0, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
|
||||
let ephemeral_ratchet_key = zt_kbkdf_hmac_sha512(&key.0, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY, 0, 0);
|
||||
let aes_factory = AesGmacSivPoolFactory(
|
||||
zt_kbkdf_hmac_sha384(&base_key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0),
|
||||
zt_kbkdf_hmac_sha384(&base_key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, 0, 0));
|
||||
zt_kbkdf_hmac_sha384(&key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0).first_n(),
|
||||
zt_kbkdf_hmac_sha384(&key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, 0, 0).first_n());
|
||||
SymmetricSecret {
|
||||
key: base_key,
|
||||
packet_hmac_key: usage_packet_hmac,
|
||||
key,
|
||||
packet_hmac_key,
|
||||
ephemeral_ratchet_key,
|
||||
aes_gmac_siv: Pool::new(2, aes_factory),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An ephemeral symmetric secret with usage timers and counters.
|
||||
pub(crate) struct EphemeralSymmetricSecret {
|
||||
pub secret: SymmetricSecret,
|
||||
pub rekey_time: i64,
|
||||
pub expire_time: i64,
|
||||
pub encrypt_uses: AtomicU32,
|
||||
pub decrypt_uses: AtomicU32,
|
||||
pub fips_compliant_exchange: bool,
|
||||
}
|
||||
|
||||
impl EphemeralSymmetricSecret {
|
||||
#[inline(always)]
|
||||
pub fn should_rekey(&self, time_ticks: i64) -> bool {
|
||||
time_ticks >= self.rekey_time || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= EPHEMERAL_SECRET_REKEY_AFTER_USES
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn expired(&self, time_ticks: i64) -> bool {
|
||||
time_ticks >= self.expire_time || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= EPHEMERAL_SECRET_REJECT_AFTER_USES
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue