mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-03 19:13:43 +02:00
Build fixes, ephemeral keying logic and ratchet logic, split out symmetric key since it is a non-trivial type.
This commit is contained in:
parent
184c4aede1
commit
06bd77946b
11 changed files with 267 additions and 41 deletions
|
@ -25,10 +25,10 @@ impl SHA512 {
|
|||
|
||||
pub fn hmac(key: &[u8], msg: &[u8]) -> [u8; SHA512_HASH_SIZE] {
|
||||
let mut m = gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::HmacSha512).unwrap();
|
||||
let _ = m.set_key(key);
|
||||
let _ = m.update(msg);
|
||||
m.set_key(key).expect("FATAL: invalid HMAC-SHA512 key");;
|
||||
m.update(msg).expect("FATAL: HMAC-SHA512 failed");
|
||||
let mut h = [0_u8; SHA512_HASH_SIZE];
|
||||
let _ = m.get_mac(&mut h);
|
||||
m.get_mac(&mut h).expect("FATAL: HMAC-SHA512 failed");
|
||||
h
|
||||
}
|
||||
|
||||
|
@ -77,10 +77,10 @@ impl SHA384 {
|
|||
|
||||
pub fn hmac(key: &[u8], msg: &[u8]) -> [u8; SHA384_HASH_SIZE] {
|
||||
let mut m = gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::HmacSha384).unwrap();
|
||||
let _ = m.set_key(key);
|
||||
let _ = m.update(msg);
|
||||
m.set_key(key).expect("FATAL: invalid HMAC-SHA384 key");
|
||||
m.update(msg).expect("FATAL: HMAC-SHA384 failed");
|
||||
let mut h = [0_u8; SHA384_HASH_SIZE];
|
||||
let _ = m.get_mac(&mut h);
|
||||
m.get_mac(&mut h).expect("FATAL: HMAC-SHA384 failed");
|
||||
h
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@ use rand_core::{RngCore, CryptoRng};
|
|||
use quickcheck::{Arbitrary, Gen, QuickCheck};
|
||||
|
||||
/// The secret key size, in bytes.
|
||||
pub const SECRET_KEY_SIZE: usize = 48;
|
||||
pub const SIDH_P751_SECRET_KEY_SIZE: usize = 48;
|
||||
/// The public key size, in bytes.
|
||||
pub const PUBLIC_KEY_SIZE: usize = 564;
|
||||
pub const SIDH_P751_PUBLIC_KEY_SIZE: usize = 564;
|
||||
/// The shared secret size, in bytes.
|
||||
pub const SHARED_SECRET_SIZE: usize = 188;
|
||||
pub const SIDH_P751_SHARED_SECRET_SIZE: usize = 188;
|
||||
|
||||
const MAX_INT_POINTS_ALICE: usize = 8;
|
||||
const MAX_INT_POINTS_BOB: usize = 10;
|
||||
|
@ -125,7 +125,7 @@ impl SIDHPublicKeyBob {
|
|||
/// Alice's secret key.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SIDHSecretKeyAlice {
|
||||
pub scalar: [u8; SECRET_KEY_SIZE],
|
||||
pub scalar: [u8; SIDH_P751_SECRET_KEY_SIZE],
|
||||
}
|
||||
|
||||
impl Debug for SIDHSecretKeyAlice {
|
||||
|
@ -206,7 +206,7 @@ impl SIDHSecretKeyAlice {
|
|||
|
||||
/// Compute (Alice's view of) a shared secret using Alice's secret key and Bob's public key.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn shared_secret(&self, bob_public: &SIDHPublicKeyBob) -> [u8; SHARED_SECRET_SIZE] {
|
||||
pub fn shared_secret(&self, bob_public: &SIDHPublicKeyBob) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] {
|
||||
let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP);
|
||||
let xP = ProjectivePoint::from_affine(&bob_public.affine_xP);
|
||||
let xQ = ProjectivePoint::from_affine(&bob_public.affine_xQ);
|
||||
|
@ -250,7 +250,7 @@ impl SIDHSecretKeyAlice {
|
|||
/// Bob's secret key.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SIDHSecretKeyBob {
|
||||
pub scalar: [u8; SECRET_KEY_SIZE],
|
||||
pub scalar: [u8; SIDH_P751_SECRET_KEY_SIZE],
|
||||
}
|
||||
|
||||
impl Debug for SIDHSecretKeyBob {
|
||||
|
@ -325,7 +325,7 @@ impl SIDHSecretKeyBob {
|
|||
|
||||
/// Compute (Bob's view of) a shared secret using Bob's secret key and Alice's public key.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn shared_secret(&self, alice_public: &SIDHPublicKeyAlice) -> [u8; SHARED_SECRET_SIZE] {
|
||||
pub fn shared_secret(&self, alice_public: &SIDHPublicKeyAlice) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] {
|
||||
let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP);
|
||||
let xP = ProjectivePoint::from_affine(&alice_public.affine_xP);
|
||||
let xQ = ProjectivePoint::from_affine(&alice_public.affine_xQ);
|
||||
|
@ -367,7 +367,7 @@ impl SIDHSecretKeyBob {
|
|||
/// implement SIDH validation, each keypair should be used for at most one
|
||||
/// shared secret computation.
|
||||
pub fn generate_alice_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> (SIDHPublicKeyAlice, SIDHSecretKeyAlice) {
|
||||
let mut scalar = [0u8; SECRET_KEY_SIZE];
|
||||
let mut scalar = [0u8; SIDH_P751_SECRET_KEY_SIZE];
|
||||
rng.fill_bytes(&mut scalar[..]);
|
||||
|
||||
// Bit-twiddle to ensure scalar is in 2*[0,2^371):
|
||||
|
@ -388,7 +388,7 @@ pub fn generate_alice_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> (SIDHPubli
|
|||
/// implement SIDH validation, each keypair should be used for at most one
|
||||
/// shared secret computation.
|
||||
pub fn generate_bob_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> (SIDHPublicKeyBob, SIDHSecretKeyBob) {
|
||||
let mut scalar = [0u8; SECRET_KEY_SIZE];
|
||||
let mut scalar = [0u8; SIDH_P751_SECRET_KEY_SIZE];
|
||||
// Perform rejection sampling to obtain a random value in [0,3^238]:
|
||||
let mut ok: u32 = 1;
|
||||
for _ in 0..102 {
|
||||
|
@ -501,7 +501,7 @@ mod test {
|
|||
//
|
||||
// This function just exists to ensure that the fast isogeny-tree strategy works correctly.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn alice_shared_secret_slow(bob_public: &SIDHPublicKeyBob, alice_secret: &SIDHSecretKeyAlice) -> [u8; SHARED_SECRET_SIZE] {
|
||||
pub fn alice_shared_secret_slow(bob_public: &SIDHPublicKeyBob, alice_secret: &SIDHSecretKeyAlice) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] {
|
||||
let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP);
|
||||
let xP = ProjectivePoint::from_affine(&bob_public.affine_xP);
|
||||
let xQ = ProjectivePoint::from_affine(&bob_public.affine_xQ);
|
||||
|
@ -532,7 +532,7 @@ mod test {
|
|||
//
|
||||
// This function just exists to ensure that the fast isogeny-tree strategy works correctly.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn bob_shared_secret_slow(alice_public: &SIDHPublicKeyAlice, bob_secret: &SIDHSecretKeyBob) -> [u8; SHARED_SECRET_SIZE] {
|
||||
pub fn bob_shared_secret_slow(alice_public: &SIDHPublicKeyAlice, bob_secret: &SIDHSecretKeyBob) -> [u8; SIDH_P751_SHARED_SECRET_SIZE] {
|
||||
let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP);
|
||||
let xP = ProjectivePoint::from_affine(&alice_public.affine_xP);
|
||||
let xQ = ProjectivePoint::from_affine(&alice_public.affine_xQ);
|
||||
|
|
|
@ -15,13 +15,13 @@ pub mod defaults;
|
|||
mod node;
|
||||
|
||||
/// Standard packet buffer type including pool container.
|
||||
pub type PacketBuffer = crate::util::pool::Pooled<Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, crate::vl1::buffer::PooledBufferFactory<{ crate::vl1::protocol::PACKET_SIZE_MAX }>>;
|
||||
pub type PacketBuffer = crate::util::pool::Pooled<crate::vl1::buffer::Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, crate::PacketBufferFactory>;
|
||||
|
||||
/// Factory type to supply to a new PacketBufferPool.
|
||||
pub type PacketBufferFactory = crate::vl1::buffer::PooledBufferFactory<{ crate::vl1::protocol::PACKET_SIZE_MAX }>;
|
||||
|
||||
/// Source for instances of PacketBuffer
|
||||
pub type PacketBufferPool = crate::util::pool::Pool<Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, crate::vl1::buffer::PacketBufferFactory>;
|
||||
pub type PacketBufferPool = crate::util::pool::Pool<crate::vl1::buffer::Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, crate::PacketBufferFactory>;
|
||||
|
||||
pub use node::{CallerInterface, Node};
|
||||
|
||||
|
|
|
@ -7,22 +7,21 @@
|
|||
*/
|
||||
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::io::Write;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use zerotier_core_crypto::c25519::C25519KeyPair;
|
||||
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
|
||||
use zerotier_core_crypto::p521::P521KeyPair;
|
||||
use zerotier_core_crypto::c25519::{C25519KeyPair, C25519_PUBLIC_KEY_SIZE};
|
||||
use zerotier_core_crypto::hash::{SHA384_HASH_SIZE, SHA384};
|
||||
use zerotier_core_crypto::p521::{P521KeyPair, P521_PUBLIC_KEY_SIZE, P521PublicKey};
|
||||
use zerotier_core_crypto::random::SecureRandom;
|
||||
use zerotier_core_crypto::secret::Secret;
|
||||
use zerotier_core_crypto::sidhp751::{SIDHPublicKeyAlice, SIDHPublicKeyBob, SIDHSecretKeyAlice, SIDHSecretKeyBob};
|
||||
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::EPHEMERAL_SECRET_SIDH_TTL;
|
||||
use crate::vl1::protocol::EphemeralKeyAgreementAlgorithm;
|
||||
use crate::vl1::symmetricsecret::SymmetricSecret;
|
||||
|
||||
const EPHEMERAL_CIPHER_C25519: u8 = 1;
|
||||
const EPHEMERAL_CIPHER_P521: u8 = 2;
|
||||
const EPHEMERAL_CIPHER_SIDH_P751: u8 = 3;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum SIDHSecretKey {
|
||||
Alice(SIDHPublicKeyAlice, SIDHSecretKeyAlice),
|
||||
|
@ -43,13 +42,22 @@ impl SIDHSecretKey {
|
|||
pub fn generate(local_address: Address, remote_address: Address) -> SIDHSecretKey {
|
||||
let mut rng = SecureRandom::get();
|
||||
if local_address < remote_address {
|
||||
let (p, s) = zerotier_core_crypto::sidh::sidh::generate_alice_keypair(&mut rng);
|
||||
let (p, s) = zerotier_core_crypto::sidhp751::generate_alice_keypair(&mut rng);
|
||||
SIDHSecretKey::Alice(p, s)
|
||||
} else {
|
||||
let (p, s) = zerotier_core_crypto::sidh::sidh::generate_bob_keypair(&mut rng);
|
||||
let (p, s) = zerotier_core_crypto::sidhp751::generate_bob_keypair(&mut rng);
|
||||
SIDHSecretKey::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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An ephemeral secret key negotiated to implement forward secrecy.
|
||||
|
@ -57,7 +65,7 @@ pub struct EphemeralSecret {
|
|||
timestamp_ticks: i64,
|
||||
c25519: C25519KeyPair,
|
||||
p521: P521KeyPair,
|
||||
sidhp751: SIDHSecretKey,
|
||||
sidhp751: Option<SIDHSecretKey>,
|
||||
}
|
||||
|
||||
impl EphemeralSecret {
|
||||
|
@ -70,19 +78,194 @@ impl EphemeralSecret {
|
|||
timestamp_ticks: time_ticks,
|
||||
c25519: C25519KeyPair::generate(true),
|
||||
p521: P521KeyPair::generate(true).expect("NIST P-521 key pair generation failed"),
|
||||
sidhp751: SIDHSecretKey::generate(local_address, remote_address),
|
||||
sidhp751: Some(SIDHSecretKey::generate(local_address, remote_address)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a public version of this ephemeral secret to share with our counterparty.
|
||||
pub fn public(&self) -> Vec<u8> {
|
||||
todo!()
|
||||
pub fn public_bytes(&self) -> Vec<u8> {
|
||||
let mut b: Vec<u8> = Vec::with_capacity(8 + C25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + SIDH_P751_PUBLIC_KEY_SIZE);
|
||||
|
||||
b.push(EphemeralKeyAgreementAlgorithm::C25519 as u8);
|
||||
let _ = varint::write(&mut b, C25519_PUBLIC_KEY_SIZE as u64);
|
||||
let _ = b.write_all(&self.c25519.public_bytes());
|
||||
|
||||
let _ = self.sidhp751.map(|sidhp751| {
|
||||
b.push(EphemeralKeyAgreementAlgorithm::SIDHP751 as u8);
|
||||
let _ = varint::write(&mut b, (SIDH_P751_PUBLIC_KEY_SIZE + 1) as u64);
|
||||
b.push(sidhp751.role());
|
||||
let pk = match &sidhp751 {
|
||||
SIDHSecretKey::Alice(a, _) => a.to_bytes(),
|
||||
SIDHSecretKey::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, FIPS-compliant algorithm secret). Order has no actual
|
||||
// implication for security.
|
||||
|
||||
b.push(EphemeralKeyAgreementAlgorithm::NistP521ECDH as u8);
|
||||
let _ = varint::write(&mut b, P521_PUBLIC_KEY_SIZE as u64);
|
||||
let _ = b.write_all(self.p521.public_key_bytes());
|
||||
|
||||
b
|
||||
}
|
||||
|
||||
/// Perform ephemeral key agreement.
|
||||
///
|
||||
/// None is returned if the public key data is malformed, no algorithms overlap, etc.
|
||||
///
|
||||
/// Input is the previous session key. The long-lived identity key exchange key starts
|
||||
/// the ratchet sequence, or rather a key derived from it for this purpose.
|
||||
///
|
||||
/// Since ephemeral secrets should only be used once, this consumes the object.
|
||||
pub fn agree(self, time_ticks: i64, static_secret: &SymmetricSecret, previous_ephemeral_secret: Option<&EphemeralSymmetricSecret>, other_public_bytes: &[u8]) -> Option<EphemeralSymmetricSecret> {
|
||||
let (ratchet_count, mut key) = previous_ephemeral_secret.map_or_else(|| {
|
||||
(0_u64, static_secret.next_ephemeral_ratchet_key.clone())
|
||||
}, |previous_ephemeral_secret| {
|
||||
(previous_ephemeral_secret.ratchet_count + 1, Secret(SHA384::hmac(&static_secret.next_ephemeral_ratchet_key.0, &previous_ephemeral_secret.secret.next_ephemeral_ratchet_key.0)))
|
||||
});
|
||||
|
||||
let mut algs: Vec<EphemeralKeyAgreementAlgorithm> = Vec::with_capacity(3);
|
||||
let mut other_public_bytes = other_public_bytes;
|
||||
while !other_public_bytes.is_empty() {
|
||||
let cipher = 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;
|
||||
|
||||
match cipher.try_into() {
|
||||
|
||||
Ok(EphemeralKeyAgreementAlgorithm::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 = SHA384::hmac(&key.0, &c25519_secret.0);
|
||||
algs.push(EphemeralKeyAgreementAlgorithm::C25519);
|
||||
},
|
||||
|
||||
Ok(EphemeralKeyAgreementAlgorithm::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(SIDHSecretKey::Alice(_, seck)) => {
|
||||
if other_public_bytes[0] != 0 { // Alice can't agree with Alice
|
||||
None
|
||||
} else {
|
||||
Some(Secret(seck.shared_secret(&SIDHPublicKeyBob::from_bytes(&other_public_bytes[1..(SIDH_P751_PUBLIC_KEY_SIZE + 1)]))))
|
||||
}
|
||||
},
|
||||
Some(SIDHSecretKey::Bob(_, seck)) => {
|
||||
if other_public_bytes[0] != 1 { // Bob can't agree with Bob
|
||||
None
|
||||
} else {
|
||||
Some(Secret(seck.shared_secret(&SIDHPublicKeyAlice::from_bytes(&other_public_bytes[1..(SIDH_P751_PUBLIC_KEY_SIZE + 1)]))))
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
}.map(|sidh_secret| {
|
||||
key.0 = SHA384::hmac(&key.0, &sidh_secret.0);
|
||||
algs.push(EphemeralKeyAgreementAlgorithm::SIDHP751);
|
||||
});
|
||||
other_public_bytes = &other_public_bytes[(SIDH_P751_PUBLIC_KEY_SIZE + 1)..];
|
||||
},
|
||||
|
||||
Ok(EphemeralKeyAgreementAlgorithm::NistP521ECDH) => {
|
||||
if other_public_bytes.len() < P521_PUBLIC_KEY_SIZE || key_len != P521_PUBLIC_KEY_SIZE {
|
||||
return None;
|
||||
}
|
||||
let p521_public = P521PublicKey::from_bytes(&other_public_bytes[0..P521_PUBLIC_KEY_SIZE]);
|
||||
other_public_bytes = &other_public_bytes[P521_PUBLIC_KEY_SIZE..];
|
||||
if p521_public.is_none() {
|
||||
return None;
|
||||
}
|
||||
let p521_key = self.p521.agree(p521_public.as_ref().unwrap());
|
||||
if p521_key.is_none() {
|
||||
return None;
|
||||
}
|
||||
key.0 = SHA384::hmac(&key.0, &p521_key.unwrap().0);
|
||||
algs.push(EphemeralKeyAgreementAlgorithm::NistP521ECDH);
|
||||
},
|
||||
|
||||
Err(_) => {
|
||||
if other_public_bytes.len() < key_len {
|
||||
return None;
|
||||
}
|
||||
other_public_bytes = &other_public_bytes[key_len..];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return if !algs.is_empty() {
|
||||
Some(EphemeralSymmetricSecret {
|
||||
secret: SymmetricSecret::new(key),
|
||||
agreement_algorithms: algs,
|
||||
agreement_timestamp_ticks: time_ticks,
|
||||
local_secret_timestamp_ticks: self.timestamp_ticks,
|
||||
ratchet_count,
|
||||
encrypt_uses: AtomicU32::new(0),
|
||||
decrypt_uses: AtomicU32::new(0)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EphemeralSymmetricSecret {
|
||||
secret: SymmetricSecret,
|
||||
agreement_algorithms: Vec<EphemeralKeyAgreementAlgorithm>,
|
||||
agreement_timestamp_ticks: i64,
|
||||
local_secret_timestamp_ticks: i64,
|
||||
ratchet_count: u64,
|
||||
encrypt_uses: AtomicU32,
|
||||
decrypt_uses: AtomicU32,
|
||||
}
|
||||
|
||||
impl EphemeralSymmetricSecret {
|
||||
#[inline(always)]
|
||||
pub fn use_secret_to_encrypt(&self) -> &SymmetricSecret {
|
||||
let _ = self.encrypt_uses.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
&self.secret
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn use_secret_to_decrypt(&self) -> &SymmetricSecret {
|
||||
let _ = self.decrypt_uses.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
&self.secret
|
||||
}
|
||||
|
||||
pub fn is_fips_compliant(&self) -> bool {
|
||||
self.agreement_algorithms.last().map_or(false, |alg| alg.is_fips_compliant())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::vl1::ephemeral::EphemeralSecret;
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::symmetricsecret::SymmetricSecret;
|
||||
use zerotier_core_crypto::secret::Secret;
|
||||
|
||||
#[test]
|
||||
fn ephemeral_agreement() {
|
||||
let static_secret = SymmetricSecret::new(Secret([1_u8; 48]));
|
||||
let alice = EphemeralSecret::new(1, Address::from_u64(0xdeadbeef00).unwrap(), Address::from_u64(0xbeefdead00).unwrap());
|
||||
let bob = EphemeralSecret::new(1, Address::from_u64(0xbeefdead00).unwrap(), Address::from_u64(0xdeadbeef00).unwrap());
|
||||
let alice_public_bytes = alice.public_bytes();
|
||||
let bob_public_bytes = bob.public_bytes();
|
||||
let alice_key = alice.agree(2, &static_secret, None, bob_public_bytes.as_slice()).unwrap();
|
||||
let bob_key = bob.agree(2, &static_secret, None, alice_public_bytes.as_slice()).unwrap();
|
||||
assert_eq!(&alice_key.secret.key.0, &bob_key.secret.key.0);
|
||||
//println!("ephemeral_agreement secret: {}", zerotier_core_crypto::hex::to_string(&alice_key.secret.key.0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use crate::vl1::vl1node::PacketBuffer;
|
||||
use crate::PacketBuffer;
|
||||
use crate::vl1::protocol::*;
|
||||
|
||||
/// Packet fragment re-assembler and container.
|
||||
|
|
|
@ -33,4 +33,4 @@ pub use dictionary::Dictionary;
|
|||
pub use inetaddress::InetAddress;
|
||||
pub use peer::Peer;
|
||||
pub use path::Path;
|
||||
pub use vl1node::{PacketBuffer, PacketBufferPool, PacketBufferFactory, VL1CallerInterface};
|
||||
pub use vl1node::{VL1Node, VL1CallerInterface};
|
||||
|
|
|
@ -14,8 +14,9 @@ use parking_lot::Mutex;
|
|||
use crate::util::U64PassThroughHasher;
|
||||
use crate::vl1::Endpoint;
|
||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||
use crate::vl1::vl1node::{PacketBuffer, VL1CallerInterface};
|
||||
use crate::vl1::vl1node::VL1CallerInterface;
|
||||
use crate::vl1::protocol::*;
|
||||
use crate::PacketBuffer;
|
||||
|
||||
/// Keepalive interval for paths in milliseconds.
|
||||
pub(crate) const PATH_KEEPALIVE_INTERVAL: i64 = 20000;
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::mem::MaybeUninit;
|
|||
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::buffer::{RawObject, Buffer};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub const VERB_VL1_NOP: u8 = 0x00;
|
||||
pub const VERB_VL1_HELLO: u8 = 0x01;
|
||||
|
@ -47,7 +48,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 acknowledgement of a shared secret.
|
||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_ACK: u8 = b'A';
|
||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET: u8 = b'E';
|
||||
|
||||
/// Try to re-key ephemeral keys after this time.
|
||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 1000 * 60 * 60; // 1 hour
|
||||
|
@ -176,6 +177,37 @@ pub const WHOIS_RETRY_MAX: u16 = 3;
|
|||
/// Maximum number of packets to queue up behind a WHOIS.
|
||||
pub const WHOIS_MAX_WAITING_PACKETS: usize = 64;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum EphemeralKeyAgreementAlgorithm {
|
||||
C25519 = 1,
|
||||
SIDHP751 = 2,
|
||||
NistP521ECDH = 3
|
||||
}
|
||||
|
||||
impl EphemeralKeyAgreementAlgorithm {
|
||||
pub fn is_fips_compliant(&self) -> bool {
|
||||
match self {
|
||||
Self::NistP521ECDH => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for EphemeralKeyAgreementAlgorithm {
|
||||
type Error = ();
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
1 => Ok(Self::C25519),
|
||||
2 => Ok(Self::SIDHP751),
|
||||
3 => Ok(Self::NistP521ECDH),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compress a packet and return true if compressed.
|
||||
/// The 'dest' buffer must be empty (will panic otherwise). A return value of false indicates an error or
|
||||
/// that the data was not compressible. The state of the destination buffer is undefined on a return
|
||||
|
|
|
@ -14,7 +14,7 @@ use zerotier_core_crypto::kbkdf::zt_kbkdf_hmac_sha384;
|
|||
use zerotier_core_crypto::secret::Secret;
|
||||
|
||||
use crate::util::pool::{Pool, PoolFactory};
|
||||
use crate::vl1::protocol::{KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, KBKDF_KEY_USAGE_LABEL_HELLO_DICTIONARY_ENCRYPT, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC};
|
||||
use crate::vl1::protocol::{KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, KBKDF_KEY_USAGE_LABEL_HELLO_DICTIONARY_ENCRYPT, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET};
|
||||
|
||||
pub struct AesGmacSivPoolFactory(Secret<SHA384_HASH_SIZE>, Secret<SHA384_HASH_SIZE>);
|
||||
|
||||
|
@ -32,13 +32,21 @@ impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
|||
pub struct SymmetricSecret {
|
||||
pub key: Secret<SHA384_HASH_SIZE>,
|
||||
pub packet_hmac_key: Secret<SHA384_HASH_SIZE>,
|
||||
pub next_ephemeral_ratchet_key: Secret<SHA384_HASH_SIZE>,
|
||||
pub hello_dictionary_keyed_cipher: Mutex<AesCtr>,
|
||||
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
||||
}
|
||||
|
||||
impl PartialEq for SymmetricSecret {
|
||||
fn eq(&self, other: &Self) -> bool { self.key.0.eq(&other.key.0) }
|
||||
}
|
||||
|
||||
impl Eq for SymmetricSecret {}
|
||||
|
||||
impl SymmetricSecret {
|
||||
pub fn new(base_key: Secret<SHA384_HASH_SIZE>) -> SymmetricSecret {
|
||||
let usage_packet_hmac = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
|
||||
let usage_ephemeral_ratchet = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET, 0, 0);
|
||||
let usage_hello_dictionary_key = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_HELLO_DICTIONARY_ENCRYPT, 0, 0);
|
||||
let aes_factory = AesGmacSivPoolFactory(
|
||||
zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0),
|
||||
|
@ -46,6 +54,7 @@ impl SymmetricSecret {
|
|||
SymmetricSecret {
|
||||
key: base_key,
|
||||
packet_hmac_key: usage_packet_hmac,
|
||||
next_ephemeral_ratchet_key: usage_ephemeral_ratchet,
|
||||
hello_dictionary_keyed_cipher: Mutex::new(AesCtr::new(&usage_hello_dictionary_key.0[0..32])),
|
||||
aes_gmac_siv: Pool::new(2, aes_factory),
|
||||
}
|
||||
|
|
|
@ -13,8 +13,9 @@ use parking_lot::Mutex;
|
|||
use crate::util::gate::IntervalGate;
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||
use crate::vl1::vl1node::{VL1Node, PacketBuffer, VL1CallerInterface};
|
||||
use crate::vl1::vl1node::{VL1Node, VL1CallerInterface};
|
||||
use crate::vl1::protocol::{WHOIS_RETRY_INTERVAL, WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_MAX};
|
||||
use crate::PacketBuffer;
|
||||
|
||||
pub(crate) enum QueuedPacket {
|
||||
Unfragmented(PacketBuffer),
|
||||
|
|
|
@ -32,6 +32,6 @@ impl VL1PacketHandler for Switch {
|
|||
|
||||
impl Switch {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
Self{}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue