mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-22 22:33:58 -07:00
More ZSSP work, add benchmarks for mimcvdf.
This commit is contained in:
parent
f8aa44082e
commit
967dcaf377
6 changed files with 279 additions and 256 deletions
|
@ -1,10 +1,27 @@
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use zerotier_crypto::mimcvdf;
|
||||||
use zerotier_crypto::p384::*;
|
use zerotier_crypto::p384::*;
|
||||||
use zerotier_crypto::x25519::*;
|
use zerotier_crypto::x25519::*;
|
||||||
|
|
||||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("cryptography");
|
||||||
|
|
||||||
|
let mut input = 1;
|
||||||
|
let mut proof = 0;
|
||||||
|
group.bench_function("mimcvdf::delay(1000)", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
input += 1;
|
||||||
|
proof = mimcvdf::delay(input, 1000);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
group.bench_function("mimcvdf::verify(1000)", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
assert!(mimcvdf::verify(proof, input, 1000));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let p384_a = P384KeyPair::generate();
|
let p384_a = P384KeyPair::generate();
|
||||||
let p384_b = P384KeyPair::generate();
|
let p384_b = P384KeyPair::generate();
|
||||||
|
|
||||||
|
@ -12,7 +29,6 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
let x25519_b = X25519KeyPair::generate();
|
let x25519_b = X25519KeyPair::generate();
|
||||||
let x25519_b_pub = x25519_b.public_bytes();
|
let x25519_b_pub = x25519_b.public_bytes();
|
||||||
|
|
||||||
let mut group = c.benchmark_group("cryptography");
|
|
||||||
group.measurement_time(Duration::new(10, 0));
|
group.measurement_time(Duration::new(10, 0));
|
||||||
|
|
||||||
group.bench_function("ecdhp384", |b| {
|
group.bench_function("ecdhp384", |b| {
|
||||||
|
|
|
@ -7,81 +7,71 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* MIMC is a hash function originally designed for use with STARK and SNARK proofs. It's based
|
||||||
|
* on modular multiplication and exponentiation instead of the usual bit twiddling or ARX
|
||||||
|
* operations that underpin more common hash algorithms.
|
||||||
|
*
|
||||||
|
* It's useful as a verifiable delay function because it can be computed in both directions with
|
||||||
|
* one direction taking orders of magnitude longer than the other. The "backward" direction is
|
||||||
|
* used as the delay function as it requires modular exponentiation which is inherently more
|
||||||
|
* compute intensive. The "forward" direction simply requires modular cubing which is two modular
|
||||||
|
* multiplications and is much faster.
|
||||||
|
*
|
||||||
|
* It's also nice because it's incredibly simple with a tiny code footprint.
|
||||||
|
*
|
||||||
|
* This is used for anti-DOS and anti-spamming delay functions. It's not used for anything
|
||||||
|
* really "cryptographically hard," and if it were broken cryptographically it would still be
|
||||||
|
* useful as a VDF as long as the break didn't yield a significantly faster way of computing a
|
||||||
|
* delay proof than the straightforward iterative way implemented here.
|
||||||
|
*
|
||||||
|
* Here are two references on MIMC with the first being the original paper and the second being
|
||||||
|
* a blog post describing its use as a VDF.
|
||||||
|
*
|
||||||
* https://eprint.iacr.org/2016/492.pdf
|
* https://eprint.iacr.org/2016/492.pdf
|
||||||
* https://vitalik.ca/general/2018/07/21/starks_part_3.html
|
* https://vitalik.ca/general/2018/07/21/starks_part_3.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 2^127 - 39
|
// p = 2^127 - 39, the largest 127-bit prime of the form 6k + 5
|
||||||
const PRIME: u128 = 170141183460469231731687303715884105689;
|
const PRIME: u128 = 170141183460469231731687303715884105689;
|
||||||
// 2p-1/3
|
|
||||||
|
// (2p - 1) / 3
|
||||||
const PRIME_2P_MINUS_1_DIV_3: u128 = 113427455640312821154458202477256070459;
|
const PRIME_2P_MINUS_1_DIV_3: u128 = 113427455640312821154458202477256070459;
|
||||||
|
|
||||||
const K_COUNT_MASK: usize = 63;
|
// Randomly generated round constants, each modulo PRIME.
|
||||||
const K: [u64; 64] = [
|
const K_COUNT_MASK: usize = 31;
|
||||||
0x921cdfd99022340f,
|
const K: [u128; 32] = [
|
||||||
0xe7c65f78c70afaa8,
|
0x1fdd07a761b611bb1ab9419a70599a7c,
|
||||||
0x72793744494c4fda,
|
0x23056b05d5c6b925e333d7418047650a,
|
||||||
0x67759e2688bc9c0a,
|
0x77a638f9b437a307f8866fbd2672c705,
|
||||||
0x7681a224661f0ac0,
|
0x60213dab83bab91d1c310bd87e9da332,
|
||||||
0xa7b81b099925a2bf,
|
0xf56bc883301ab373179e46b098b7a7,
|
||||||
0x16d43792e66b030a,
|
0x7914a0dbd2f971344173b350c28a838,
|
||||||
0x841bd90742d26ee9,
|
0x44bb64af5e446e6ebdc068d10d318f26,
|
||||||
0xb1346ec08db97053,
|
0x1bca1921fd328bb725ae0cbcbc20a263,
|
||||||
0xd044229c1173d972,
|
0xafa963242f5216a7da1cd5328b23659,
|
||||||
0xf4813498dfdead0e,
|
0x7fe17c43782b883a63ee0a790e0b2b77,
|
||||||
0xe46dca4c237d2c28,
|
0x23bb62abf728bf453200ee528f902c33,
|
||||||
0xac64872778089599,
|
0x75ec0c055be14955db6878567e3c0465,
|
||||||
0x67be75af74416e74,
|
0x7902bb57876e0b08b4de02a66755e5d7,
|
||||||
0xb9dec3aefd3ae012,
|
0xe5d7094f37b615f5a1e1594b0390de8,
|
||||||
0xf0497147953c4276,
|
0x12d4ddee90653a26f5de63ff4651f2d,
|
||||||
0xf6ac07fd3944177d,
|
0xce4a15bc35633b5ed8bcae2c93d739c,
|
||||||
0xccf1c28813eb589b,
|
0x23f25b935e52df87255db8c608ef9ab4,
|
||||||
0x49abb5e2b0bff5bd,
|
0x611a08d7464fb984c98104d77f1609a7,
|
||||||
0xd5c15eeb39587d69,
|
0x7aa825876a7f6acde5efa57992da9c43,
|
||||||
0x9c6ff50ee6898649,
|
0x2be9686f630fa28a0a0e1081a59755b4,
|
||||||
0x763f3b25524a0fbf,
|
0x50060dac9ac4656ba3f8ee7592f4e28a,
|
||||||
0xa6029c37f715c02c,
|
0x4113abff6f5bb303eac2ca809d4d529d,
|
||||||
0xe458a5902b2b5629,
|
0x2af9d01d4e753feb5834c14ca0543397,
|
||||||
0x8e4d6be6a1ba32c5,
|
0x73c2d764691ced2b823dda887e22ae85,
|
||||||
0x052aba0b61738f20,
|
0x5b53dcd4750ff888dca2497cec4dacb7,
|
||||||
0xc18a6901fa026b12,
|
0x5d8984a52c2d8f3cc9bcf61ef29f8a1,
|
||||||
0x137df11cf1dbe811,
|
0x588d8cc99533d649aabb5f0f552140e,
|
||||||
0x5da0310e419be602,
|
0x4dae04985fde8c8464ba08aaa7d8761e,
|
||||||
0xc66ddec578f52891,
|
0x53f0c4740b8c3bda3fc05109b9a2b71,
|
||||||
0xe4eae4efc0f0d54f,
|
0x3e918c88a6795e3bf840e0b74d91b9d7,
|
||||||
0xf9d488269f118012,
|
0x1dbcb30d724f11200aebb1dff87def91,
|
||||||
0xcf9b5108f66e77d1,
|
0x6086b0af0e1e68558170239d23be9780,
|
||||||
0x443ba29939f5a657,
|
|
||||||
0xa4e4b7d28c51e5c2,
|
|
||||||
0xe030d1772f112c01,
|
|
||||||
0xe136f0cf8da5e172,
|
|
||||||
0x3e9ee638f9663dc2,
|
|
||||||
0xbc5c1db73e639dfd,
|
|
||||||
0xa9fbbaa873fedf73,
|
|
||||||
0xffb2a5247d10ab8f,
|
|
||||||
0x06e6f3b5ae4b67ac,
|
|
||||||
0x475e7d427d331282,
|
|
||||||
0xcac6237c40a9d653,
|
|
||||||
0xe9a15c1d177beefa,
|
|
||||||
0xa14ef2111c2175a3,
|
|
||||||
0x8427d4b68982fc21,
|
|
||||||
0x12171e2a55d43343,
|
|
||||||
0x37715fdea87a0a60,
|
|
||||||
0x24bc5d28cff8ecad,
|
|
||||||
0x92276e4118304e62,
|
|
||||||
0x824b66792f58dd45,
|
|
||||||
0xe43973cf253b6947,
|
|
||||||
0xd0db2c5a2a4f064d,
|
|
||||||
0x734cdb241520ad04,
|
|
||||||
0xcec4f2ce5013069e,
|
|
||||||
0x2741c83c07bbf9e0,
|
|
||||||
0x284be707dcbda1a4,
|
|
||||||
0xd602f3d8545799b2,
|
|
||||||
0xea3977f56573b4d2,
|
|
||||||
0x0723fda64d57d0c6,
|
|
||||||
0x04dc344d0dde863a,
|
|
||||||
0x7584143462914be4,
|
|
||||||
0x111307f7823dfcc6,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fn mulmod<const M: u128>(mut a: u128, mut b: u128) -> u128 {
|
fn mulmod<const M: u128>(mut a: u128, mut b: u128) -> u128 {
|
||||||
|
@ -116,21 +106,23 @@ fn powmod<const M: u128>(mut base: u128, mut exp: u128) -> u128 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute MIMC for the given number of iterations and return a proof that can be checked much more quickly.
|
||||||
pub fn delay(mut input: u128, rounds: usize) -> u128 {
|
pub fn delay(mut input: u128, rounds: usize) -> u128 {
|
||||||
debug_assert!(rounds > 0);
|
debug_assert!(rounds > 0);
|
||||||
input %= PRIME;
|
input %= PRIME;
|
||||||
for r in 1..(rounds + 1) {
|
for r in 1..(rounds + 1) {
|
||||||
input = powmod::<PRIME>(input ^ (K[(rounds - r) & K_COUNT_MASK] as u128), PRIME_2P_MINUS_1_DIV_3);
|
input = powmod::<PRIME>(input ^ K[(rounds - r) & K_COUNT_MASK], PRIME_2P_MINUS_1_DIV_3);
|
||||||
}
|
}
|
||||||
input
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(mut proof: u128, expected: u128, rounds: usize) -> bool {
|
/// Quickly verify the result of delay() given the returned proof, original input, and original number of rounds.
|
||||||
|
pub fn verify(mut proof: u128, original_input: u128, rounds: usize) -> bool {
|
||||||
debug_assert!(rounds > 0);
|
debug_assert!(rounds > 0);
|
||||||
for r in 0..rounds {
|
for r in 0..rounds {
|
||||||
proof = mulmod::<PRIME>(proof, mulmod::<PRIME>(proof, proof)) ^ (K[r & K_COUNT_MASK] as u128);
|
proof = mulmod::<PRIME>(proof, mulmod::<PRIME>(proof, proof)) ^ K[r & K_COUNT_MASK];
|
||||||
}
|
}
|
||||||
proof == (expected % PRIME)
|
proof == (original_input % PRIME)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -142,6 +134,7 @@ mod tests {
|
||||||
for i in 1..5 {
|
for i in 1..5 {
|
||||||
let input = (crate::random::xorshift64_random() as u128).wrapping_mul(crate::random::xorshift64_random() as u128);
|
let input = (crate::random::xorshift64_random() as u128).wrapping_mul(crate::random::xorshift64_random() as u128);
|
||||||
let proof = delay(input, i * 3);
|
let proof = delay(input, i * 3);
|
||||||
|
//println!("{}", proof);
|
||||||
assert!(verify(proof, input, i * 3));
|
assert!(verify(proof, input, i * 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,3 @@ pub(crate) const REKEY_AFTER_TIME_MS_MAX_JITTER: u32 = 1000 * 60 * 10; // 10 min
|
||||||
|
|
||||||
/// Timeout for incoming sessions in incomplete state in milliseconds.
|
/// Timeout for incoming sessions in incomplete state in milliseconds.
|
||||||
pub(crate) const INCOMPLETE_SESSION_TIMEOUT: i64 = 1000;
|
pub(crate) const INCOMPLETE_SESSION_TIMEOUT: i64 = 1000;
|
||||||
|
|
||||||
/// Maximum number of pending incomplete sessions.
|
|
||||||
pub(crate) const INCOMPLETE_SESSION_MAX_QUEUE_SIZE: usize = 256;
|
|
||||||
|
|
|
@ -32,6 +32,9 @@ pub enum Error {
|
||||||
/// Packet ignored by rate limiter.
|
/// Packet ignored by rate limiter.
|
||||||
RateLimited,
|
RateLimited,
|
||||||
|
|
||||||
|
/// Packet counter is too far outside window.
|
||||||
|
OutOfCounterWindow,
|
||||||
|
|
||||||
/// The other peer specified an unrecognized protocol version
|
/// The other peer specified an unrecognized protocol version
|
||||||
UnknownProtocolVersion,
|
UnknownProtocolVersion,
|
||||||
|
|
||||||
|
@ -66,6 +69,7 @@ impl std::fmt::Display for Error {
|
||||||
Self::MaxKeyLifetimeExceeded => f.write_str("MaxKeyLifetimeExceeded"),
|
Self::MaxKeyLifetimeExceeded => f.write_str("MaxKeyLifetimeExceeded"),
|
||||||
Self::SessionNotEstablished => f.write_str("SessionNotEstablished"),
|
Self::SessionNotEstablished => f.write_str("SessionNotEstablished"),
|
||||||
Self::RateLimited => f.write_str("RateLimited"),
|
Self::RateLimited => f.write_str("RateLimited"),
|
||||||
|
Self::OutOfCounterWindow => f.write_str("OutOfCounterWindow"),
|
||||||
Self::UnknownProtocolVersion => f.write_str("UnknownProtocolVersion"),
|
Self::UnknownProtocolVersion => f.write_str("UnknownProtocolVersion"),
|
||||||
Self::DataBufferTooSmall => f.write_str("DataBufferTooSmall"),
|
Self::DataBufferTooSmall => f.write_str("DataBufferTooSmall"),
|
||||||
Self::DataTooLarge => f.write_str("DataTooLarge"),
|
Self::DataTooLarge => f.write_str("DataTooLarge"),
|
||||||
|
|
|
@ -37,17 +37,22 @@ pub(crate) const KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION: u8 = b'X'; // intermediat
|
||||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION: u8 = b'x'; // intermediate keys used in key exchanges
|
pub(crate) const KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION: u8 = b'x'; // intermediate keys used in key exchanges
|
||||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB: u8 = b'A'; // AES-GCM in A->B direction
|
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB: u8 = b'A'; // AES-GCM in A->B direction
|
||||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE: u8 = b'B'; // AES-GCM in B->A direction
|
pub(crate) const KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE: u8 = b'B'; // AES-GCM in B->A direction
|
||||||
|
pub(crate) const KBKDF_KEY_USAGE_LABEL_RATCHET: u8 = b'R'; // Key used in derivatin of next session key
|
||||||
|
|
||||||
pub(crate) const MAX_FRAGMENTS: usize = 48; // hard protocol max: 63
|
pub(crate) const MAX_FRAGMENTS: usize = 48; // hard protocol max: 63
|
||||||
pub(crate) const MAX_NOISE_HANDSHAKE_FRAGMENTS: usize = 16; // enough room for p384 + ZT identity + kyber1024 + tag/hmac/etc.
|
pub(crate) const MAX_NOISE_HANDSHAKE_FRAGMENTS: usize = 16; // enough room for p384 + ZT identity + kyber1024 + tag/hmac/etc.
|
||||||
pub(crate) const MAX_NOISE_HANDSHAKE_SIZE: usize = MAX_NOISE_HANDSHAKE_FRAGMENTS * MIN_TRANSPORT_MTU;
|
pub(crate) const MAX_NOISE_HANDSHAKE_SIZE: usize = MAX_NOISE_HANDSHAKE_FRAGMENTS * MIN_TRANSPORT_MTU;
|
||||||
|
|
||||||
|
pub(crate) const BASE_KEY_SIZE: usize = 64;
|
||||||
|
|
||||||
pub(crate) const AES_KEY_SIZE: usize = 32;
|
pub(crate) const AES_KEY_SIZE: usize = 32;
|
||||||
pub(crate) const AES_HEADER_CHECK_KEY_SIZE: usize = 16;
|
pub(crate) const AES_HEADER_CHECK_KEY_SIZE: usize = 16;
|
||||||
pub(crate) const AES_GCM_TAG_SIZE: usize = 16;
|
pub(crate) const AES_GCM_TAG_SIZE: usize = 16;
|
||||||
pub(crate) const AES_GCM_NONCE_SIZE: usize = 12;
|
pub(crate) const AES_GCM_NONCE_SIZE: usize = 12;
|
||||||
pub(crate) const AES_CTR_NONCE_SIZE: usize = 12;
|
pub(crate) const AES_CTR_NONCE_SIZE: usize = 12;
|
||||||
|
|
||||||
|
/// The first packet in Noise_XK exchange containing Alice's ephemeral keys, session ID, and a random
|
||||||
|
/// symmetric key to protect header fragmentation fields for this session.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(crate) struct AliceNoiseXKInit {
|
pub(crate) struct AliceNoiseXKInit {
|
||||||
|
@ -68,6 +73,7 @@ impl AliceNoiseXKInit {
|
||||||
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
|
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The response to AliceNoiceXKInit containing Bob's ephemeral keys.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub(crate) struct BobNoiseXKAck {
|
pub(crate) struct BobNoiseXKAck {
|
||||||
|
@ -87,6 +93,7 @@ impl BobNoiseXKAck {
|
||||||
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
|
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Alice's final response containing her identity (she already knows Bob's) and meta-data.
|
||||||
/*
|
/*
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
|
|
118
zssp/src/zssp.rs
118
zssp/src/zssp.rs
|
@ -20,6 +20,7 @@ use zerotier_crypto::p384::{P384KeyPair, P384PublicKey, P384_PUBLIC_KEY_SIZE};
|
||||||
use zerotier_crypto::secret::Secret;
|
use zerotier_crypto::secret::Secret;
|
||||||
use zerotier_crypto::{random, secure_eq};
|
use zerotier_crypto::{random, secure_eq};
|
||||||
|
|
||||||
|
use zerotier_utils::arrayvec::ArrayVec;
|
||||||
use zerotier_utils::gatherarray::GatherArray;
|
use zerotier_utils::gatherarray::GatherArray;
|
||||||
use zerotier_utils::memory;
|
use zerotier_utils::memory;
|
||||||
use zerotier_utils::ringbuffermap::RingBufferMap;
|
use zerotier_utils::ringbuffermap::RingBufferMap;
|
||||||
|
@ -37,8 +38,9 @@ use crate::sessionid::SessionId;
|
||||||
/// Each application using ZSSP must create an instance of this to own sessions and
|
/// Each application using ZSSP must create an instance of this to own sessions and
|
||||||
/// defragment incoming packets that are not yet associated with a session.
|
/// defragment incoming packets that are not yet associated with a session.
|
||||||
pub struct Context<Application: ApplicationLayer> {
|
pub struct Context<Application: ApplicationLayer> {
|
||||||
|
max_incomplete_session_queue_size: usize,
|
||||||
initial_offer_defrag:
|
initial_offer_defrag:
|
||||||
Mutex<RingBufferMap<u64, GatherArray<Application::IncomingPacketBuffer, MAX_NOISE_HANDSHAKE_FRAGMENTS>, 1024, 1024>>,
|
Mutex<RingBufferMap<u64, GatherArray<Application::IncomingPacketBuffer, MAX_NOISE_HANDSHAKE_FRAGMENTS>, 256, 256>>,
|
||||||
sessions: RwLock<SessionMaps<Application>>,
|
sessions: RwLock<SessionMaps<Application>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,13 +52,13 @@ pub enum ReceiveResult<'b, Application: ApplicationLayer> {
|
||||||
/// Packet was valid and a data payload was decoded and authenticated.
|
/// Packet was valid and a data payload was decoded and authenticated.
|
||||||
OkData(Arc<Session<Application>>, &'b mut [u8]),
|
OkData(Arc<Session<Application>>, &'b mut [u8]),
|
||||||
|
|
||||||
/// Packet was valid and a new session was created.
|
/// Packet was valid and a new session was created, with static public blob and optional meta-data.
|
||||||
OkNewSession(Arc<Session<Application>>),
|
OkNewSession(Arc<Session<Application>>, &'b [u8], Option<&'b [u8]>),
|
||||||
|
|
||||||
/// Packet appears valid but was ignored e.g. as a duplicate.
|
/// Packet appears valid but was ignored as a duplicate or as meaningless given the current state.
|
||||||
Ignored,
|
Ignored,
|
||||||
|
|
||||||
/// Packet appears valid but new session was rejected by application layer.
|
/// Packet appears valid but was rejected by the application layer, e.g. a rejected new session attempt.
|
||||||
Rejected,
|
Rejected,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +72,7 @@ pub struct Session<Application: ApplicationLayer> {
|
||||||
/// An arbitrary application defined object associated with each session
|
/// An arbitrary application defined object associated with each session
|
||||||
pub application_data: Application::Data,
|
pub application_data: Application::Data,
|
||||||
|
|
||||||
psk: Secret<64>,
|
psk: Secret<BASE_KEY_SIZE>,
|
||||||
send_counter: AtomicU64,
|
send_counter: AtomicU64,
|
||||||
receive_window: [AtomicU64; COUNTER_WINDOW_MAX_OOO],
|
receive_window: [AtomicU64; COUNTER_WINDOW_MAX_OOO],
|
||||||
header_check_cipher: Aes,
|
header_check_cipher: Aes,
|
||||||
|
@ -92,12 +94,13 @@ struct NoiseXKIncoming {
|
||||||
timestamp: i64,
|
timestamp: i64,
|
||||||
alice_session_id: SessionId,
|
alice_session_id: SessionId,
|
||||||
bob_session_id: SessionId,
|
bob_session_id: SessionId,
|
||||||
noise_es_ee: Secret<64>,
|
noise_es_ee: Secret<BASE_KEY_SIZE>,
|
||||||
hk: Secret<KYBER_SSBYTES>,
|
hk: Secret<KYBER_SSBYTES>,
|
||||||
header_check_cipher_key: Secret<AES_HEADER_CHECK_KEY_SIZE>,
|
header_check_cipher_key: Secret<AES_HEADER_CHECK_KEY_SIZE>,
|
||||||
bob_noise_e_secret: P384KeyPair,
|
bob_noise_e_secret: P384KeyPair,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// State that needs to be cached for the most recent outgoing offer.
|
||||||
enum EphemeralOffer {
|
enum EphemeralOffer {
|
||||||
None,
|
None,
|
||||||
NoiseXKInit(
|
NoiseXKInit(
|
||||||
|
@ -105,7 +108,7 @@ enum EphemeralOffer {
|
||||||
Box<(
|
Box<(
|
||||||
// alice_e_secret, metadata, noise_es, alice_hk_public, alice_hk_secret, header check key
|
// alice_e_secret, metadata, noise_es, alice_hk_public, alice_hk_secret, header check key
|
||||||
P384KeyPair,
|
P384KeyPair,
|
||||||
Option<Vec<u8>>,
|
Option<ArrayVec<u8, MAX_METADATA_SIZE>>,
|
||||||
Secret<48>,
|
Secret<48>,
|
||||||
Secret<KYBER_SECRETKEYBYTES>,
|
Secret<KYBER_SECRETKEYBYTES>,
|
||||||
)>,
|
)>,
|
||||||
|
@ -113,13 +116,16 @@ enum EphemeralOffer {
|
||||||
RekeyInit(P384KeyPair),
|
RekeyInit(P384KeyPair),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Other mutable state within the session.
|
||||||
struct State {
|
struct State {
|
||||||
remote_session_id: Option<SessionId>,
|
remote_session_id: Option<SessionId>,
|
||||||
keys: [Option<SessionKey>; 2],
|
keys: [Option<SessionKey>; 2],
|
||||||
current_key: usize,
|
current_key: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A session key with lifetime information.
|
||||||
struct SessionKey {
|
struct SessionKey {
|
||||||
|
ratchet_key: Secret<BASE_KEY_SIZE>, // Key used in derivation of the next session key
|
||||||
receive_key: Secret<AES_KEY_SIZE>, // Receive side AES-GCM key
|
receive_key: Secret<AES_KEY_SIZE>, // Receive side AES-GCM key
|
||||||
send_key: Secret<AES_KEY_SIZE>, // Send side AES-GCM key
|
send_key: Secret<AES_KEY_SIZE>, // Send side AES-GCM key
|
||||||
receive_cipher_pool: Mutex<Vec<Box<AesGcm>>>, // Pool of reusable sending ciphers
|
receive_cipher_pool: Mutex<Vec<Box<AesGcm>>>, // Pool of reusable sending ciphers
|
||||||
|
@ -134,12 +140,13 @@ struct SessionKey {
|
||||||
|
|
||||||
impl<Application: ApplicationLayer> Context<Application> {
|
impl<Application: ApplicationLayer> Context<Application> {
|
||||||
/// Create a new session context.
|
/// Create a new session context.
|
||||||
pub fn new(_: &Application) -> Self {
|
pub fn new(_: &Application, max_incomplete_session_queue_size: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
max_incomplete_session_queue_size,
|
||||||
initial_offer_defrag: Mutex::new(RingBufferMap::new(random::next_u32_secure())),
|
initial_offer_defrag: Mutex::new(RingBufferMap::new(random::next_u32_secure())),
|
||||||
sessions: RwLock::new(SessionMaps {
|
sessions: RwLock::new(SessionMaps {
|
||||||
active: HashMap::with_capacity(64),
|
active: HashMap::with_capacity(64),
|
||||||
incomplete: HashMap::with_capacity(16),
|
incomplete: HashMap::with_capacity(64),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +203,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
mtu: usize,
|
mtu: usize,
|
||||||
remote_s_public_blob: &[u8],
|
remote_s_public_blob: &[u8],
|
||||||
metadata: Option<&[u8]>,
|
metadata: Option<&[u8]>,
|
||||||
psk: Secret<64>,
|
psk: Secret<BASE_KEY_SIZE>,
|
||||||
application_data: Application::Data,
|
application_data: Application::Data,
|
||||||
) -> Result<Arc<Session<Application>>, Error> {
|
) -> Result<Arc<Session<Application>>, Error> {
|
||||||
if let Some(md) = metadata.as_ref() {
|
if let Some(md) = metadata.as_ref() {
|
||||||
|
@ -232,7 +239,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
header_check_cipher: Aes::new(&header_check_cipher_key),
|
header_check_cipher: Aes::new(&header_check_cipher_key),
|
||||||
offer: Mutex::new(EphemeralOffer::NoiseXKInit(Box::new((
|
offer: Mutex::new(EphemeralOffer::NoiseXKInit(Box::new((
|
||||||
alice_noise_e_secret,
|
alice_noise_e_secret,
|
||||||
metadata.map(|md| md.to_vec()),
|
metadata.map(|md| ArrayVec::try_from(md).unwrap()),
|
||||||
noise_es.clone(),
|
noise_es.clone(),
|
||||||
Secret(alice_hk_secret.secret),
|
Secret(alice_hk_secret.secret),
|
||||||
)))),
|
)))),
|
||||||
|
@ -278,29 +285,18 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
/// wtth an active session this session is supplied, otherwise this parameter is None. The size
|
/// wtth an active session this session is supplied, otherwise this parameter is None. The size
|
||||||
/// of packets to be sent will not exceed the supplied mtu.
|
/// of packets to be sent will not exceed the supplied mtu.
|
||||||
///
|
///
|
||||||
/// New sessions can be accepted or rejected at both the initial negotiation phase and the final
|
|
||||||
/// negotiation phase using the incoming session filter function. For the initial phase of Noise_XK
|
|
||||||
/// the function will be called with None as a parameter since we do not yet know the static identity
|
|
||||||
/// or meta-data associated with the connection attempt. In the final phase the function will be called
|
|
||||||
/// again with the static public identity blob of the initiating endpoint and optionally any meta-data
|
|
||||||
/// that was supplied. In both cases a return value of false causes abandonment of the session.
|
|
||||||
///
|
|
||||||
/// * `app` - Interface to application using ZSSP
|
/// * `app` - Interface to application using ZSSP
|
||||||
/// * `incoming_session_filter` - Function to call to check whether new sessions should be accepted
|
/// * `check_allow_incoming_session` - Function to call to check whether an unidentified new session should be accepted
|
||||||
/// * `send` - Function to call to send packets
|
/// * `send` - Function to call to send packets
|
||||||
/// * `data_buf` - Buffer to receive decrypted and authenticated object data (an error is returned if too small)
|
/// * `data_buf` - Buffer to receive decrypted and authenticated object data (an error is returned if too small)
|
||||||
/// * `incoming_packet_buf` - Buffer containing incoming wire packet (receive() takes ownership)
|
/// * `incoming_packet_buf` - Buffer containing incoming wire packet (receive() takes ownership)
|
||||||
/// * `mtu` - Physical wire MTU for sending packets
|
/// * `mtu` - Physical wire MTU for sending packets
|
||||||
/// * `current_time` - Current monotonic time in milliseconds
|
/// * `current_time` - Current monotonic time in milliseconds
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn receive<
|
pub fn receive<'b, SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]), CheckAllowIncomingSession: FnMut() -> bool>(
|
||||||
'b,
|
|
||||||
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
|
||||||
PermitIncomingSession: FnMut(Option<&[u8]>, Option<&[u8]>) -> bool,
|
|
||||||
>(
|
|
||||||
&self,
|
&self,
|
||||||
app: &Application,
|
app: &Application,
|
||||||
mut incoming_session_filter: PermitIncomingSession,
|
mut check_allow_incoming_session: CheckAllowIncomingSession,
|
||||||
mut send: SendFunction,
|
mut send: SendFunction,
|
||||||
data_buf: &'b mut [u8],
|
data_buf: &'b mut [u8],
|
||||||
mut incoming_packet_buf: Application::IncomingPacketBuffer,
|
mut incoming_packet_buf: Application::IncomingPacketBuffer,
|
||||||
|
@ -330,7 +326,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
return self.receive_complete(
|
return self.receive_complete(
|
||||||
app,
|
app,
|
||||||
&mut send,
|
&mut send,
|
||||||
&mut incoming_session_filter,
|
&mut check_allow_incoming_session,
|
||||||
data_buf,
|
data_buf,
|
||||||
counter,
|
counter,
|
||||||
assembled_packet.as_ref(),
|
assembled_packet.as_ref(),
|
||||||
|
@ -351,7 +347,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
return self.receive_complete(
|
return self.receive_complete(
|
||||||
app,
|
app,
|
||||||
&mut send,
|
&mut send,
|
||||||
&mut incoming_session_filter,
|
&mut check_allow_incoming_session,
|
||||||
data_buf,
|
data_buf,
|
||||||
counter,
|
counter,
|
||||||
&[incoming_packet_buf],
|
&[incoming_packet_buf],
|
||||||
|
@ -364,7 +360,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(ReceiveResult::Ignored);
|
return Err(Error::OutOfCounterWindow);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(p) = self.sessions.read().unwrap().incomplete.get(&local_session_id).cloned() {
|
if let Some(p) = self.sessions.read().unwrap().incomplete.get(&local_session_id).cloned() {
|
||||||
|
@ -389,7 +385,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
return self.receive_complete(
|
return self.receive_complete(
|
||||||
app,
|
app,
|
||||||
&mut send,
|
&mut send,
|
||||||
&mut incoming_session_filter,
|
&mut check_allow_incoming_session,
|
||||||
data_buf,
|
data_buf,
|
||||||
counter,
|
counter,
|
||||||
assembled_packet.as_ref(),
|
assembled_packet.as_ref(),
|
||||||
|
@ -405,7 +401,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
return self.receive_complete(
|
return self.receive_complete(
|
||||||
app,
|
app,
|
||||||
&mut send,
|
&mut send,
|
||||||
&mut incoming_session_filter,
|
&mut check_allow_incoming_session,
|
||||||
data_buf,
|
data_buf,
|
||||||
counter,
|
counter,
|
||||||
&[incoming_packet_buf],
|
&[incoming_packet_buf],
|
||||||
|
@ -424,12 +420,12 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
fn receive_complete<
|
fn receive_complete<
|
||||||
'b,
|
'b,
|
||||||
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
||||||
PermitIncomingSession: FnMut(Option<&[u8]>, Option<&[u8]>) -> bool,
|
CheckAllowIncomingSession: FnMut() -> bool,
|
||||||
>(
|
>(
|
||||||
&self,
|
&self,
|
||||||
app: &Application,
|
app: &Application,
|
||||||
send: &mut SendFunction,
|
send: &mut SendFunction,
|
||||||
incoming_session_filter: &mut PermitIncomingSession,
|
check_allow_incoming_session: &mut CheckAllowIncomingSession,
|
||||||
data_buf: &'b mut [u8],
|
data_buf: &'b mut [u8],
|
||||||
counter: u64,
|
counter: u64,
|
||||||
fragments: &[Application::IncomingPacketBuffer],
|
fragments: &[Application::IncomingPacketBuffer],
|
||||||
|
@ -511,7 +507,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
return Ok(ReceiveResult::OkData(session, &mut data_buf[..data_len]));
|
return Ok(ReceiveResult::OkData(session, &mut data_buf[..data_len]));
|
||||||
} else {
|
} else {
|
||||||
return Ok(ReceiveResult::Ignored);
|
return Err(Error::OutOfCounterWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,9 +547,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
* to the current exchange.
|
* to the current exchange.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// There shouldn't be a session yet on Bob's end, and this should be the first packet.
|
|
||||||
if session.is_some() || counter != 1 {
|
if session.is_some() || counter != 1 {
|
||||||
return Ok(ReceiveResult::Ignored);
|
return Err(Error::OutOfCounterWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?;
|
let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?;
|
||||||
|
@ -572,11 +567,12 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
return Err(Error::FailedAuthentication);
|
return Err(Error::FailedAuthentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !incoming_session_filter(None, None) {
|
// Let application filter incoming connection attempt by whatever criteria it wants.
|
||||||
|
if !check_allow_incoming_session() {
|
||||||
return Ok(ReceiveResult::Rejected);
|
return Ok(ReceiveResult::Rejected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt encrypted part of payload (already authenticated above).
|
// Decrypt encrypted part of payload.
|
||||||
let mut ctr = AesCtr::new(kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION>(noise_es.as_bytes()).as_bytes());
|
let mut ctr = AesCtr::new(kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ENCRYPTION>(noise_es.as_bytes()).as_bytes());
|
||||||
ctr.reset_set_iv(&SHA384::hash(&pkt.alice_noise_e)[..AES_CTR_NONCE_SIZE]);
|
ctr.reset_set_iv(&SHA384::hash(&pkt.alice_noise_e)[..AES_CTR_NONCE_SIZE]);
|
||||||
ctr.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
ctr.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
||||||
|
@ -611,19 +607,23 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sessions.incomplete.len() >= INCOMPLETE_SESSION_MAX_QUEUE_SIZE {
|
if sessions.incomplete.len() >= self.max_incomplete_session_queue_size {
|
||||||
// If this queue is too big, we remove the latest entry and replace it. The latest
|
// If this queue is too big, we remove the latest entry and replace it. The latest
|
||||||
// is used because under flood conditions this is most likely to be another bogus
|
// is used because under flood conditions this is most likely to be another bogus
|
||||||
// entry.
|
// entry. If we find one that is actually timed out, that always gets replaced.
|
||||||
let mut newest = i64::MIN;
|
let mut newest = i64::MIN;
|
||||||
let mut newest_id = None;
|
let mut replace_id = None;
|
||||||
|
let cutoff_time = current_time - INCOMPLETE_SESSION_TIMEOUT;
|
||||||
for (id, s) in sessions.incomplete.iter() {
|
for (id, s) in sessions.incomplete.iter() {
|
||||||
if s.timestamp >= newest {
|
if s.timestamp <= cutoff_time {
|
||||||
|
replace_id = Some(*id);
|
||||||
|
break;
|
||||||
|
} else if s.timestamp >= newest {
|
||||||
newest = s.timestamp;
|
newest = s.timestamp;
|
||||||
newest_id = Some(*id);
|
replace_id = Some(*id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = sessions.incomplete.remove(newest_id.as_ref().unwrap());
|
let _ = sessions.incomplete.remove(replace_id.as_ref().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
sessions.incomplete.insert(
|
sessions.incomplete.insert(
|
||||||
|
@ -688,11 +688,13 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if counter != 1 {
|
if counter != 1 {
|
||||||
return Ok(ReceiveResult::Ignored);
|
return Err(Error::OutOfCounterWindow);
|
||||||
} else if let Some(session) = session {
|
}
|
||||||
match std::mem::replace(&mut *session.offer.lock().unwrap(), EphemeralOffer::None) {
|
|
||||||
EphemeralOffer::NoiseXKInit(mut boxed_offer) => {
|
if let Some(session) = session {
|
||||||
let (alice_e_secret, metadata, noise_es, alice_hk_secret) = boxed_offer.as_mut();
|
let mut offer = session.offer.lock().unwrap();
|
||||||
|
if let EphemeralOffer::NoiseXKInit(boxed_offer) = &*offer {
|
||||||
|
let (alice_e_secret, metadata, noise_es, alice_hk_secret) = boxed_offer.as_ref();
|
||||||
let pkt: &BobNoiseXKAck = byte_array_as_proto_buffer(pkt_assembled)?;
|
let pkt: &BobNoiseXKAck = byte_array_as_proto_buffer(pkt_assembled)?;
|
||||||
|
|
||||||
if let Some(bob_session_id) = SessionId::new_from_bytes(&pkt.bob_session_id) {
|
if let Some(bob_session_id) = SessionId::new_from_bytes(&pkt.bob_session_id) {
|
||||||
|
@ -743,9 +745,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
&hmac_sha512(session.psk.as_bytes(), hk.as_bytes()),
|
&hmac_sha512(session.psk.as_bytes(), hk.as_bytes()),
|
||||||
));
|
));
|
||||||
|
|
||||||
let noise_es_ee_se_hk_psk_hmac_key = kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION>(
|
let noise_es_ee_se_hk_psk_hmac_key =
|
||||||
noise_es_ee_se_hk_psk.as_bytes(),
|
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION>(noise_es_ee_se_hk_psk.as_bytes());
|
||||||
);
|
|
||||||
|
|
||||||
let reply_counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?;
|
let reply_counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?;
|
||||||
let reply_message_nonce = create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_ACK, reply_counter.get());
|
let reply_message_nonce = create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_ACK, reply_counter.get());
|
||||||
|
@ -767,7 +768,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
reply_buffer_append(alice_s_public_blob);
|
reply_buffer_append(alice_s_public_blob);
|
||||||
if let Some(md) = metadata.as_ref() {
|
if let Some(md) = metadata.as_ref() {
|
||||||
reply_buffer_append(&(md.len() as u16).to_le_bytes());
|
reply_buffer_append(&(md.len() as u16).to_le_bytes());
|
||||||
reply_buffer_append(md.as_slice());
|
reply_buffer_append(md.as_ref());
|
||||||
} else {
|
} else {
|
||||||
reply_buffer_append(&[0u8, 0u8]); // no meta-data
|
reply_buffer_append(&[0u8, 0u8]); // no meta-data
|
||||||
}
|
}
|
||||||
|
@ -800,6 +801,10 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
reply_buffer[reply_len..reply_len + HMAC_SHA384_SIZE].copy_from_slice(&hmac_es_ee_se_hk_psk);
|
reply_buffer[reply_len..reply_len + HMAC_SHA384_SIZE].copy_from_slice(&hmac_es_ee_se_hk_psk);
|
||||||
reply_len += HMAC_SHA384_SIZE;
|
reply_len += HMAC_SHA384_SIZE;
|
||||||
|
|
||||||
|
// Clear the offer field since we're finished handling a response to our initial offer.
|
||||||
|
*offer = EphemeralOffer::None;
|
||||||
|
drop(offer);
|
||||||
|
|
||||||
// Learn Bob's session ID and the first session key.
|
// Learn Bob's session ID and the first session key.
|
||||||
{
|
{
|
||||||
let mut state = session.state.write().unwrap();
|
let mut state = session.state.write().unwrap();
|
||||||
|
@ -829,8 +834,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::InvalidPacket);
|
return Err(Error::InvalidPacket);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
_ => return Ok(ReceiveResult::Ignored),
|
return Ok(ReceiveResult::Ignored);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::SessionNotEstablished);
|
return Err(Error::SessionNotEstablished);
|
||||||
|
@ -1166,7 +1171,7 @@ fn send_with_fragmentation<SendFunction: FnMut(&mut [u8])>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionKey {
|
impl SessionKey {
|
||||||
fn new(key: Secret<64>, current_time: i64, current_counter: u64, confirmed: bool, role_is_bob: bool) -> Self {
|
fn new(key: Secret<BASE_KEY_SIZE>, current_time: i64, current_counter: u64, confirmed: bool, role_is_bob: bool) -> Self {
|
||||||
let a2b = kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB>(key.as_bytes());
|
let a2b = kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB>(key.as_bytes());
|
||||||
let b2a = kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE>(key.as_bytes());
|
let b2a = kbkdf::<AES_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE>(key.as_bytes());
|
||||||
let (receive_key, send_key) = if role_is_bob {
|
let (receive_key, send_key) = if role_is_bob {
|
||||||
|
@ -1175,6 +1180,7 @@ impl SessionKey {
|
||||||
(b2a, a2b)
|
(b2a, a2b)
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
|
ratchet_key: kbkdf::<BASE_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_RATCHET>(key.as_bytes()),
|
||||||
receive_key,
|
receive_key,
|
||||||
send_key,
|
send_key,
|
||||||
receive_cipher_pool: Mutex::new(Vec::with_capacity(2)),
|
receive_cipher_pool: Mutex::new(Vec::with_capacity(2)),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue