Use a state hash for determining if ephemeral ratchet can advance, and some big perf improvements in SIDH.

This commit is contained in:
Adam Ierymenko 2021-11-17 10:17:23 -05:00
parent be90abdc52
commit b335c631a9
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 105 additions and 114 deletions

View file

@ -25,7 +25,7 @@ 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();
m.set_key(key).expect("FATAL: invalid HMAC-SHA512 key");;
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];
m.get_mac(&mut h).expect("FATAL: HMAC-SHA512 failed");

View file

@ -9,7 +9,7 @@
//! This module contains internal curve representation and operations
//! for SIDH, which is not part of the public API.
use crate::sidhp751::fp::Fp751Element;
use crate::sidhp751::fp::{Fp751Element, FP751_NUM_WORDS};
use crate::sidhp751::field::{PrimeFieldElement, ExtensionFieldElement};
use crate::sidhp751::constants::*;
@ -19,9 +19,7 @@ use subtle::{ConditionallySelectable, Choice};
#[cfg(test)]
use quickcheck::{Gen, Arbitrary};
use std::mem::zeroed;
// Macro to assign tuples, as Rust does not allow tuples as lvalue.
macro_rules! assign{
{($v1:ident, $v2:ident) = $e:expr} =>
{
@ -36,7 +34,7 @@ macro_rules! assign{
// = 256
const CONST_256: ExtensionFieldElement = ExtensionFieldElement {
A: Fp751Element([0x249ad67, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7300000, 0x9973da8b, 0x73815496, 0x46718c7f, 0x856657c1, 0xe363a697, 0x461860e4,0xbba838cd, 0xf9fd6510,0x06993c0c, 0x4e1a3c3f, 0xef5b75c7, 0x55ab]),
B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0])
B: Fp751Element([0_u32; FP751_NUM_WORDS])
};
/// A point on the projective line `P^1(F_{p^2})`.
@ -193,8 +191,7 @@ impl Arbitrary for ProjectivePoint {
impl ProjectivePoint {
/// Creates a new zero `ProejctivePoint`.
pub fn new() -> ProjectivePoint {
unsafe { zeroed() }
//ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() }
ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() }
}
#[allow(non_snake_case)]
@ -731,8 +728,7 @@ impl Arbitrary for ProjectivePrimeFieldPoint {
impl ProjectivePrimeFieldPoint {
/// Creates a new zero `ProjectivePrimeFieldPoint`.
pub fn new() -> ProjectivePrimeFieldPoint {
unsafe { zeroed() }
//ProjectivePrimeFieldPoint{ X: PrimeFieldElement::zero(), Z: PrimeFieldElement::zero() }
ProjectivePrimeFieldPoint{ X: PrimeFieldElement::zero(), Z: PrimeFieldElement::zero() }
}
pub fn from_affine(x: &PrimeFieldElement) -> ProjectivePrimeFieldPoint {

View file

@ -14,7 +14,6 @@ use crate::sidhp751::fp::*;
use std::fmt::Debug;
use std::cmp::{Eq, PartialEq};
use std::mem::zeroed;
use std::ops::*;
use subtle::ConditionallySelectable;
@ -179,18 +178,17 @@ impl Arbitrary for ExtensionFieldElement {
impl ExtensionFieldElement {
/// Construct a zero `ExtensionFieldElement`.
pub fn zero() -> ExtensionFieldElement {
unsafe { zeroed() }
//ExtensionFieldElement{
// A: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
// B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
//}
ExtensionFieldElement{
A: Fp751Element::zero(),
B: Fp751Element::zero(),
}
}
/// Construct a one `ExtensionFieldElement`.
pub fn one() -> ExtensionFieldElement {
ExtensionFieldElement{
A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]),
B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
B: Fp751Element([0_u32; FP751_NUM_WORDS]),
}
}
@ -406,11 +404,11 @@ impl Arbitrary for PrimeFieldElement {
impl PrimeFieldElement {
/// Construct a zero `PrimeFieldElement`.
#[inline(always)]
pub fn zero() -> PrimeFieldElement {
unsafe { zeroed() }
//PrimeFieldElement{
// A: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
//}
PrimeFieldElement{
A: Fp751Element::zero(),
}
}
/// Construct a one `PrimeFieldElement`.

View file

@ -8,7 +8,7 @@
use crate::random::SecureRandom;
use std::mem::{size_of, MaybeUninit, zeroed};
use std::mem::size_of;
use std::fmt::Debug;
use std::ops::Neg;
@ -18,8 +18,6 @@ use quickcheck::{Arbitrary, Gen};
use subtle::{ConditionallySelectable, Choice};
use rand_core::RngCore;
// Macro to assign tuples, as Rust does not allow tuples as lvalue.
#[macro_export]
macro_rules! assign{
{($v1:ident, $v2:expr) = $e:expr} =>
{
@ -31,7 +29,6 @@ macro_rules! assign{
};
}
// X86 finite field arithmetic
const RADIX: u32 = 32;
pub const FP751_NUM_WORDS: usize = 24;
const P751_ZERO_WORDS: usize = 11;
@ -40,44 +37,11 @@ const P751: [u32; FP751_NUM_WORDS] = [4294967295, 4294967295, 4294967295, 429496
const P751P1: [u32; FP751_NUM_WORDS] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4004511744, 1241020584, 3823933061, 335006838, 3667237658, 3605784694, 139368551, 1555191624, 2237838596, 2545605734, 236097695, 3577870108, 28645];
const P751X2: [u32; FP751_NUM_WORDS] = [4294967294, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 3714056191, 2482041169, 3352898826, 670013677, 3039508020, 2916602093, 278737103, 3110383248, 180709896, 796244173, 472195391, 2860772920, 57291];
fn digit_x_digit(a: u32, b: u32, c: &mut [u32; 2]) {
#[allow(non_upper_case_globals)]
const sizeof_u32: u32 = size_of::<u32>() as u32;
#[allow(non_upper_case_globals)]
const mask_low: u32 = <u32>::MAX >> (sizeof_u32 * 4);
#[allow(non_upper_case_globals)]
const mask_high: u32 = <u32>::MAX << (sizeof_u32 * 4);
let al = a & mask_low;
let ah = a >> (sizeof_u32 * 4);
let bl = b & mask_low;
let bh = b >> (sizeof_u32 * 4);
let albl = al * bl;
let albh = al * bh;
let ahbl = ah * bl;
let ahbh = ah * bh;
let c0 = albl & mask_low;
let mut res1 = albl >> (sizeof_u32 * 4);
let mut res2 = ahbl & mask_low;
let mut res3 = albh & mask_low;
let mut temp = res1 + res2 + res3;
let mut carry = temp >> (sizeof_u32 * 4);
c[0] = c0 ^ (temp << (sizeof_u32 * 4));
res1 = ahbl >> (sizeof_u32 * 4);
res2 = albh >> (sizeof_u32 * 4);
res3 = ahbh & mask_low;
temp = res1 + res2 + res3 + carry;
let c1 = temp & mask_low;
carry = temp & mask_high;
c[1] = c1 ^ ((ahbh & mask_high) + carry);
}
#[inline(always)]
fn mul(multiplier: u32, multiplicant: u32, uv: &mut [u32; 2]) {
digit_x_digit(multiplier, multiplicant, uv);
let p = (multiplier as u64) * (multiplicant as u64);
uv[0] = p as u32;
uv[1] = (p >> 32) as u32;
}
#[inline(always)]
@ -320,16 +284,14 @@ pub struct Fp751Element(pub (crate) [u32; FP751_NUM_WORDS]);
pub struct Fp751ElementDist;
impl ConditionallySelectable for Fp751Element {
#[inline(always)]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut bytes = unsafe { MaybeUninit::<Fp751Element>::uninit().assume_init() };
let mut bytes = Fp751Element::zero();
for i in 0..FP751_NUM_WORDS {
bytes.0[i] = u32::conditional_select(&a.0[i], &b.0[i], choice);
}
bytes
}
#[inline(always)]
fn conditional_assign(&mut self, f: &Self, choice: Choice) {
let mask = ((choice.unwrap_u8() as i32).neg()) as u32;
for i in 0..FP751_NUM_WORDS {
@ -376,8 +338,7 @@ impl Fp751Element {
/// Construct a new zero `Fp751Element`.
#[inline(always)]
pub fn zero() -> Fp751Element {
unsafe { zeroed() }
//Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0])
Fp751Element([0_u32; FP751_NUM_WORDS])
}
/// Given an `Fp751Element` in Montgomery form, convert to little-endian bytes.
@ -386,10 +347,8 @@ impl Fp751Element {
let mut a = Fp751Element::zero();
#[allow(non_snake_case)]
let mut aR = Fp751X2::zero();
//let one = Fp751Element([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
aR.0[..FP751_NUM_WORDS].clone_from_slice(&self.0);
//aR = self * &one;
a = aR.reduce(); // = a mod p in [0, 2p)
a = a.strong_reduce(); // = a mod p in [0, p)
@ -399,7 +358,6 @@ impl Fp751Element {
for i in 0..94 {
j = i / 4;
k = (i % 4) as u32;
// Rust indexes are of type usize.
bytes[i as usize] = (a.0[j as usize] >> (8 * k)) as u8;
}
bytes
@ -439,9 +397,7 @@ impl Fp751X2 {
// Construct a zero `Fp751X2`.
#[inline(always)]
pub fn zero() -> Fp751X2 {
unsafe { zeroed() }
//Fp751X2([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
// 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0])
Fp751X2([0_u32; { 2 * FP751_NUM_WORDS }])
}
}

View file

@ -6,7 +6,7 @@
* https://www.zerotier.com/
*/
use std::sync::atomic::AtomicU32;
use std::sync::atomic::{AtomicU32, Ordering};
use std::io::Write;
use std::convert::TryInto;
@ -19,47 +19,52 @@ use zerotier_core_crypto::sidhp751::{SIDHPublicKeyAlice, SIDHPublicKeyBob, SIDHS
use zerotier_core_crypto::varint;
use crate::vl1::Address;
use crate::vl1::protocol::EphemeralKeyAgreementAlgorithm;
use crate::vl1::protocol::{EphemeralKeyAgreementAlgorithm, EPHEMERAL_SECRET_USE_SIDH_EVERY_N_RATCHETS, EPHEMERAL_SECRET_REKEY_AFTER_TIME, EPHEMERAL_SECRET_REKEY_AFTER_USES, EPHEMERAL_SECRET_REJECT_AFTER_TIME};
use crate::vl1::symmetricsecret::SymmetricSecret;
/// An ephemeral secret key negotiated to implement forward secrecy.
pub struct EphemeralSecret {
timestamp_ticks: i64,
ratchet_count: u64,
/// A set of ephemeral secret key pairs. Multiple algorithms are used.
pub struct EphemeralKeyPairSet {
previous_ratchet_state: Option<[u8; 16]>,
c25519: C25519KeyPair,
p521: P521KeyPair,
sidhp751: Option<SIDHEphemeralKeyPair>,
}
impl EphemeralSecret {
/// Create a new ephemeral secret key.
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.
///
/// SIDH is much slower than Curve25519 and NIST P-521, so it's only included every
/// 256 clicks of the ratchet. The point of SIDH is forward secrecy out to the age
/// of quantum computing in case someone is warehousing traffic today to analyze
/// tomorrow. An attacker from 5-15 years from now will not be able to time travel
/// back in time and steal an ephemeral SIDH secret key with a side channel attack.
pub fn new(time_ticks: i64, local_address: Address, remote_address: Address, previous_ephemeral_secret: Option<&EphemeralSymmetricSecret>) -> Self {
let ratchet_count = previous_ephemeral_secret.map_or(0_u64, |previous_ephemeral_secret| previous_ephemeral_secret.next_ratchet_count);
EphemeralSecret {
timestamp_ticks: time_ticks,
ratchet_count,
/// SIDH is only used the first time since it's slow and its only purpose is to
/// defend against further-future quantum computer attacks.
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(|| {
(Some(SIDHEphemeralKeyPair::generate(local_address, remote_address)), None)
}, |previous_ephemeral_secret| {
(None, Some(previous_ephemeral_secret.ratchet_state.clone()))
});
EphemeralKeyPairSet {
previous_ratchet_state,
c25519: C25519KeyPair::generate(true),
p521: P521KeyPair::generate(true).expect("NIST P-521 key pair generation failed"),
sidhp751: if (ratchet_count % 256) == 0 {
Some(SIDHEphemeralKeyPair::generate(local_address, remote_address))
} else {
None
},
sidhp751,
}
}
/// 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(8 + C25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + SIDH_P751_PUBLIC_KEY_SIZE);
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);
self.previous_ratchet_state.as_ref().map_or_else(|| {
b.push(0); // no flags
}, |previous_ratchet_state| {
b.push(1); // flag: previous ephemeral secret hash included
let _ = b.write_all(previous_ratchet_state);
});
b.push(EphemeralKeyAgreementAlgorithm::C25519 as u8);
let _ = varint::write(&mut b, C25519_PUBLIC_KEY_SIZE as u64);
@ -78,8 +83,8 @@ impl EphemeralSecret {
// 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.
// 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);
let _ = varint::write(&mut b, P521_PUBLIC_KEY_SIZE as u64);
@ -97,14 +102,36 @@ impl EphemeralSecret {
///
/// 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 = previous_ephemeral_secret.map_or_else(|| {
static_secret.next_ephemeral_ratchet_key.clone()
let (mut key, mut c25519_ratchet_count, mut sidhp751_ratchet_count, mut nistp521_ratchet_count) = previous_ephemeral_secret.map_or_else(|| {
(static_secret.next_ephemeral_ratchet_key.clone(), 0, 0, 0)
}, |previous_ephemeral_secret| {
Secret(SHA384::hmac(&static_secret.next_ephemeral_ratchet_key.0, &previous_ephemeral_secret.secret.next_ephemeral_ratchet_key.0))
(
Secret(SHA384::hmac(&static_secret.next_ephemeral_ratchet_key.0, &previous_ephemeral_secret.secret.next_ephemeral_ratchet_key.0)),
previous_ephemeral_secret.c25519_ratchet_count,
previous_ephemeral_secret.sidhp751_ratchet_count,
previous_ephemeral_secret.nistp512_ratchet_count
)
});
let mut algs: Vec<EphemeralKeyAgreementAlgorithm> = Vec::with_capacity(3);
let mut other_public_bytes = other_public_bytes;
// Make sure the state of the ratchet matches on both ends. Otherwise it must restart.
if other_public_bytes.is_empty() {
return None;
}
if (other_public_bytes[0] & 1) == 0 {
if previous_ephemeral_secret.is_some() {
return None;
}
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].ne(&previous_ephemeral_secret.ratchet_state)) {
return None;
}
other_public_bytes = &other_public_bytes[17..];
}
while !other_public_bytes.is_empty() {
let cipher = other_public_bytes[0];
other_public_bytes = &other_public_bytes[1..];
@ -124,6 +151,7 @@ impl EphemeralSecret {
other_public_bytes = &other_public_bytes[C25519_PUBLIC_KEY_SIZE..];
key.0 = SHA384::hmac(&key.0, &c25519_secret.0);
algs.push(EphemeralKeyAgreementAlgorithm::C25519);
c25519_ratchet_count += 1;
},
Ok(EphemeralKeyAgreementAlgorithm::SIDHP751) => {
@ -149,6 +177,7 @@ impl EphemeralSecret {
}.map(|sidh_secret| {
key.0 = SHA384::hmac(&key.0, &sidh_secret.0);
algs.push(EphemeralKeyAgreementAlgorithm::SIDHP751);
sidh_ratchet_count += 1;
});
other_public_bytes = &other_public_bytes[(SIDH_P751_PUBLIC_KEY_SIZE + 1)..];
},
@ -168,6 +197,7 @@ impl EphemeralSecret {
}
key.0 = SHA384::hmac(&key.0, &p521_key.unwrap().0);
algs.push(EphemeralKeyAgreementAlgorithm::NistP521ECDH);
nistp521_ratchet_count += 1;
},
Err(_) => {
@ -183,10 +213,13 @@ impl EphemeralSecret {
return if !algs.is_empty() {
Some(EphemeralSymmetricSecret {
secret: SymmetricSecret::new(key),
ratchet_state: SHA384::hash(&key.0)[0..16].try_into().unwrap(),
agreement_algorithms: algs,
agreement_timestamp_ticks: time_ticks,
local_secret_timestamp_ticks: self.timestamp_ticks,
next_ratchet_count: self.ratchet_count + 1,
rekey_time: time_ticks + EPHEMERAL_SECRET_REKEY_AFTER_TIME,
expire_time: time_ticks + EPHEMERAL_SECRET_REJECT_AFTER_TIME,
c25519_ratchet_count,
sidhp751_ratchet_count,
nistp512_ratchet_count,
encrypt_uses: AtomicU32::new(0),
decrypt_uses: AtomicU32::new(0)
})
@ -196,12 +229,16 @@ impl EphemeralSecret {
}
}
/// Symmetric secret representing a step in the ephemeral keying ratchet.
pub struct EphemeralSymmetricSecret {
secret: SymmetricSecret,
ratchet_state: [u8; 16],
agreement_algorithms: Vec<EphemeralKeyAgreementAlgorithm>,
agreement_timestamp_ticks: i64,
local_secret_timestamp_ticks: i64,
next_ratchet_count: u64,
rekey_time: i64,
expire_time: i64,
c25519_ratchet_count: u64,
sidhp751_ratchet_count: u64,
nistp512_ratchet_count: u64,
encrypt_uses: AtomicU32,
decrypt_uses: AtomicU32,
}
@ -209,19 +246,23 @@ pub struct EphemeralSymmetricSecret {
impl EphemeralSymmetricSecret {
#[inline(always)]
pub fn use_secret_to_encrypt(&self) -> &SymmetricSecret {
let _ = self.encrypt_uses.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let _ = self.encrypt_uses.fetch_add(1, 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);
let _ = self.decrypt_uses.fetch_add(1, Ordering::Relaxed);
&self.secret
}
pub fn is_fips_compliant(&self) -> bool {
self.agreement_algorithms.last().map_or(false, |alg| alg.is_fips_compliant())
}
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
}
}
#[derive(Copy, Clone)]
@ -264,7 +305,7 @@ impl SIDHEphemeralKeyPair {
#[cfg(test)]
mod tests {
use crate::vl1::ephemeral::EphemeralSecret;
use crate::vl1::ephemeral::EphemeralKeyPairSet;
use crate::vl1::Address;
use crate::vl1::symmetricsecret::SymmetricSecret;
use zerotier_core_crypto::secret::Secret;
@ -272,8 +313,8 @@ mod tests {
#[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(), None);
let bob = EphemeralSecret::new(1, Address::from_u64(0xbeefdead00).unwrap(), Address::from_u64(0xdeadbeef00).unwrap(), None);
let alice = EphemeralKeyPairSet::new(Address::from_u64(0xdeadbeef00).unwrap(), Address::from_u64(0xbeefdead00).unwrap(), None);
let bob = EphemeralKeyPairSet::new(Address::from_u64(0xbeefdead00).unwrap(), Address::from_u64(0xdeadbeef00).unwrap(), None);
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();

View file

@ -51,16 +51,16 @@ pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1: u8 = b'1';
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
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
/// Maximum number of times to use an ephemeral secret before trying to replace it.
pub const EPHEMERAL_SECRET_REKEY_AFTER_USES: u32 = 536870912; // 1/4 the NIST security limit
pub const EPHEMERAL_SECRET_REKEY_AFTER_USES: u32 = 536870912; // 1/4 the NIST/FIPS security bound of 2^31
/// Ephemeral secret reject after time.
pub const EPHEMERAL_SECRET_REJECT_AFTER_TIME: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME * 2;
/// Ephemeral secret reject after uses.
pub const EPHEMERAL_SECRET_REJECT_AFTER_USES: u32 = 2147483648; // NIST security limit
pub const EPHEMERAL_SECRET_REJECT_AFTER_USES: u32 = 2147483648; // NIST/FIPS security bound
/// Length of an address in bytes.
pub const ADDRESS_SIZE: usize = 5;