mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-07 13:03:45 +02:00
Make ZSSP use just GCM to simplify, and change final ACK to auth and encrypt public blob separately from meta-data.
This commit is contained in:
parent
e66477c168
commit
757cc88abc
3 changed files with 165 additions and 207 deletions
|
@ -80,7 +80,7 @@ fn alice_main(
|
||||||
match context.receive(
|
match context.receive(
|
||||||
alice_app,
|
alice_app,
|
||||||
|| true,
|
|| true,
|
||||||
|s_public, _| Some((P384PublicKey::from_bytes(s_public).unwrap(), Secret::default(), ())),
|
|s_public| Some((P384PublicKey::from_bytes(s_public).unwrap(), Secret::default(), ())),
|
||||||
|_, b| {
|
|_, b| {
|
||||||
let _ = alice_out.send(b.to_vec());
|
let _ = alice_out.send(b.to_vec());
|
||||||
},
|
},
|
||||||
|
@ -96,7 +96,7 @@ fn alice_main(
|
||||||
Ok(zssp::ReceiveResult::OkData(_, _)) => {
|
Ok(zssp::ReceiveResult::OkData(_, _)) => {
|
||||||
//println!("[alice] received {}", data.len());
|
//println!("[alice] received {}", data.len());
|
||||||
}
|
}
|
||||||
Ok(zssp::ReceiveResult::OkNewSession(s)) => {
|
Ok(zssp::ReceiveResult::OkNewSession(s, _)) => {
|
||||||
println!("[alice] new session {}", s.id.to_string());
|
println!("[alice] new session {}", s.id.to_string());
|
||||||
}
|
}
|
||||||
Ok(zssp::ReceiveResult::Rejected) => {}
|
Ok(zssp::ReceiveResult::Rejected) => {}
|
||||||
|
@ -178,7 +178,7 @@ fn bob_main(
|
||||||
match context.receive(
|
match context.receive(
|
||||||
bob_app,
|
bob_app,
|
||||||
|| true,
|
|| true,
|
||||||
|s_public, _| Some((P384PublicKey::from_bytes(s_public).unwrap(), Secret::default(), ())),
|
|s_public| Some((P384PublicKey::from_bytes(s_public).unwrap(), Secret::default(), ())),
|
||||||
|_, b| {
|
|_, b| {
|
||||||
let _ = bob_out.send(b.to_vec());
|
let _ = bob_out.send(b.to_vec());
|
||||||
},
|
},
|
||||||
|
@ -204,7 +204,7 @@ fn bob_main(
|
||||||
.is_ok());
|
.is_ok());
|
||||||
transferred += data.len() as u64 * 2; // *2 because we are also sending this many bytes back
|
transferred += data.len() as u64 * 2; // *2 because we are also sending this many bytes back
|
||||||
}
|
}
|
||||||
Ok(zssp::ReceiveResult::OkNewSession(s)) => {
|
Ok(zssp::ReceiveResult::OkNewSession(s, _)) => {
|
||||||
println!("[bob] new session {}", s.id.to_string());
|
println!("[bob] new session {}", s.id.to_string());
|
||||||
let _ = bob_session.replace(s);
|
let _ = bob_session.replace(s);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
use pqc_kyber::{KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES};
|
use pqc_kyber::{KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES};
|
||||||
use zerotier_crypto::hash::{HMAC_SHA384_SIZE, SHA384_HASH_SIZE};
|
use zerotier_crypto::hash::SHA384_HASH_SIZE;
|
||||||
use zerotier_crypto::p384::P384_PUBLIC_KEY_SIZE;
|
use zerotier_crypto::p384::P384_PUBLIC_KEY_SIZE;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
@ -45,8 +45,7 @@ pub(crate) const HEADER_SIZE: usize = 16;
|
||||||
pub(crate) const HEADER_PROTECT_ENCRYPT_START: usize = 6;
|
pub(crate) const HEADER_PROTECT_ENCRYPT_START: usize = 6;
|
||||||
pub(crate) const HEADER_PROTECT_ENCRYPT_END: usize = 22;
|
pub(crate) const HEADER_PROTECT_ENCRYPT_END: usize = 22;
|
||||||
|
|
||||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION: u8 = b'x'; // AES-CTR encryption during initial setup
|
pub(crate) const KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION: u8 = b'x'; // AES-GCM encryption during initial setup
|
||||||
pub(crate) const KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION: u8 = b'X'; // HMAC-SHA384 during initial setup
|
|
||||||
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 KBKDF_KEY_USAGE_LABEL_RATCHET: u8 = b'R'; // Key used in derivatin of next session key
|
||||||
|
@ -71,18 +70,18 @@ pub(crate) struct AliceNoiseXKInit {
|
||||||
pub header: [u8; HEADER_SIZE],
|
pub header: [u8; HEADER_SIZE],
|
||||||
pub session_protocol_version: u8,
|
pub session_protocol_version: u8,
|
||||||
pub alice_noise_e: [u8; P384_PUBLIC_KEY_SIZE],
|
pub alice_noise_e: [u8; P384_PUBLIC_KEY_SIZE],
|
||||||
// -- start AES-CTR(es) encrypted section
|
// -- start AES-GCM(es) encrypted section
|
||||||
pub alice_session_id: [u8; SessionId::SIZE],
|
pub alice_session_id: [u8; SessionId::SIZE],
|
||||||
pub alice_hk_public: [u8; KYBER_PUBLICKEYBYTES],
|
pub alice_hk_public: [u8; KYBER_PUBLICKEYBYTES],
|
||||||
pub header_protection_key: [u8; AES_HEADER_PROTECTION_KEY_SIZE],
|
pub header_protection_key: [u8; AES_HEADER_PROTECTION_KEY_SIZE],
|
||||||
// -- end encrypted section
|
// -- end encrypted section
|
||||||
pub hmac_es: [u8; HMAC_SHA384_SIZE],
|
pub gcm_tag: [u8; AES_GCM_TAG_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AliceNoiseXKInit {
|
impl AliceNoiseXKInit {
|
||||||
pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
|
pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
|
||||||
pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_PUBLICKEYBYTES + AES_HEADER_PROTECTION_KEY_SIZE;
|
pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_PUBLICKEYBYTES + AES_HEADER_PROTECTION_KEY_SIZE;
|
||||||
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
|
pub const SIZE: usize = Self::AUTH_START + AES_GCM_TAG_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The response to AliceNoiceXKInit containing Bob's ephemeral keys.
|
/// The response to AliceNoiceXKInit containing Bob's ephemeral keys.
|
||||||
|
@ -92,17 +91,17 @@ pub(crate) struct BobNoiseXKAck {
|
||||||
pub header: [u8; HEADER_SIZE],
|
pub header: [u8; HEADER_SIZE],
|
||||||
pub session_protocol_version: u8,
|
pub session_protocol_version: u8,
|
||||||
pub bob_noise_e: [u8; P384_PUBLIC_KEY_SIZE],
|
pub bob_noise_e: [u8; P384_PUBLIC_KEY_SIZE],
|
||||||
// -- start AES-CTR(es_ee) encrypted section
|
// -- start AES-GCM(es_ee) encrypted section
|
||||||
pub bob_session_id: [u8; SessionId::SIZE],
|
pub bob_session_id: [u8; SessionId::SIZE],
|
||||||
pub bob_hk_ciphertext: [u8; KYBER_CIPHERTEXTBYTES],
|
pub bob_hk_ciphertext: [u8; KYBER_CIPHERTEXTBYTES],
|
||||||
// -- end encrypted sectiion
|
// -- end encrypted sectiion
|
||||||
pub hmac_es_ee: [u8; HMAC_SHA384_SIZE],
|
pub gcm_tag: [u8; AES_GCM_TAG_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BobNoiseXKAck {
|
impl BobNoiseXKAck {
|
||||||
pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
|
pub const ENC_START: usize = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE;
|
||||||
pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_CIPHERTEXTBYTES;
|
pub const AUTH_START: usize = Self::ENC_START + SessionId::SIZE + KYBER_CIPHERTEXTBYTES;
|
||||||
pub const SIZE: usize = Self::AUTH_START + HMAC_SHA384_SIZE;
|
pub const SIZE: usize = Self::AUTH_START + AES_GCM_TAG_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alice's final response containing her identity (she already knows Bob's) and meta-data.
|
/// Alice's final response containing her identity (she already knows Bob's) and meta-data.
|
||||||
|
@ -112,20 +111,20 @@ impl BobNoiseXKAck {
|
||||||
pub(crate) struct AliceNoiseXKAck {
|
pub(crate) struct AliceNoiseXKAck {
|
||||||
pub header: [u8; HEADER_SIZE],
|
pub header: [u8; HEADER_SIZE],
|
||||||
pub session_protocol_version: u8,
|
pub session_protocol_version: u8,
|
||||||
// -- start AES-CTR(es_ee_hk) encrypted section
|
|
||||||
pub alice_static_blob_length: [u8; 2],
|
pub alice_static_blob_length: [u8; 2],
|
||||||
|
// -- start AES-GCM(es_ee_hk) encrypted section
|
||||||
pub alice_static_blob: [u8; ???],
|
pub alice_static_blob: [u8; ???],
|
||||||
|
// -- end encrypted section
|
||||||
|
pub gcm_tag_0: [u8; AES_GCM_TAG_SIZE],
|
||||||
pub alice_metadata_length: [u8; 2],
|
pub alice_metadata_length: [u8; 2],
|
||||||
|
// -- start AES-GCM(es_ee_se_hk_psk) encrypted section
|
||||||
pub alice_metadata: [u8; ???],
|
pub alice_metadata: [u8; ???],
|
||||||
// -- end encrypted section
|
// -- end encrypted section
|
||||||
pub hmac_es_ee: [u8; HMAC_SHA384_SIZE],
|
pub gcm_tag_1: [u8; AES_GCM_TAG_SIZE],
|
||||||
pub hmac_es_ee_se_hk_psk: [u8; HMAC_SHA384_SIZE],
|
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) const ALICE_NOISE_XK_ACK_ENC_START: usize = HEADER_SIZE + 1;
|
pub(crate) const ALICE_NOISE_XK_ACK_MIN_SIZE: usize = HEADER_SIZE + 1 + 2 + AES_GCM_TAG_SIZE + 2 + AES_GCM_TAG_SIZE;
|
||||||
pub(crate) const ALICE_NOISE_XK_ACK_AUTH_SIZE: usize = HMAC_SHA384_SIZE + HMAC_SHA384_SIZE;
|
|
||||||
pub(crate) const ALICE_NOISE_XK_ACK_MIN_SIZE: usize = ALICE_NOISE_XK_ACK_ENC_START + 2 + 2 + ALICE_NOISE_XK_ACK_AUTH_SIZE;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
|
@ -135,7 +134,7 @@ pub(crate) struct RekeyInit {
|
||||||
// -- start AES-GCM encrypted portion (using current key)
|
// -- start AES-GCM encrypted portion (using current key)
|
||||||
pub alice_e: [u8; P384_PUBLIC_KEY_SIZE],
|
pub alice_e: [u8; P384_PUBLIC_KEY_SIZE],
|
||||||
// -- end AES-GCM encrypted portion
|
// -- end AES-GCM encrypted portion
|
||||||
pub gcm_mac: [u8; AES_GCM_TAG_SIZE],
|
pub gcm_tag: [u8; AES_GCM_TAG_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RekeyInit {
|
impl RekeyInit {
|
||||||
|
@ -151,9 +150,9 @@ pub(crate) struct RekeyAck {
|
||||||
pub session_protocol_version: u8,
|
pub session_protocol_version: u8,
|
||||||
// -- start AES-GCM encrypted portion (using current key)
|
// -- start AES-GCM encrypted portion (using current key)
|
||||||
pub bob_e: [u8; P384_PUBLIC_KEY_SIZE],
|
pub bob_e: [u8; P384_PUBLIC_KEY_SIZE],
|
||||||
pub next_key_fingerprint: [u8; SHA384_HASH_SIZE],
|
pub next_key_fingerprint: [u8; SHA384_HASH_SIZE], // SHA384(next secret)
|
||||||
// -- end AES-GCM encrypted portion
|
// -- end AES-GCM encrypted portion
|
||||||
pub gcm_mac: [u8; AES_GCM_TAG_SIZE],
|
pub gcm_tag: [u8; AES_GCM_TAG_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RekeyAck {
|
impl RekeyAck {
|
||||||
|
|
315
zssp/src/zssp.rs
315
zssp/src/zssp.rs
|
@ -10,12 +10,13 @@
|
||||||
// FIPS compliant Noise_XK with Jedi powers (Kyber1024) and built-in attack-resistant large payload (fragmentation) support.
|
// FIPS compliant Noise_XK with Jedi powers (Kyber1024) and built-in attack-resistant large payload (fragmentation) support.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::io::Write;
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock, Weak};
|
use std::sync::{Arc, Mutex, RwLock, Weak};
|
||||||
|
|
||||||
use zerotier_crypto::aes::{Aes, AesCtr, AesGcm};
|
use zerotier_crypto::aes::{Aes, AesGcm};
|
||||||
use zerotier_crypto::hash::{hmac_sha512, HMACSHA384, HMAC_SHA384_SIZE, SHA384};
|
use zerotier_crypto::hash::{hmac_sha512, SHA384};
|
||||||
use zerotier_crypto::p384::{P384KeyPair, P384PublicKey, P384_ECDH_SHARED_SECRET_SIZE};
|
use zerotier_crypto::p384::{P384KeyPair, P384PublicKey, P384_ECDH_SHARED_SECRET_SIZE};
|
||||||
use zerotier_crypto::secret::Secret;
|
use zerotier_crypto::secret::Secret;
|
||||||
use zerotier_crypto::{random, secure_eq};
|
use zerotier_crypto::{random, secure_eq};
|
||||||
|
@ -63,8 +64,8 @@ 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 optional attached meta-data.
|
||||||
OkNewSession(Arc<Session<Application>>),
|
OkNewSession(Arc<Session<Application>>, Option<&'b mut [u8]>),
|
||||||
|
|
||||||
/// Packet appears valid but was rejected by the application layer, e.g. a rejected new session attempt.
|
/// Packet appears valid but was rejected by the application layer, e.g. a rejected new session attempt.
|
||||||
Rejected,
|
Rejected,
|
||||||
|
@ -339,24 +340,22 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
panic!(); // should be impossible
|
panic!(); // should be impossible
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
let init: &mut AliceNoiseXKInit = byte_array_as_proto_buffer_mut(init_packet).unwrap();
|
let init: &mut AliceNoiseXKInit = byte_array_as_proto_buffer_mut(init_packet).unwrap();
|
||||||
init.session_protocol_version = SESSION_PROTOCOL_VERSION;
|
init.session_protocol_version = SESSION_PROTOCOL_VERSION;
|
||||||
init.alice_noise_e = alice_noise_e;
|
init.alice_noise_e = alice_noise_e;
|
||||||
init.alice_session_id = *local_session_id.as_bytes();
|
init.alice_session_id = *local_session_id.as_bytes();
|
||||||
init.alice_hk_public = alice_hk_secret.public;
|
init.alice_hk_public = alice_hk_secret.public;
|
||||||
init.header_protection_key = header_protection_key.0;
|
init.header_protection_key = header_protection_key.0;
|
||||||
|
}
|
||||||
|
|
||||||
aes_ctr_crypt_one_time_use_key(
|
let mut gcm = AesGcm::new(
|
||||||
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es.as_bytes()).as_bytes(),
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es.as_bytes()).as_bytes(),
|
||||||
&mut init_packet[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START],
|
true,
|
||||||
);
|
);
|
||||||
|
gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_INIT, 1));
|
||||||
let hmac = hmac_sha384_2(
|
gcm.crypt_in_place(&mut init_packet[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
||||||
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION>(noise_es.as_bytes()).as_bytes(),
|
init_packet[AliceNoiseXKInit::AUTH_START..AliceNoiseXKInit::AUTH_START + AES_GCM_TAG_SIZE].copy_from_slice(&gcm.finish_encrypt());
|
||||||
&create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_INIT, 1),
|
|
||||||
&init_packet[HEADER_SIZE..AliceNoiseXKInit::AUTH_START],
|
|
||||||
);
|
|
||||||
init_packet[AliceNoiseXKInit::AUTH_START..AliceNoiseXKInit::AUTH_START + HMAC_SHA384_SIZE].copy_from_slice(&hmac);
|
|
||||||
|
|
||||||
send_with_fragmentation(
|
send_with_fragmentation(
|
||||||
&mut send,
|
&mut send,
|
||||||
|
@ -385,9 +384,9 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
/// with negotiation. False drops the packet.
|
/// with negotiation. False drops the packet.
|
||||||
///
|
///
|
||||||
/// The check_accept_session function is called at the end of negotiation for an incoming session
|
/// The check_accept_session function is called at the end of negotiation for an incoming session
|
||||||
/// with the caller's static public blob and meta-data if any. It must return the P-384 static public
|
/// with the caller's static public blob. It must return the P-384 static public key extracted from
|
||||||
/// key extracted from the supplied blob, a PSK (or all zeroes if none), and application data to
|
/// the supplied blob, a PSK (or all zeroes if none), and application data to associate with the new
|
||||||
/// associate with the new session. A return of None abandons the session.
|
/// session. A return of None rejects and abandons the session.
|
||||||
///
|
///
|
||||||
/// Note that if check_accept_session accepts and returns Some() the session could still fail with
|
/// Note that if check_accept_session accepts and returns Some() the session could still fail with
|
||||||
/// receive() returning an error. A Some() return from check_accept_sesion doesn't guarantee
|
/// receive() returning an error. A Some() return from check_accept_sesion doesn't guarantee
|
||||||
|
@ -409,7 +408,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
'b,
|
'b,
|
||||||
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
||||||
CheckAllowIncomingSession: FnMut() -> bool,
|
CheckAllowIncomingSession: FnMut() -> bool,
|
||||||
CheckAcceptSession: FnMut(&[u8], Option<&[u8]>) -> Option<(P384PublicKey, Secret<64>, Application::Data)>,
|
CheckAcceptSession: FnMut(&[u8]) -> Option<(P384PublicKey, Secret<64>, Application::Data)>,
|
||||||
>(
|
>(
|
||||||
&self,
|
&self,
|
||||||
app: &Application,
|
app: &Application,
|
||||||
|
@ -568,7 +567,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
'b,
|
'b,
|
||||||
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
SendFunction: FnMut(Option<&Arc<Session<Application>>>, &mut [u8]),
|
||||||
CheckAllowIncomingSession: FnMut() -> bool,
|
CheckAllowIncomingSession: FnMut() -> bool,
|
||||||
CheckAcceptSession: FnMut(&[u8], Option<&[u8]>) -> Option<(P384PublicKey, Secret<64>, Application::Data)>,
|
CheckAcceptSession: FnMut(&[u8]) -> Option<(P384PublicKey, Secret<64>, Application::Data)>,
|
||||||
>(
|
>(
|
||||||
&self,
|
&self,
|
||||||
app: &Application,
|
app: &Application,
|
||||||
|
@ -716,15 +715,14 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let alice_noise_e = P384PublicKey::from_bytes(&pkt.alice_noise_e).ok_or(Error::FailedAuthentication)?;
|
let alice_noise_e = P384PublicKey::from_bytes(&pkt.alice_noise_e).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_es = app.get_local_s_keypair().agree(&alice_noise_e).ok_or(Error::FailedAuthentication)?;
|
let noise_es = app.get_local_s_keypair().agree(&alice_noise_e).ok_or(Error::FailedAuthentication)?;
|
||||||
|
|
||||||
// Authenticate packet and also prove that Alice knows our static public key.
|
// Decrypt and authenticate init packet, also proving that caller knows our static identity.
|
||||||
if !secure_eq(
|
let mut gcm = AesGcm::new(
|
||||||
&pkt.hmac_es,
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es.as_bytes()).as_bytes(),
|
||||||
&hmac_sha384_2(
|
false,
|
||||||
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION>(noise_es.as_bytes()).as_bytes(),
|
);
|
||||||
&incoming_message_nonce,
|
gcm.reset_init_gcm(&incoming_message_nonce);
|
||||||
&pkt_assembled[HEADER_SIZE..AliceNoiseXKInit::AUTH_START],
|
gcm.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
||||||
),
|
if !gcm.finish_decrypt(&pkt_assembled[AliceNoiseXKInit::AUTH_START..AliceNoiseXKInit::AUTH_START + AES_GCM_TAG_SIZE]) {
|
||||||
) {
|
|
||||||
return Err(Error::FailedAuthentication);
|
return Err(Error::FailedAuthentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,12 +731,6 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
return Ok(ReceiveResult::Rejected);
|
return Ok(ReceiveResult::Rejected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt encrypted part of payload.
|
|
||||||
aes_ctr_crypt_one_time_use_key(
|
|
||||||
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es.as_bytes()).as_bytes(),
|
|
||||||
&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START],
|
|
||||||
);
|
|
||||||
|
|
||||||
let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?;
|
let pkt: &AliceNoiseXKInit = byte_array_as_proto_buffer(pkt_assembled)?;
|
||||||
let alice_session_id = SessionId::new_from_bytes(&pkt.alice_session_id).ok_or(Error::InvalidPacket)?;
|
let alice_session_id = SessionId::new_from_bytes(&pkt.alice_session_id).ok_or(Error::InvalidPacket)?;
|
||||||
let header_protection_key = Secret(pkt.header_protection_key);
|
let header_protection_key = Secret(pkt.header_protection_key);
|
||||||
|
@ -809,19 +801,14 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
ack.bob_session_id = *bob_session_id.as_bytes();
|
ack.bob_session_id = *bob_session_id.as_bytes();
|
||||||
ack.bob_hk_ciphertext = bob_hk_ciphertext;
|
ack.bob_hk_ciphertext = bob_hk_ciphertext;
|
||||||
|
|
||||||
// Encrypt main section of reply.
|
// Encrypt main section of reply and attach tag.
|
||||||
aes_ctr_crypt_one_time_use_key(
|
let mut gcm = AesGcm::new(
|
||||||
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es_ee.as_bytes()).as_bytes(),
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es_ee.as_bytes()).as_bytes(),
|
||||||
&mut ack_packet[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START],
|
true,
|
||||||
);
|
);
|
||||||
|
gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_BOB_NOISE_XK_ACK, 1));
|
||||||
// Add HMAC-SHA384 to reply packet.
|
gcm.crypt_in_place(&mut ack_packet[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]);
|
||||||
let reply_hmac = hmac_sha384_2(
|
ack_packet[BobNoiseXKAck::AUTH_START..BobNoiseXKAck::AUTH_START + AES_GCM_TAG_SIZE].copy_from_slice(&gcm.finish_encrypt());
|
||||||
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION>(noise_es_ee.as_bytes()).as_bytes(),
|
|
||||||
&create_message_nonce(PACKET_TYPE_BOB_NOISE_XK_ACK, 1),
|
|
||||||
&ack_packet[HEADER_SIZE..BobNoiseXKAck::AUTH_START],
|
|
||||||
);
|
|
||||||
ack_packet[BobNoiseXKAck::AUTH_START..].copy_from_slice(&reply_hmac);
|
|
||||||
|
|
||||||
send_with_fragmentation(
|
send_with_fragmentation(
|
||||||
|b| send(None, b),
|
|b| send(None, b),
|
||||||
|
@ -875,26 +862,17 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let noise_es_ee_kex_hmac_key =
|
// Decrypt and authenticate Bob's reply.
|
||||||
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION>(noise_es_ee.as_bytes());
|
let mut gcm = AesGcm::new(
|
||||||
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es_ee.as_bytes()).as_bytes(),
|
||||||
// Authenticate Bob's reply and the validity of bob_noise_e.
|
false,
|
||||||
if !secure_eq(
|
);
|
||||||
&pkt.hmac_es_ee,
|
gcm.reset_init_gcm(&incoming_message_nonce);
|
||||||
&hmac_sha384_2(
|
gcm.crypt_in_place(&mut pkt_assembled[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]);
|
||||||
noise_es_ee_kex_hmac_key.as_bytes(),
|
if !gcm.finish_decrypt(&pkt_assembled[BobNoiseXKAck::AUTH_START..BobNoiseXKAck::AUTH_START + AES_GCM_TAG_SIZE]) {
|
||||||
&incoming_message_nonce,
|
|
||||||
&pkt_assembled[HEADER_SIZE..BobNoiseXKAck::AUTH_START],
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
return Err(Error::FailedAuthentication);
|
return Err(Error::FailedAuthentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt encrypted portion of message.
|
|
||||||
aes_ctr_crypt_one_time_use_key(
|
|
||||||
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es_ee.as_bytes()).as_bytes(),
|
|
||||||
&mut pkt_assembled[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START],
|
|
||||||
);
|
|
||||||
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) {
|
||||||
|
@ -922,51 +900,47 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
// up forward secrecy. Also return Bob's opaque note.
|
// up forward secrecy. Also return Bob's opaque note.
|
||||||
let mut reply_buffer = [0u8; MAX_NOISE_HANDSHAKE_SIZE];
|
let mut reply_buffer = [0u8; MAX_NOISE_HANDSHAKE_SIZE];
|
||||||
reply_buffer[HEADER_SIZE] = SESSION_PROTOCOL_VERSION;
|
reply_buffer[HEADER_SIZE] = SESSION_PROTOCOL_VERSION;
|
||||||
let mut reply_len = HEADER_SIZE + 1;
|
let mut rw = &mut reply_buffer[HEADER_SIZE + 1..];
|
||||||
let mut reply_buffer_append = |b: &[u8]| {
|
|
||||||
let reply_len_new = reply_len + b.len();
|
|
||||||
debug_assert!(reply_len_new <= MAX_NOISE_HANDSHAKE_SIZE);
|
|
||||||
reply_buffer[reply_len..reply_len_new].copy_from_slice(b);
|
|
||||||
reply_len = reply_len_new;
|
|
||||||
};
|
|
||||||
let alice_s_public_blob = app.get_local_s_public_blob();
|
let alice_s_public_blob = app.get_local_s_public_blob();
|
||||||
assert!(alice_s_public_blob.len() <= (u16::MAX as usize));
|
assert!(alice_s_public_blob.len() <= (u16::MAX as usize));
|
||||||
reply_buffer_append(&(alice_s_public_blob.len() as u16).to_le_bytes());
|
rw.write_all(&(alice_s_public_blob.len() as u16).to_le_bytes())?;
|
||||||
reply_buffer_append(alice_s_public_blob);
|
let mut enc_start = MAX_NOISE_HANDSHAKE_SIZE - rw.len();
|
||||||
|
rw.write_all(alice_s_public_blob)?;
|
||||||
|
|
||||||
|
let mut reply_len = MAX_NOISE_HANDSHAKE_SIZE - rw.len();
|
||||||
|
let mut gcm = AesGcm::new(
|
||||||
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(&hmac_sha512(
|
||||||
|
noise_es_ee.as_bytes(),
|
||||||
|
hk.as_bytes(),
|
||||||
|
))
|
||||||
|
.as_bytes(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
gcm.reset_init_gcm(&reply_message_nonce);
|
||||||
|
gcm.crypt_in_place(&mut reply_buffer[enc_start..reply_len]);
|
||||||
|
let mut rw = &mut reply_buffer[reply_len..];
|
||||||
|
rw.write_all(&gcm.finish_encrypt())?;
|
||||||
|
|
||||||
if let Some(md) = outgoing_offer.metadata.as_ref() {
|
if let Some(md) = outgoing_offer.metadata.as_ref() {
|
||||||
reply_buffer_append(&(md.len() as u16).to_le_bytes());
|
assert!(md.len() <= (u16::MAX as usize));
|
||||||
reply_buffer_append(md.as_ref());
|
rw.write_all(&(md.len() as u16).to_le_bytes())?;
|
||||||
|
enc_start = MAX_NOISE_HANDSHAKE_SIZE - rw.len();
|
||||||
|
rw.write_all(md.as_ref())?;
|
||||||
} else {
|
} else {
|
||||||
reply_buffer_append(&[0u8, 0u8]); // no meta-data
|
rw.write_all(&[0u8, 0u8])?; // no meta-data
|
||||||
|
enc_start = MAX_NOISE_HANDSHAKE_SIZE - rw.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt Alice's static identity and other inner payload items. The key used here is
|
reply_len = MAX_NOISE_HANDSHAKE_SIZE - rw.len();
|
||||||
// mixed with 'hk' to make identity secrecy PQ forward secure.
|
let mut gcm = AesGcm::new(
|
||||||
aes_ctr_crypt_one_time_use_key(
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es_ee_se_hk_psk.as_bytes()).as_bytes(),
|
||||||
&hmac_sha512(noise_es_ee.as_bytes(), hk.as_bytes())[..AES_256_KEY_SIZE],
|
true,
|
||||||
&mut reply_buffer[HEADER_SIZE + 1..reply_len],
|
|
||||||
);
|
);
|
||||||
|
gcm.reset_init_gcm(&reply_message_nonce);
|
||||||
// First attach HMAC allowing Bob to verify that this is from the same Alice and to
|
gcm.crypt_in_place(&mut reply_buffer[enc_start..reply_len]);
|
||||||
// verify the authenticity of encrypted data.
|
reply_buffer[reply_len..reply_len + AES_GCM_TAG_SIZE].copy_from_slice(&gcm.finish_encrypt());
|
||||||
let hmac_es_ee = hmac_sha384_2(
|
reply_len += AES_GCM_TAG_SIZE;
|
||||||
noise_es_ee_kex_hmac_key.as_bytes(),
|
|
||||||
&reply_message_nonce,
|
|
||||||
&reply_buffer[HEADER_SIZE..reply_len],
|
|
||||||
);
|
|
||||||
reply_buffer[reply_len..reply_len + HMAC_SHA384_SIZE].copy_from_slice(&hmac_es_ee);
|
|
||||||
reply_len += HMAC_SHA384_SIZE;
|
|
||||||
|
|
||||||
// Then attach the final HMAC permitting Bob to verify the authenticity of the whole
|
|
||||||
// key exchange. Bob won't be able to do this until he decrypts and parses Alice's
|
|
||||||
// identity, so the first HMAC is to let him authenticate that first.
|
|
||||||
let hmac_es_ee_se_hk_psk = hmac_sha384_2(
|
|
||||||
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION>(noise_es_ee_se_hk_psk.as_bytes()).as_bytes(),
|
|
||||||
&reply_message_nonce,
|
|
||||||
&reply_buffer[HEADER_SIZE..reply_len],
|
|
||||||
);
|
|
||||||
reply_buffer[reply_len..reply_len + HMAC_SHA384_SIZE].copy_from_slice(&hmac_es_ee_se_hk_psk);
|
|
||||||
reply_len += HMAC_SHA384_SIZE;
|
|
||||||
|
|
||||||
drop(state);
|
drop(state);
|
||||||
{
|
{
|
||||||
|
@ -1025,62 +999,20 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(incoming) = incoming {
|
if let Some(incoming) = incoming {
|
||||||
// Check the first HMAC to verify against the currently known noise_es_ee key, which verifies
|
let mut r = PktReader(pkt_assembled, HEADER_SIZE + 1);
|
||||||
// that this reply is part of this session.
|
|
||||||
let auth_start = pkt_assembled.len() - ALICE_NOISE_XK_ACK_AUTH_SIZE;
|
let alice_static_public_blob_size = r.read_u16()? as usize;
|
||||||
if !secure_eq(
|
let alice_static_public_blob = r.read_decrypt_auth(
|
||||||
&pkt_assembled[auth_start..pkt_assembled.len() - HMAC_SHA384_SIZE],
|
alice_static_public_blob_size,
|
||||||
&hmac_sha384_2(
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(&hmac_sha512(
|
||||||
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION>(incoming.noise_es_ee.as_bytes()).as_bytes(),
|
incoming.noise_es_ee.as_bytes(),
|
||||||
|
incoming.hk.as_bytes(),
|
||||||
|
)),
|
||||||
&incoming_message_nonce,
|
&incoming_message_nonce,
|
||||||
&pkt_assembled[HEADER_SIZE..auth_start],
|
)?;
|
||||||
),
|
|
||||||
) {
|
|
||||||
return Err(Error::FailedAuthentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a copy of pkt_assembled so we can check the second HMAC against original ciphertext later.
|
// Check session acceptance and fish Alice's NIST P-384 static public key out of her static public blob.
|
||||||
let mut pkt_assembly_buffer_copy = [0u8; MAX_NOISE_HANDSHAKE_SIZE];
|
let check_result = check_accept_session(alice_static_public_blob);
|
||||||
pkt_assembly_buffer_copy[..pkt_assembled.len()].copy_from_slice(pkt_assembled);
|
|
||||||
|
|
||||||
// Decrypt encrypted section so we can finally learn Alice's static identity.
|
|
||||||
aes_ctr_crypt_one_time_use_key(
|
|
||||||
&hmac_sha512(incoming.noise_es_ee.as_bytes(), incoming.hk.as_bytes())[..AES_256_KEY_SIZE],
|
|
||||||
&mut pkt_assembled[ALICE_NOISE_XK_ACK_ENC_START..auth_start],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Read the static public blob and optional meta-data.
|
|
||||||
let mut pkt_assembled_ptr = HEADER_SIZE + 1;
|
|
||||||
let mut pkt_assembled_field_end = pkt_assembled_ptr + 2;
|
|
||||||
if pkt_assembled_field_end >= pkt_assembled.len() {
|
|
||||||
return Err(Error::InvalidPacket);
|
|
||||||
}
|
|
||||||
let alice_static_public_blob_size =
|
|
||||||
u16::from_le_bytes(pkt_assembled[pkt_assembled_ptr..pkt_assembled_field_end].try_into().unwrap()) as usize;
|
|
||||||
pkt_assembled_ptr = pkt_assembled_field_end;
|
|
||||||
pkt_assembled_field_end = pkt_assembled_ptr + alice_static_public_blob_size;
|
|
||||||
if pkt_assembled_field_end >= pkt_assembled.len() {
|
|
||||||
return Err(Error::InvalidPacket);
|
|
||||||
}
|
|
||||||
let alice_static_public_blob = &pkt_assembled[pkt_assembled_ptr..pkt_assembled_field_end];
|
|
||||||
pkt_assembled_ptr = pkt_assembled_field_end;
|
|
||||||
pkt_assembled_field_end = pkt_assembled_ptr + 2;
|
|
||||||
if pkt_assembled_field_end >= pkt_assembled.len() {
|
|
||||||
return Err(Error::InvalidPacket);
|
|
||||||
}
|
|
||||||
let alice_meta_data_size =
|
|
||||||
u16::from_le_bytes(pkt_assembled[pkt_assembled_ptr..pkt_assembled_field_end].try_into().unwrap()) as usize;
|
|
||||||
pkt_assembled_ptr = pkt_assembled_field_end;
|
|
||||||
pkt_assembled_field_end = pkt_assembled_ptr + alice_meta_data_size;
|
|
||||||
let alice_meta_data = if alice_meta_data_size > 0 {
|
|
||||||
Some(&pkt_assembled[pkt_assembled_ptr..pkt_assembled_field_end])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check session acceptance and fish Alice's NIST P-384 static public key out of
|
|
||||||
// her static public blob.
|
|
||||||
let check_result = check_accept_session(alice_static_public_blob, alice_meta_data);
|
|
||||||
if check_result.is_none() {
|
if check_result.is_none() {
|
||||||
self.sessions.write().unwrap().incoming.remove(&incoming.bob_session_id);
|
self.sessions.write().unwrap().incoming.remove(&incoming.bob_session_id);
|
||||||
return Ok(ReceiveResult::Rejected);
|
return Ok(ReceiveResult::Rejected);
|
||||||
|
@ -1100,17 +1032,18 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
&hmac_sha512(psk.as_bytes(), incoming.hk.as_bytes()),
|
&hmac_sha512(psk.as_bytes(), incoming.hk.as_bytes()),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Verify the packet using the final key to verify the whole key exchange.
|
// Decrypt meta-data and verify the final key in the process. Copy meta-data
|
||||||
if !secure_eq(
|
// into the temporary data buffer to return.
|
||||||
&pkt_assembly_buffer_copy[auth_start + HMAC_SHA384_SIZE..pkt_assembled.len()],
|
let alice_meta_data_size = r.read_u16()? as usize;
|
||||||
&hmac_sha384_2(
|
let alice_meta_data = r.read_decrypt_auth(
|
||||||
kbkdf::<HMAC_SHA384_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_AUTHENTICATION>(noise_es_ee_se_hk_psk.as_bytes()).as_bytes(),
|
alice_meta_data_size,
|
||||||
|
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_INIT_ENCRYPTION>(noise_es_ee_se_hk_psk.as_bytes()),
|
||||||
&incoming_message_nonce,
|
&incoming_message_nonce,
|
||||||
&pkt_assembly_buffer_copy[HEADER_SIZE..auth_start + HMAC_SHA384_SIZE],
|
)?;
|
||||||
),
|
if alice_meta_data.len() > data_buf.len() {
|
||||||
) {
|
return Err(Error::DataTooLarge);
|
||||||
return Err(Error::FailedAuthentication);
|
|
||||||
}
|
}
|
||||||
|
data_buf[..alice_meta_data.len()].copy_from_slice(alice_meta_data);
|
||||||
|
|
||||||
let session = Arc::new(Session {
|
let session = Arc::new(Session {
|
||||||
id: incoming.bob_session_id,
|
id: incoming.bob_session_id,
|
||||||
|
@ -1131,6 +1064,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
defrag: std::array::from_fn(|_| Mutex::new(Fragged::new())),
|
defrag: std::array::from_fn(|_| Mutex::new(Fragged::new())),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Promote incoming session to active.
|
||||||
{
|
{
|
||||||
let mut sessions = self.sessions.write().unwrap();
|
let mut sessions = self.sessions.write().unwrap();
|
||||||
sessions.incoming.remove(&incoming.bob_session_id);
|
sessions.incoming.remove(&incoming.bob_session_id);
|
||||||
|
@ -1139,7 +1073,14 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
let _ = session.send_nop(|b| send(Some(&session), b));
|
let _ = session.send_nop(|b| send(Some(&session), b));
|
||||||
|
|
||||||
return Ok(ReceiveResult::OkNewSession(session));
|
return Ok(ReceiveResult::OkNewSession(
|
||||||
|
session,
|
||||||
|
if alice_meta_data.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&mut data_buf[..alice_meta_data.len()])
|
||||||
|
},
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::UnknownLocalSessionId);
|
return Err(Error::UnknownLocalSessionId);
|
||||||
}
|
}
|
||||||
|
@ -1650,22 +1591,40 @@ impl SessionKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut to HMAC data split into two slices.
|
/// Helper for parsing variable length ALICE_NOISE_XK_ACK
|
||||||
fn hmac_sha384_2(key: &[u8], a: &[u8], b: &[u8]) -> [u8; 48] {
|
struct PktReader<'a>(&'a mut [u8], usize);
|
||||||
let mut hmac = HMACSHA384::new(key);
|
|
||||||
hmac.update(a);
|
|
||||||
hmac.update(b);
|
|
||||||
hmac.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shortcut to AES-CTR encrypt or decrypt with a zero IV.
|
impl<'a> PktReader<'a> {
|
||||||
///
|
fn read_u16(&mut self) -> Result<u16, Error> {
|
||||||
/// This is used during Noise_XK handshaking. Each stage uses a different key to encrypt the
|
let tmp = self.1 + 2;
|
||||||
/// payload that is used only once per handshake and per session.
|
if tmp <= self.0.len() {
|
||||||
fn aes_ctr_crypt_one_time_use_key(key: &[u8], data: &mut [u8]) {
|
let n = u16::from_le_bytes(self.0[self.1..tmp].try_into().unwrap());
|
||||||
let mut ctr = AesCtr::new(key);
|
self.1 = tmp;
|
||||||
ctr.reset_set_iv(&[0u8; 12]);
|
Ok(n)
|
||||||
ctr.crypt_in_place(data);
|
} else {
|
||||||
|
Err(Error::InvalidPacket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_decrypt_auth<'b>(&'b mut self, l: usize, k: Secret<AES_256_KEY_SIZE>, nonce: &[u8]) -> Result<&'b [u8], Error> {
|
||||||
|
let mut tmp = self.1 + l;
|
||||||
|
if (tmp + AES_GCM_TAG_SIZE) <= self.0.len() {
|
||||||
|
let mut gcm = AesGcm::new(k.as_bytes(), false);
|
||||||
|
gcm.reset_init_gcm(nonce);
|
||||||
|
gcm.crypt_in_place(&mut self.0[self.1..tmp]);
|
||||||
|
let s = &self.0[self.1..tmp];
|
||||||
|
self.1 = tmp;
|
||||||
|
tmp += AES_GCM_TAG_SIZE;
|
||||||
|
if !gcm.finish_decrypt(&self.0[self.1..tmp]) {
|
||||||
|
Err(Error::FailedAuthentication)
|
||||||
|
} else {
|
||||||
|
self.1 = tmp;
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidPacket)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HMAC-SHA512 key derivation based on: https://csrc.nist.gov/publications/detail/sp/800-108/final (page 7)
|
/// HMAC-SHA512 key derivation based on: https://csrc.nist.gov/publications/detail/sp/800-108/final (page 7)
|
||||||
|
|
Loading…
Add table
Reference in a new issue