mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
More cleanup and one more tweak to ephemeral keys. Add a ratchet counter to prevent replay attacks.
This commit is contained in:
parent
c39f38d818
commit
07cfd12620
5 changed files with 136 additions and 103 deletions
|
@ -9,6 +9,10 @@
|
|||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
pub const VERSION_MAJOR: u8 = 1;
|
||||
pub const VERSION_MINOR: u8 = 99;
|
||||
pub const VERSION_REVISION: u8 = 1;
|
||||
|
||||
pub mod util;
|
||||
pub mod error;
|
||||
pub mod vl1;
|
||||
|
@ -26,11 +30,6 @@ pub type PacketBufferFactory = crate::util::buffer::PooledBufferFactory<{ crate:
|
|||
/// Source for instances of PacketBuffer
|
||||
pub type PacketBufferPool = crate::util::pool::Pool<crate::util::buffer::Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, crate::PacketBufferFactory>;
|
||||
|
||||
pub const VERSION_MAJOR: u8 = 1;
|
||||
pub const VERSION_MINOR: u8 = 99;
|
||||
pub const VERSION_REVISION: u8 = 1;
|
||||
pub const VERSION_STR: &'static str = "1.99.1";
|
||||
|
||||
/*
|
||||
* Protocol versions
|
||||
*
|
||||
|
@ -60,13 +59,13 @@ pub const VERSION_STR: &'static str = "1.99.1";
|
|||
* 10 - 1.4.0 ... 1.4.6
|
||||
* + Contained early pre-alpha versions of multipath, which are deprecated
|
||||
* 11 - 1.6.0 ... 2.0.0
|
||||
* + Supports AES-GMAC-SIV symmetric crypto, backported from v2 tree.
|
||||
* + Supports and prefers AES-GMAC-SIV symmetric crypto, backported.
|
||||
*
|
||||
* 20 - 2.0.0 ... CURRENT
|
||||
* + Forward secrecy with cryptographic ratchet! Finally!!!
|
||||
* + New identity format including both x25519 and NIST P-521 keys.
|
||||
* + AES-GMAC-SIV, a FIPS-compliant SIV construction using AES.
|
||||
* + HELLO and OK(HELLO) include an extra HMAC to harden authentication
|
||||
* + HELLO and OK(HELLO) carry meta-data in a dictionary that's encrypted
|
||||
* + Forward secrecy, key lifetime management
|
||||
* + Old planet/moon stuff is DEAD! Independent roots are easier.
|
||||
* + AES encryption with the SIV construction AES-GMAC-SIV
|
||||
* + New combined Curve25519/NIST P-384 identity
|
||||
* + HELLO and OK(HELLO) use a dictionary for better extensibilit.
|
||||
*/
|
||||
pub const VERSION_PROTO: u8 = 20;
|
||||
|
|
|
@ -27,7 +27,7 @@ pub const TYPE_HTTP: u8 = 8;
|
|||
pub const TYPE_WEBRTC: u8 = 9;
|
||||
pub const TYPE_ZEROTIER_ENCAP: u8 = 10;
|
||||
|
||||
/// A communication endpoint on the network where some ZeroTier node can be reached.
|
||||
/// A communication endpoint on the network where a ZeroTier node can be reached.
|
||||
///
|
||||
/// Currently only a few of these are supported. The rest are reserved for future use.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::error::Error;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::io::Write;
|
||||
use std::convert::TryInto;
|
||||
|
@ -23,11 +25,41 @@ use crate::vl1::Address;
|
|||
use crate::vl1::protocol::*;
|
||||
use crate::vl1::symmetricsecret::SymmetricSecret;
|
||||
|
||||
const EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE: u8 = 0x01;
|
||||
const EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE_HMAC: u8 = 0x01;
|
||||
|
||||
pub const ALGORITHM_C25519: u8 = 0x01;
|
||||
pub const ALGORITHM_NISTP521ECDH: u8 = 0x02;
|
||||
pub const ALGORITHM_SIDHP751: u8 = 0x04;
|
||||
|
||||
pub enum EphemeralKeyAgreementError {
|
||||
OldPublic,
|
||||
StateMismatch,
|
||||
InvalidData,
|
||||
NoCompatibleAlgorithms
|
||||
}
|
||||
|
||||
impl Display for EphemeralKeyAgreementError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EphemeralKeyAgreementError::OldPublic => f.write_str("old (replayed?) public key data from remote"),
|
||||
EphemeralKeyAgreementError::StateMismatch => f.write_str("ratchet state mismatch"),
|
||||
EphemeralKeyAgreementError::InvalidData => f.write_str("invalid public key data"),
|
||||
EphemeralKeyAgreementError::NoCompatibleAlgorithms => f.write_str("no compatible algorithms in public key data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for EphemeralKeyAgreementError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { <Self as Display>::fmt(self, f) }
|
||||
}
|
||||
|
||||
impl Error for EphemeralKeyAgreementError {}
|
||||
|
||||
/// A set of ephemeral secret key pairs. Multiple algorithms are used.
|
||||
pub(crate) struct EphemeralKeyPairSet {
|
||||
previous_ratchet_state: Option<[u8; 16]>, // Previous state of ratchet on which this agreement should build
|
||||
previous_ratchet_count: u64, // Previous ratchet count, next ratchet should be this + 1
|
||||
state_hmac: Option<[u8; 48]>, // HMAC of previous ratchet count, if there was a previous state
|
||||
c25519: C25519KeyPair, // Hipster DJB cryptography
|
||||
p521: P521KeyPair, // US Federal Government cryptography
|
||||
sidhp751: Option<SIDHEphemeralKeyPair>, // Post-quantum moon math cryptography (not used in every ratchet tick)
|
||||
|
@ -39,15 +71,16 @@ impl EphemeralKeyPairSet {
|
|||
/// This contains key pairs for the asymmetric key agreement algorithms used and a
|
||||
/// timestamp used to enforce TTL.
|
||||
pub fn new(local_address: Address, remote_address: Address, previous_ephemeral_secret: Option<&EphemeralSymmetricSecret>) -> Self {
|
||||
let (sidhp751, previous_ratchet_state) = previous_ephemeral_secret.map_or_else(|| {
|
||||
let (sidhp751, previous_ratchet_count, state_hmac) = previous_ephemeral_secret.map_or_else(|| {
|
||||
(
|
||||
Some(SIDHEphemeralKeyPair::generate(local_address, remote_address)),
|
||||
0,
|
||||
None
|
||||
)
|
||||
}, |previous_ephemeral_secret| {
|
||||
(
|
||||
if previous_ephemeral_secret.ratchet_state[0] == 0 {
|
||||
// We include SIDH with a probability of 1/256, which for a 5 minute re-key interval
|
||||
if (previous_ephemeral_secret.ratchet_count & 0xff) == 0 {
|
||||
// We include SIDH every 256 ratchets, which for a 5 minute re-key interval
|
||||
// means SIDH will be included about every 24 hours. SIDH is slower and is intended
|
||||
// to guard against long term warehousing for eventual cracking with a QC, so this
|
||||
// should be good enough for that threat model.
|
||||
|
@ -55,11 +88,13 @@ impl EphemeralKeyPairSet {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
Some(previous_ephemeral_secret.ratchet_state.clone())
|
||||
previous_ephemeral_secret.ratchet_count,
|
||||
Some(SHA384::hmac(previous_ephemeral_secret.secret.ephemeral_ratchet_state_key.as_bytes(), &previous_ephemeral_secret.ratchet_count.to_be_bytes()))
|
||||
)
|
||||
});
|
||||
EphemeralKeyPairSet {
|
||||
previous_ratchet_state,
|
||||
previous_ratchet_count,
|
||||
state_hmac,
|
||||
c25519: C25519KeyPair::generate(true),
|
||||
p521: P521KeyPair::generate(true).expect("NIST P-521 key pair generation failed"),
|
||||
sidhp751,
|
||||
|
@ -73,19 +108,21 @@ impl EphemeralKeyPairSet {
|
|||
pub fn public_bytes(&self) -> Vec<u8> {
|
||||
let mut b: Vec<u8> = Vec::with_capacity(SHA384_HASH_SIZE + 8 + C25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + SIDH_P751_PUBLIC_KEY_SIZE);
|
||||
|
||||
if self.previous_ratchet_state.is_some() {
|
||||
b.push(EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE);
|
||||
let _ = b.write_all(self.previous_ratchet_state.as_ref().unwrap());
|
||||
let _ = varint::write(&mut b, self.previous_ratchet_count);
|
||||
|
||||
if self.state_hmac.is_some() {
|
||||
b.push(EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE_HMAC);
|
||||
let _ = b.write_all(self.state_hmac.as_ref().unwrap());
|
||||
} else {
|
||||
b.push(0);
|
||||
}
|
||||
|
||||
b.push(EphemeralKeyAgreementAlgorithm::C25519 as u8);
|
||||
b.push(ALGORITHM_C25519);
|
||||
let _ = varint::write(&mut b, C25519_PUBLIC_KEY_SIZE as u64);
|
||||
let _ = b.write_all(&self.c25519.public_bytes());
|
||||
|
||||
let _ = self.sidhp751.as_ref().map(|sidhp751| {
|
||||
b.push(EphemeralKeyAgreementAlgorithm::SIDHP751 as u8);
|
||||
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 {
|
||||
|
@ -100,7 +137,7 @@ impl EphemeralKeyPairSet {
|
|||
// 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(EphemeralKeyAgreementAlgorithm::NISTP521ECDH as u8);
|
||||
b.push(ALGORITHM_NISTP521ECDH);
|
||||
let _ = varint::write(&mut b, P521_PUBLIC_KEY_SIZE as u64);
|
||||
let _ = b.write_all(self.p521.public_key_bytes());
|
||||
|
||||
|
@ -115,41 +152,61 @@ impl EphemeralKeyPairSet {
|
|||
/// 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 (mut key, mut c25519_ratchet_count, mut sidhp751_ratchet_count, mut nistp521_ratchet_count) = previous_ephemeral_secret.map_or_else(|| {
|
||||
pub fn agree(self, time_ticks: i64, static_secret: &SymmetricSecret, previous_ephemeral_secret: Option<&EphemeralSymmetricSecret>, other_public_bytes: &[u8]) -> Result<EphemeralSymmetricSecret, EphemeralKeyAgreementError> {
|
||||
let (mut key, mut ratchet_count, mut c25519_ratchet_count, mut sidhp751_ratchet_count, mut nistp521_ratchet_count) = previous_ephemeral_secret.map_or_else(|| {
|
||||
(
|
||||
static_secret.ephemeral_ratchet_key.clone(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
}, |previous_ephemeral_secret| {
|
||||
(
|
||||
previous_ephemeral_secret.secret.ephemeral_ratchet_key.clone(),
|
||||
previous_ephemeral_secret.ratchet_count,
|
||||
previous_ephemeral_secret.c25519_ratchet_count,
|
||||
previous_ephemeral_secret.sidhp751_ratchet_count,
|
||||
previous_ephemeral_secret.nistp521_ratchet_count
|
||||
)
|
||||
});
|
||||
|
||||
let mut it_happened = false;
|
||||
let mut fips_compliant_exchange = false; // ends up true if last algorithm was FIPS compliant
|
||||
let mut it_happened = false; // set to true if at least one exchange occurred
|
||||
let mut fips_compliant_exchange = false; // is true in the end if the last algorithm was FIPS-compliant
|
||||
let mut other_public_bytes = other_public_bytes;
|
||||
|
||||
// Check that the other side's ratchet state matches ours. If not the ratchet must restart.
|
||||
if other_public_bytes.is_empty() {
|
||||
return None;
|
||||
// If the other side's ratchet counter is less than ours it means this may be a replayed
|
||||
// public key (or duplicate packet) and should be ignored. If it's greater it's a state
|
||||
// mismatch since there's no other way it could be from the future.
|
||||
let other_ratchet_count = varint::read(&mut other_public_bytes);
|
||||
if other_ratchet_count.is_err() {
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
if (other_public_bytes[0] & EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE) == 0 {
|
||||
if previous_ephemeral_secret.is_some() {
|
||||
return None;
|
||||
let other_ratchet_count = other_ratchet_count.unwrap().0;
|
||||
if other_ratchet_count < ratchet_count {
|
||||
return Err(EphemeralKeyAgreementError::OldPublic);
|
||||
} else if other_ratchet_count > ratchet_count {
|
||||
return Err(EphemeralKeyAgreementError::StateMismatch);
|
||||
}
|
||||
|
||||
// Now check the other side's HMAC of the ratchet state to fully verify that the ratchet
|
||||
// is aligned properly.
|
||||
if other_public_bytes.is_empty() {
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
if (other_public_bytes[0] & EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE_HMAC) != 0 {
|
||||
if other_public_bytes.len() < 49 {
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
if self.state_hmac.as_ref().map_or(true, |state_hmac| state_hmac != &other_public_bytes[1..49]) {
|
||||
return Err(EphemeralKeyAgreementError::StateMismatch);
|
||||
}
|
||||
other_public_bytes = &other_public_bytes[49..];
|
||||
} else {
|
||||
if self.state_hmac.is_some() {
|
||||
return Err(EphemeralKeyAgreementError::OldPublic);
|
||||
}
|
||||
other_public_bytes = &other_public_bytes[1..];
|
||||
} else {
|
||||
if other_public_bytes.len() < 17 || previous_ephemeral_secret.map_or(false, |previous_ephemeral_secret| other_public_bytes[1..17] != previous_ephemeral_secret.ratchet_state) {
|
||||
return None;
|
||||
}
|
||||
other_public_bytes = &other_public_bytes[17..];
|
||||
}
|
||||
|
||||
while !other_public_bytes.is_empty() {
|
||||
|
@ -157,15 +214,15 @@ impl EphemeralKeyPairSet {
|
|||
other_public_bytes = &other_public_bytes[1..];
|
||||
let key_len = varint::read(&mut other_public_bytes);
|
||||
if key_len.is_err() {
|
||||
return None;
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
let key_len = key_len.unwrap().0 as usize;
|
||||
|
||||
match cipher.try_into() {
|
||||
match cipher {
|
||||
|
||||
Ok(EphemeralKeyAgreementAlgorithm::C25519) => {
|
||||
ALGORITHM_C25519 => {
|
||||
if other_public_bytes.len() < C25519_PUBLIC_KEY_SIZE || key_len != C25519_PUBLIC_KEY_SIZE {
|
||||
return None;
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
|
||||
let c25519_secret = self.c25519.agree(&other_public_bytes[0..C25519_PUBLIC_KEY_SIZE]);
|
||||
|
@ -177,9 +234,9 @@ impl EphemeralKeyPairSet {
|
|||
c25519_ratchet_count += 1;
|
||||
},
|
||||
|
||||
Ok(EphemeralKeyAgreementAlgorithm::SIDHP751) => {
|
||||
ALGORITHM_SIDHP751 => {
|
||||
if other_public_bytes.len() < (SIDH_P751_PUBLIC_KEY_SIZE + 1) || key_len != (SIDH_P751_PUBLIC_KEY_SIZE + 1) {
|
||||
return None;
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
|
||||
let _ = match self.sidhp751.as_ref() {
|
||||
|
@ -207,20 +264,20 @@ impl EphemeralKeyPairSet {
|
|||
other_public_bytes = &other_public_bytes[(SIDH_P751_PUBLIC_KEY_SIZE + 1)..];
|
||||
},
|
||||
|
||||
Ok(EphemeralKeyAgreementAlgorithm::NISTP521ECDH) => {
|
||||
ALGORITHM_NISTP751ECDH => {
|
||||
if other_public_bytes.len() < P521_PUBLIC_KEY_SIZE || key_len != P521_PUBLIC_KEY_SIZE {
|
||||
return None;
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
|
||||
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;
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
|
||||
let p521_key = self.p521.agree(p521_public.as_ref().unwrap());
|
||||
if p521_key.is_none() {
|
||||
return None;
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
|
||||
key.0 = SHA384::hmac(&key.0, &p521_key.unwrap().0);
|
||||
|
@ -229,9 +286,9 @@ impl EphemeralKeyPairSet {
|
|||
nistp521_ratchet_count += 1;
|
||||
},
|
||||
|
||||
Err(_) => {
|
||||
_ => {
|
||||
if other_public_bytes.len() < key_len {
|
||||
return None;
|
||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
||||
}
|
||||
other_public_bytes = &other_public_bytes[key_len..];
|
||||
}
|
||||
|
@ -240,10 +297,10 @@ impl EphemeralKeyPairSet {
|
|||
}
|
||||
|
||||
return if it_happened {
|
||||
let rs = zt_kbkdf_hmac_sha384(&key.0, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_STATE_ID, 0, 0);
|
||||
Some(EphemeralSymmetricSecret {
|
||||
ratchet_count += 1;
|
||||
Ok(EphemeralSymmetricSecret {
|
||||
secret: SymmetricSecret::new(key),
|
||||
ratchet_state: (&rs.0[0..16]).try_into().unwrap(),
|
||||
ratchet_count,
|
||||
rekey_time: time_ticks + EPHEMERAL_SECRET_REKEY_AFTER_TIME,
|
||||
expire_time: time_ticks + EPHEMERAL_SECRET_REJECT_AFTER_TIME,
|
||||
c25519_ratchet_count,
|
||||
|
@ -254,17 +311,17 @@ impl EphemeralKeyPairSet {
|
|||
fips_compliant_exchange
|
||||
})
|
||||
} else {
|
||||
None
|
||||
Err(EphemeralKeyAgreementError::NoCompatibleAlgorithms)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Symmetric secret representing a step in the ephemeral keying ratchet.
|
||||
/// An ephemeral symmetric secret with usage timers and counters.
|
||||
pub(crate) struct EphemeralSymmetricSecret {
|
||||
/// Current ephemeral secret key.
|
||||
pub secret: SymmetricSecret,
|
||||
/// An identifier used to check negotiation of the ratchet.
|
||||
ratchet_state: [u8; 16],
|
||||
/// Total number of ratchets that has occurred.
|
||||
ratchet_count: u64,
|
||||
/// Time at or after which we should start trying to re-key.
|
||||
rekey_time: i64,
|
||||
/// Time after which this key is no longer valid.
|
||||
|
@ -280,7 +337,7 @@ pub(crate) struct EphemeralSymmetricSecret {
|
|||
/// Number of times this secret has been used to decrypt.
|
||||
decrypt_uses: AtomicU32,
|
||||
/// True if most recent key exchange was NIST/FIPS compliant.
|
||||
fips_compliant_exchange: bool,
|
||||
pub fips_compliant_exchange: bool,
|
||||
}
|
||||
|
||||
impl EphemeralSymmetricSecret {
|
||||
|
@ -307,6 +364,7 @@ impl EphemeralSymmetricSecret {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
|
@ -364,10 +422,10 @@ mod tests {
|
|||
for t in 1..ratchets+1 {
|
||||
let alice_public = alice.public_bytes();
|
||||
let bob_public = bob.public_bytes();
|
||||
let alice_key = alice.agree(1, &static_secret, prev_alice_key.as_ref(), bob_public.as_slice());
|
||||
let bob_key = bob.agree(1, &static_secret, prev_bob_key.as_ref(), alice_public.as_slice());
|
||||
assert!(alice_key.is_some());
|
||||
assert!(bob_key.is_some());
|
||||
let alice_key = alice.agree(t, &static_secret, prev_alice_key.as_ref(), bob_public.as_slice());
|
||||
let bob_key = bob.agree(t, &static_secret, prev_bob_key.as_ref(), alice_public.as_slice());
|
||||
assert!(alice_key.is_ok());
|
||||
assert!(bob_key.is_ok());
|
||||
let alice_key = alice_key.unwrap();
|
||||
let bob_key = bob_key.unwrap();
|
||||
assert_eq!(&alice_key.secret.key.0, &bob_key.secret.key.0);
|
||||
|
@ -379,10 +437,12 @@ mod tests {
|
|||
}
|
||||
let last_alice_key = prev_alice_key.unwrap();
|
||||
let last_bob_key = prev_bob_key.unwrap();
|
||||
assert_eq!(last_alice_key.c25519_ratchet_count, ratchets);
|
||||
assert_eq!(last_bob_key.c25519_ratchet_count, ratchets);
|
||||
assert_eq!(last_alice_key.nistp521_ratchet_count, ratchets);
|
||||
assert_eq!(last_bob_key.nistp521_ratchet_count, ratchets);
|
||||
assert_eq!(last_alice_key.ratchet_count, ratchets as u64);
|
||||
assert_eq!(last_bob_key.ratchet_count, ratchets as u64);
|
||||
assert_eq!(last_alice_key.c25519_ratchet_count, ratchets as u64);
|
||||
assert_eq!(last_bob_key.c25519_ratchet_count, ratchets as u64);
|
||||
assert_eq!(last_alice_key.nistp521_ratchet_count, ratchets as u64);
|
||||
assert_eq!(last_bob_key.nistp521_ratchet_count, ratchets as u64);
|
||||
assert_eq!(last_alice_key.sidhp751_ratchet_count, last_bob_key.sidhp751_ratchet_count);
|
||||
assert!(last_alice_key.sidhp751_ratchet_count >= 1);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1: u8 = b'1';
|
|||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_NEXT_KEY: u8 = b'e';
|
||||
|
||||
/// KBKDF usage label for generating the ratchet state ID (which is not actually a key).
|
||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_STATE_ID: u8 = b'E';
|
||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_STATE_KEY: u8 = b'E';
|
||||
|
||||
/// Try to re-key ephemeral keys after this time.
|
||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
|
||||
|
@ -181,37 +181,6 @@ pub const IDENTITY_V0_POW_THRESHOLD: u8 = 17;
|
|||
/// This is lower than the V0 threshold, causing the V0 part of V1 identities to verify on old nodes.
|
||||
pub const IDENTITY_V1_POW_THRESHOLD: u8 = 9;
|
||||
|
||||
#[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
|
||||
|
|
|
@ -38,6 +38,9 @@ pub(crate) struct SymmetricSecret {
|
|||
/// A key used as input to the ephemeral key ratcheting mechanism.
|
||||
pub ephemeral_ratchet_key: Secret<SHA384_HASH_SIZE>,
|
||||
|
||||
/// A key used to verify the state of the ephemeral ratchet.
|
||||
pub ephemeral_ratchet_state_key: Secret<SHA384_HASH_SIZE>,
|
||||
|
||||
/// A pool of reusable keyed and initialized AES-GMAC-SIV ciphers.
|
||||
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
||||
}
|
||||
|
@ -54,6 +57,7 @@ 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_NEXT_KEY, 0, 0);
|
||||
let usage_ephemeral_ratchet_state = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_STATE_KEY, 0, 0);
|
||||
let aes_factory = AesGmacSivPoolFactory(
|
||||
zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0),
|
||||
zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, 0, 0));
|
||||
|
@ -61,6 +65,7 @@ impl SymmetricSecret {
|
|||
key: base_key,
|
||||
packet_hmac_key: usage_packet_hmac,
|
||||
ephemeral_ratchet_key: usage_ephemeral_ratchet,
|
||||
ephemeral_ratchet_state_key: usage_ephemeral_ratchet_state,
|
||||
aes_gmac_siv: Pool::new(2, aes_factory),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue