mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 05:23:44 +02:00
switched to sha512 everywhere
This commit is contained in:
parent
faf4c9a5b1
commit
7a7703a268
3 changed files with 88 additions and 70 deletions
|
@ -276,12 +276,10 @@ pub fn hmac_sha512_into(key: &[u8], msg: &[u8], md: &mut [u8]) {
|
||||||
hm.finish_into(md);
|
hm.finish_into(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hmac_sha512_secret<const C: usize>(key: &[u8], msg: &[u8]) -> Secret<C> {
|
pub fn hmac_sha512_secret(key: &[u8], msg: &[u8]) -> Secret<HMAC_SHA512_SIZE> {
|
||||||
debug_assert!(C <= HMAC_SHA512_SIZE);
|
|
||||||
let mut hm = HMACSHA512::new(key);
|
let mut hm = HMACSHA512::new(key);
|
||||||
hm.update(msg);
|
hm.update(msg);
|
||||||
let buff = hm.finish();
|
Secret::move_bytes(hm.finish())
|
||||||
unsafe { Secret::from_bytes(&buff[..C]) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -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::SHA384_HASH_SIZE;
|
use zerotier_crypto::hash::SHA512_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;
|
||||||
|
@ -26,9 +26,8 @@ pub const MAX_INIT_PAYLOAD_SIZE: usize = MAX_NOISE_HANDSHAKE_SIZE - ALICE_NOISE_
|
||||||
|
|
||||||
/// Initial value of 'h'
|
/// Initial value of 'h'
|
||||||
/// echo -n 'Noise_XKpsk3_P384_AESGCM_SHA384_hybridKyber1024' | shasum -a 384
|
/// echo -n 'Noise_XKpsk3_P384_AESGCM_SHA384_hybridKyber1024' | shasum -a 384
|
||||||
pub(crate) const INITIAL_H: [u8; SHA384_HASH_SIZE] = [
|
pub(crate) const INITIAL_H: [u8; SHA512_HASH_SIZE] = [
|
||||||
0x35, 0x27, 0x16, 0x62, 0x58, 0x04, 0x0c, 0x7a, 0x99, 0xa8, 0x0b, 0x49, 0xb2, 0x6b, 0x25, 0xfb, 0xf5, 0x26, 0x2a, 0x26, 0xe7, 0xb3, 0x70, 0xcb,
|
0xd3, 0x18, 0x1c, 0xee, 0x05, 0x8d, 0x35, 0x06, 0x22, 0xcd, 0xfc, 0x46, 0x82, 0x2a, 0x60, 0x81, 0xf5, 0xe0, 0x47, 0x65, 0x57, 0x66, 0x53, 0x17, 0x95, 0xae, 0x33, 0x3c, 0x90, 0x83, 0x3b, 0xbf, 0x7f, 0x5e, 0xd7, 0x3d, 0x03, 0x39, 0x10, 0x03, 0x29, 0xfd, 0x2d, 0x59, 0xa0, 0x99, 0x56, 0x63, 0x18, 0x2d, 0x63, 0xb7, 0x8d, 0xd1, 0x7a, 0x2c, 0xf7, 0x92, 0x16, 0xdd, 0xfa, 0xf1, 0x05, 0x37,
|
||||||
0x2c, 0x3c, 0xcb, 0x7f, 0xca, 0x20, 0x06, 0x91, 0x20, 0x55, 0x52, 0x8e, 0xd4, 0x3c, 0x97, 0xc3, 0xd5, 0x6c, 0xb4, 0x13, 0x02, 0x54, 0x83, 0x12,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Version 0: Noise_XK with NIST P-384 plus Kyber1024 hybrid exchange on session init.
|
/// Version 0: Noise_XK with NIST P-384 plus Kyber1024 hybrid exchange on session init.
|
||||||
|
@ -65,7 +64,7 @@ pub(crate) const MAX_NOISE_HANDSHAKE_FRAGMENTS: usize = 16; // enough room for p
|
||||||
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;
|
||||||
|
|
||||||
/// Size of keys used during derivation, mixing, etc. process.
|
/// Size of keys used during derivation, mixing, etc. process.
|
||||||
pub(crate) const BASE_KEY_SIZE: usize = 64;
|
pub(crate) const NOISE_HASHLEN: usize = SHA512_HASH_SIZE;
|
||||||
|
|
||||||
pub(crate) const AES_256_KEY_SIZE: usize = 32;
|
pub(crate) const AES_256_KEY_SIZE: usize = 32;
|
||||||
pub(crate) const AES_HEADER_PROTECTION_KEY_SIZE: usize = 16;
|
pub(crate) const AES_HEADER_PROTECTION_KEY_SIZE: usize = 16;
|
||||||
|
@ -160,14 +159,14 @@ 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], // SHA384(next secret)
|
pub next_key_fingerprint: [u8; SHA512_HASH_SIZE], // SHA384(next secret)
|
||||||
// -- end AES-GCM encrypted portion
|
// -- end AES-GCM encrypted portion
|
||||||
pub gcm_tag: [u8; AES_GCM_TAG_SIZE],
|
pub gcm_tag: [u8; AES_GCM_TAG_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RekeyAck {
|
impl RekeyAck {
|
||||||
pub const ENC_START: usize = HEADER_SIZE + 1;
|
pub const ENC_START: usize = HEADER_SIZE + 1;
|
||||||
pub const AUTH_START: usize = Self::ENC_START + P384_PUBLIC_KEY_SIZE + SHA384_HASH_SIZE;
|
pub const AUTH_START: usize = Self::ENC_START + P384_PUBLIC_KEY_SIZE + SHA512_HASH_SIZE;
|
||||||
pub const SIZE: usize = Self::AUTH_START + AES_GCM_TAG_SIZE;
|
pub const SIZE: usize = Self::AUTH_START + AES_GCM_TAG_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
139
zssp/src/zssp.rs
139
zssp/src/zssp.rs
|
@ -15,8 +15,8 @@ use std::sync::atomic::{AtomicI64, AtomicU64, AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak};
|
use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak};
|
||||||
|
|
||||||
use zerotier_crypto::aes::{Aes, AesGcm};
|
use zerotier_crypto::aes::{Aes, AesGcm};
|
||||||
use zerotier_crypto::hash::{hmac_sha512_secret, SHA384, SHA384_HASH_SIZE};
|
use zerotier_crypto::hash::{SHA512, hmac_sha512_secret};
|
||||||
use zerotier_crypto::p384::{P384KeyPair, P384PublicKey, P384_ECDH_SHARED_SECRET_SIZE};
|
use zerotier_crypto::p384::{P384KeyPair, P384PublicKey};
|
||||||
use zerotier_crypto::secret::Secret;
|
use zerotier_crypto::secret::Secret;
|
||||||
use zerotier_crypto::{random, secure_eq};
|
use zerotier_crypto::{random, secure_eq};
|
||||||
|
|
||||||
|
@ -106,8 +106,8 @@ struct IncomingIncompleteSession<Application: ApplicationLayer> {
|
||||||
timestamp: i64,
|
timestamp: i64,
|
||||||
alice_session_id: SessionId,
|
alice_session_id: SessionId,
|
||||||
bob_session_id: SessionId,
|
bob_session_id: SessionId,
|
||||||
noise_h: [u8; SHA384_HASH_SIZE],
|
noise_h: [u8; NOISE_HASHLEN],
|
||||||
noise_es_ee: Secret<BASE_KEY_SIZE>,
|
noise_ck_es_ee: Secret<NOISE_HASHLEN>,
|
||||||
hk: Secret<KYBER_SSBYTES>,
|
hk: Secret<KYBER_SSBYTES>,
|
||||||
header_protection_key: Secret<AES_HEADER_PROTECTION_KEY_SIZE>,
|
header_protection_key: Secret<AES_HEADER_PROTECTION_KEY_SIZE>,
|
||||||
bob_noise_e_secret: P384KeyPair,
|
bob_noise_e_secret: P384KeyPair,
|
||||||
|
@ -116,9 +116,9 @@ struct IncomingIncompleteSession<Application: ApplicationLayer> {
|
||||||
|
|
||||||
struct OutgoingSessionOffer {
|
struct OutgoingSessionOffer {
|
||||||
last_retry_time: AtomicI64,
|
last_retry_time: AtomicI64,
|
||||||
psk: Secret<BASE_KEY_SIZE>,
|
psk: Secret<NOISE_HASHLEN>,
|
||||||
noise_h: [u8; SHA384_HASH_SIZE],
|
noise_h: [u8; NOISE_HASHLEN],
|
||||||
noise_es: Secret<P384_ECDH_SHARED_SECRET_SIZE>,
|
noise_ck_es: Secret<NOISE_HASHLEN>,
|
||||||
alice_noise_e_secret: P384KeyPair,
|
alice_noise_e_secret: P384KeyPair,
|
||||||
alice_hk_secret: Secret<KYBER_SECRETKEYBYTES>,
|
alice_hk_secret: Secret<KYBER_SECRETKEYBYTES>,
|
||||||
metadata: Option<Vec<u8>>,
|
metadata: Option<Vec<u8>>,
|
||||||
|
@ -139,7 +139,7 @@ enum Offer {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SessionKey {
|
struct SessionKey {
|
||||||
ratchet_key: Secret<BASE_KEY_SIZE>, // Key used in derivation of the next session key
|
ratchet_key: Secret<NOISE_HASHLEN>, // Key used in derivation of the next session key
|
||||||
receive_cipher_pool: [Mutex<AesGcm<false>>; GCM_CIPHER_POOL_SIZE], // Pool of reusable sending ciphers
|
receive_cipher_pool: [Mutex<AesGcm<false>>; GCM_CIPHER_POOL_SIZE], // Pool of reusable sending ciphers
|
||||||
send_cipher_pool: [Mutex<AesGcm<true>>; GCM_CIPHER_POOL_SIZE], // Pool of reusable receiving ciphers
|
send_cipher_pool: [Mutex<AesGcm<true>>; GCM_CIPHER_POOL_SIZE], // Pool of reusable receiving ciphers
|
||||||
rekey_at_time: i64, // Rekey at or after this time (ticks)
|
rekey_at_time: i64, // Rekey at or after this time (ticks)
|
||||||
|
@ -290,7 +290,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
mtu: usize,
|
mtu: usize,
|
||||||
remote_s_public_blob: &[u8],
|
remote_s_public_blob: &[u8],
|
||||||
remote_s_public_p384: P384PublicKey,
|
remote_s_public_p384: P384PublicKey,
|
||||||
psk: Secret<BASE_KEY_SIZE>,
|
psk: Secret<NOISE_HASHLEN>,
|
||||||
metadata: Option<Vec<u8>>,
|
metadata: Option<Vec<u8>>,
|
||||||
application_data: Application::Data,
|
application_data: Application::Data,
|
||||||
current_time: i64,
|
current_time: i64,
|
||||||
|
@ -302,9 +302,14 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let alice_noise_e_secret = P384KeyPair::generate();
|
let alice_noise_e_secret = P384KeyPair::generate();
|
||||||
let alice_noise_e = alice_noise_e_secret.public_key_bytes().clone();
|
let alice_noise_e = alice_noise_e_secret.public_key_bytes().clone();
|
||||||
let noise_es = alice_noise_e_secret.agree(&remote_s_public_p384).ok_or(Error::InvalidParameter)?;
|
let noise_es = alice_noise_e_secret.agree(&remote_s_public_p384).ok_or(Error::InvalidParameter)?;
|
||||||
|
let noise_h = mix_hash(&mix_hash(&INITIAL_H, remote_s_public_blob), &alice_noise_e);
|
||||||
|
let noise_ck_es = hmac_sha512_secret(&INITIAL_H, noise_es.as_bytes());
|
||||||
let alice_hk_secret = pqc_kyber::keypair(&mut random::SecureRandom::default());
|
let alice_hk_secret = pqc_kyber::keypair(&mut random::SecureRandom::default());
|
||||||
let header_protection_key: Secret<AES_HEADER_PROTECTION_KEY_SIZE> = Secret(random::get_bytes_secure());
|
let header_protection_key: Secret<AES_HEADER_PROTECTION_KEY_SIZE> = Secret(random::get_bytes_secure());
|
||||||
|
|
||||||
|
// Init aesgcm before we move noise_ck_es
|
||||||
|
let mut gcm = AesGcm::new(&kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES>(&noise_ck_es));
|
||||||
|
|
||||||
let (local_session_id, session) = {
|
let (local_session_id, session) = {
|
||||||
let mut sessions = self.sessions.write().unwrap();
|
let mut sessions = self.sessions.write().unwrap();
|
||||||
|
|
||||||
|
@ -331,8 +336,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
outgoing_offer: Offer::NoiseXKInit(Box::new(OutgoingSessionOffer {
|
outgoing_offer: Offer::NoiseXKInit(Box::new(OutgoingSessionOffer {
|
||||||
last_retry_time: AtomicI64::new(current_time),
|
last_retry_time: AtomicI64::new(current_time),
|
||||||
psk,
|
psk,
|
||||||
noise_h: mix_hash(&mix_hash(&INITIAL_H, remote_s_public_blob), &alice_noise_e),
|
noise_h,
|
||||||
noise_es: noise_es.clone(),
|
noise_ck_es,
|
||||||
alice_noise_e_secret,
|
alice_noise_e_secret,
|
||||||
alice_hk_secret: Secret(alice_hk_secret.secret),
|
alice_hk_secret: Secret(alice_hk_secret.secret),
|
||||||
metadata,
|
metadata,
|
||||||
|
@ -367,7 +372,6 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt and add authentication tag.
|
// Encrypt and add authentication tag.
|
||||||
let mut gcm = AesGcm::new(&kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES>(noise_es.as_bytes()));
|
|
||||||
gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_INIT, 1));
|
gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_INIT, 1));
|
||||||
gcm.aad(&offer.noise_h);
|
gcm.aad(&offer.noise_h);
|
||||||
gcm.crypt_in_place(&mut init_packet[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
gcm.crypt_in_place(&mut init_packet[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
||||||
|
@ -736,9 +740,11 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
let noise_h = mix_hash(&mix_hash(&INITIAL_H, app.get_local_s_public_blob()), alice_noise_e.as_bytes());
|
let noise_h = mix_hash(&mix_hash(&INITIAL_H, app.get_local_s_public_blob()), alice_noise_e.as_bytes());
|
||||||
let noise_h_next = mix_hash(&noise_h, &pkt_assembled[HEADER_SIZE..]);
|
let noise_h_next = mix_hash(&noise_h, &pkt_assembled[HEADER_SIZE..]);
|
||||||
|
let noise_ck_es = hmac_sha512_secret(&INITIAL_H, noise_es.as_bytes());
|
||||||
|
drop(noise_es);
|
||||||
|
|
||||||
// Decrypt and authenticate init packet, also proving that caller knows our static identity.
|
// Decrypt and authenticate init packet, also proving that caller knows our static identity.
|
||||||
let mut gcm = AesGcm::new(&kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES>(noise_es.as_bytes()));
|
let mut gcm = AesGcm::new(&kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES>(&noise_ck_es));
|
||||||
gcm.reset_init_gcm(&incoming_message_nonce);
|
gcm.reset_init_gcm(&incoming_message_nonce);
|
||||||
gcm.aad(&noise_h);
|
gcm.aad(&noise_h);
|
||||||
gcm.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
gcm.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]);
|
||||||
|
@ -755,12 +761,12 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let alice_session_id = SessionId::new_from_array(&pkt.alice_session_id).ok_or(Error::InvalidPacket)?;
|
let alice_session_id = SessionId::new_from_array(&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);
|
||||||
|
|
||||||
// Create Bob's ephemeral keys and derive noise_es_ee by agreeing with Alice's. Also create
|
// Create Bob's ephemeral keys and derive noise_ck by agreeing with Alice's. Also create
|
||||||
// a Kyber ciphertext to send back to Alice.
|
// a Kyber ciphertext to send back to Alice.
|
||||||
let bob_noise_e_secret = P384KeyPair::generate();
|
let bob_noise_e_secret = P384KeyPair::generate();
|
||||||
let bob_noise_e = bob_noise_e_secret.public_key_bytes().clone();
|
let bob_noise_e = bob_noise_e_secret.public_key_bytes().clone();
|
||||||
let noise_es_ee = hmac_sha512_secret(
|
let noise_ck_es_ee = hmac_sha512_secret(
|
||||||
noise_es.as_bytes(),
|
noise_ck_es.as_bytes(),
|
||||||
bob_noise_e_secret.agree(&alice_noise_e).ok_or(Error::FailedAuthentication)?.as_bytes(),
|
bob_noise_e_secret.agree(&alice_noise_e).ok_or(Error::FailedAuthentication)?.as_bytes(),
|
||||||
);
|
);
|
||||||
let (bob_hk_ciphertext, hk) = pqc_kyber::encapsulate(&pkt.alice_hk_public, &mut random::SecureRandom::default())
|
let (bob_hk_ciphertext, hk) = pqc_kyber::encapsulate(&pkt.alice_hk_public, &mut random::SecureRandom::default())
|
||||||
|
@ -787,7 +793,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
ack.bob_hk_ciphertext = bob_hk_ciphertext;
|
ack.bob_hk_ciphertext = bob_hk_ciphertext;
|
||||||
|
|
||||||
// Encrypt main section of reply and attach tag.
|
// Encrypt main section of reply and attach tag.
|
||||||
let mut gcm = AesGcm::new(&kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES_EE>(noise_es_ee.as_bytes()));
|
let mut gcm = AesGcm::new(&kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES_EE>(&noise_ck_es_ee));
|
||||||
gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_BOB_NOISE_XK_ACK, 1));
|
gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_BOB_NOISE_XK_ACK, 1));
|
||||||
gcm.aad(&noise_h_next);
|
gcm.aad(&noise_h_next);
|
||||||
gcm.crypt_in_place(&mut ack_packet[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]);
|
gcm.crypt_in_place(&mut ack_packet[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]);
|
||||||
|
@ -820,7 +826,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
alice_session_id,
|
alice_session_id,
|
||||||
bob_session_id,
|
bob_session_id,
|
||||||
noise_h: mix_hash(&mix_hash(&noise_h_next, &bob_noise_e), &ack_packet[HEADER_SIZE..]),
|
noise_h: mix_hash(&mix_hash(&noise_h_next, &bob_noise_e), &ack_packet[HEADER_SIZE..]),
|
||||||
noise_es_ee: noise_es_ee.clone(),
|
noise_ck_es_ee,
|
||||||
hk,
|
hk,
|
||||||
bob_noise_e_secret,
|
bob_noise_e_secret,
|
||||||
header_protection_key: Secret(pkt.header_protection_key),
|
header_protection_key: Secret(pkt.header_protection_key),
|
||||||
|
@ -875,8 +881,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
// Derive noise_es_ee from Bob's ephemeral public key.
|
// Derive noise_es_ee from Bob's ephemeral public key.
|
||||||
let bob_noise_e = P384PublicKey::from_bytes(&pkt.bob_noise_e).ok_or(Error::FailedAuthentication)?;
|
let bob_noise_e = P384PublicKey::from_bytes(&pkt.bob_noise_e).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_es_ee = hmac_sha512_secret::<BASE_KEY_SIZE>(
|
let noise_ck_es_ee = hmac_sha512_secret(
|
||||||
outgoing_offer.noise_es.as_bytes(),
|
outgoing_offer.noise_ck_es.as_bytes(),
|
||||||
outgoing_offer
|
outgoing_offer
|
||||||
.alice_noise_e_secret
|
.alice_noise_e_secret
|
||||||
.agree(&bob_noise_e)
|
.agree(&bob_noise_e)
|
||||||
|
@ -888,7 +894,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let noise_h_next = mix_hash(&mix_hash(&outgoing_offer.noise_h, bob_noise_e.as_bytes()), &pkt_assembled[HEADER_SIZE..]);
|
let noise_h_next = mix_hash(&mix_hash(&outgoing_offer.noise_h, bob_noise_e.as_bytes()), &pkt_assembled[HEADER_SIZE..]);
|
||||||
|
|
||||||
// Decrypt and authenticate Bob's reply.
|
// Decrypt and authenticate Bob's reply.
|
||||||
let mut gcm = AesGcm::new(&kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES_EE>(noise_es_ee.as_bytes()));
|
let mut gcm = AesGcm::new(&kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES_EE>(&noise_ck_es_ee));
|
||||||
gcm.reset_init_gcm(&incoming_message_nonce);
|
gcm.reset_init_gcm(&incoming_message_nonce);
|
||||||
gcm.aad(&outgoing_offer.noise_h);
|
gcm.aad(&outgoing_offer.noise_h);
|
||||||
gcm.crypt_in_place(&mut pkt_assembled[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]);
|
gcm.crypt_in_place(&mut pkt_assembled[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]);
|
||||||
|
@ -910,9 +916,9 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
// Packet fully authenticated
|
// Packet fully authenticated
|
||||||
if session.update_receive_window(incoming_counter) {
|
if session.update_receive_window(incoming_counter) {
|
||||||
let noise_es_ee_se_hk_psk = hmac_sha512_secret::<BASE_KEY_SIZE>(
|
let noise_ck_es_ee_se_hk_psk = hmac_sha512_secret(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(noise_es_ee.as_bytes(), noise_se.as_bytes()).as_bytes(),
|
hmac_sha512_secret(noise_ck_es_ee.as_bytes(), noise_se.as_bytes()).as_bytes(),
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(outgoing_offer.psk.as_bytes(), hk.as_bytes()).as_bytes(),
|
hmac_sha512_secret(outgoing_offer.psk.as_bytes(), hk.as_bytes()).as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let reply_message_nonce = create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_ACK, 2);
|
let reply_message_nonce = create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_ACK, 2);
|
||||||
|
@ -929,8 +935,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let mut enc_start = ack_len;
|
let mut enc_start = ack_len;
|
||||||
ack_len = append_to_slice(&mut ack, ack_len, alice_s_public_blob)?;
|
ack_len = append_to_slice(&mut ack, ack_len, alice_s_public_blob)?;
|
||||||
|
|
||||||
let mut gcm = AesGcm::new(&kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_HK>(
|
let mut gcm = AesGcm::new(&kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_HK>(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(noise_es_ee.as_bytes(), hk.as_bytes()).as_bytes(),
|
&hmac_sha512_secret(noise_ck_es_ee.as_bytes(), hk.as_bytes()),
|
||||||
));
|
));
|
||||||
gcm.reset_init_gcm(&reply_message_nonce);
|
gcm.reset_init_gcm(&reply_message_nonce);
|
||||||
gcm.aad(&noise_h_next);
|
gcm.aad(&noise_h_next);
|
||||||
|
@ -947,8 +953,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
enc_start = ack_len;
|
enc_start = ack_len;
|
||||||
ack_len = append_to_slice(&mut ack, ack_len, metadata)?;
|
ack_len = append_to_slice(&mut ack, ack_len, metadata)?;
|
||||||
|
|
||||||
let mut gcm = AesGcm::new(&kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_SE_HK_PSK>(
|
let mut gcm = AesGcm::new(&kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_SE_HK_PSK>(
|
||||||
noise_es_ee_se_hk_psk.as_bytes(),
|
&noise_ck_es_ee_se_hk_psk,
|
||||||
));
|
));
|
||||||
gcm.reset_init_gcm(&reply_message_nonce);
|
gcm.reset_init_gcm(&reply_message_nonce);
|
||||||
gcm.aad(&noise_h_next);
|
gcm.aad(&noise_h_next);
|
||||||
|
@ -962,7 +968,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let mut state = session.state.write().unwrap();
|
let mut state = session.state.write().unwrap();
|
||||||
let _ = state.remote_session_id.insert(bob_session_id);
|
let _ = state.remote_session_id.insert(bob_session_id);
|
||||||
let _ = state.keys[0].insert(SessionKey::new::<Application>(
|
let _ = state.keys[0].insert(SessionKey::new::<Application>(
|
||||||
noise_es_ee_se_hk_psk,
|
noise_ck_es_ee_se_hk_psk,
|
||||||
1,
|
1,
|
||||||
current_time,
|
current_time,
|
||||||
2,
|
2,
|
||||||
|
@ -1035,8 +1041,8 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
let alice_static_public_blob = r.read_decrypt_auth(
|
let alice_static_public_blob = r.read_decrypt_auth(
|
||||||
alice_static_public_blob_size,
|
alice_static_public_blob_size,
|
||||||
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_HK>(
|
kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_HK>(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(incoming.noise_es_ee.as_bytes(), incoming.hk.as_bytes()).as_bytes(),
|
&hmac_sha512_secret(incoming.noise_ck_es_ee.as_bytes(), incoming.hk.as_bytes()),
|
||||||
),
|
),
|
||||||
&incoming.noise_h,
|
&incoming.noise_h,
|
||||||
&incoming_message_nonce,
|
&incoming_message_nonce,
|
||||||
|
@ -1053,9 +1059,9 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let noise_h_next = mix_hash(&noise_h_next, psk.as_bytes());
|
let noise_h_next = mix_hash(&noise_h_next, psk.as_bytes());
|
||||||
|
|
||||||
// Complete Noise_XKpsk3 on Bob's side.
|
// Complete Noise_XKpsk3 on Bob's side.
|
||||||
let noise_es_ee_se_hk_psk = hmac_sha512_secret::<BASE_KEY_SIZE>(
|
let noise_ck_es_ee_se_hk_psk = hmac_sha512_secret(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(
|
hmac_sha512_secret(
|
||||||
incoming.noise_es_ee.as_bytes(),
|
incoming.noise_ck_es_ee.as_bytes(),
|
||||||
incoming
|
incoming
|
||||||
.bob_noise_e_secret
|
.bob_noise_e_secret
|
||||||
.agree(&alice_noise_s)
|
.agree(&alice_noise_s)
|
||||||
|
@ -1063,7 +1069,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(psk.as_bytes(), incoming.hk.as_bytes()).as_bytes(),
|
hmac_sha512_secret(psk.as_bytes(), incoming.hk.as_bytes()).as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decrypt meta-data and verify the final key in the process. Copy meta-data
|
// Decrypt meta-data and verify the final key in the process. Copy meta-data
|
||||||
|
@ -1071,7 +1077,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let alice_meta_data_size = r.read_u16()? as usize;
|
let alice_meta_data_size = r.read_u16()? as usize;
|
||||||
let alice_meta_data = r.read_decrypt_auth(
|
let alice_meta_data = r.read_decrypt_auth(
|
||||||
alice_meta_data_size,
|
alice_meta_data_size,
|
||||||
kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_SE_HK_PSK>(noise_es_ee_se_hk_psk.as_bytes()),
|
kbkdf256::<KBKDF_KEY_USAGE_LABEL_KEX_ES_EE_SE_HK_PSK>(&noise_ck_es_ee_se_hk_psk),
|
||||||
&noise_h_next,
|
&noise_h_next,
|
||||||
&incoming_message_nonce,
|
&incoming_message_nonce,
|
||||||
)?;
|
)?;
|
||||||
|
@ -1091,7 +1097,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
physical_mtu: self.default_physical_mtu.load(Ordering::Relaxed),
|
physical_mtu: self.default_physical_mtu.load(Ordering::Relaxed),
|
||||||
remote_session_id: Some(incoming.alice_session_id),
|
remote_session_id: Some(incoming.alice_session_id),
|
||||||
keys: [
|
keys: [
|
||||||
Some(SessionKey::new::<Application>(noise_es_ee_se_hk_psk, 1, current_time, 2, true, true)),
|
Some(SessionKey::new::<Application>(noise_ck_es_ee_se_hk_psk, 1, current_time, 2, true, true)),
|
||||||
None,
|
None,
|
||||||
],
|
],
|
||||||
current_key: 0,
|
current_key: 0,
|
||||||
|
@ -1151,9 +1157,9 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let noise_es = app.get_local_s_keypair().agree(&alice_e).ok_or(Error::FailedAuthentication)?;
|
let noise_es = app.get_local_s_keypair().agree(&alice_e).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_ee = bob_e_secret.agree(&alice_e).ok_or(Error::FailedAuthentication)?;
|
let noise_ee = bob_e_secret.agree(&alice_e).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_se = bob_e_secret.agree(&session.static_public_key).ok_or(Error::FailedAuthentication)?;
|
let noise_se = bob_e_secret.agree(&session.static_public_key).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_psk_se_ee_es = hmac_sha512_secret::<BASE_KEY_SIZE>(
|
let noise_psk_se_ee_es = hmac_sha512_secret(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(
|
hmac_sha512_secret(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(key.ratchet_key.as_bytes(), noise_es.as_bytes()).as_bytes(),
|
hmac_sha512_secret(key.ratchet_key.as_bytes(), noise_es.as_bytes()).as_bytes(),
|
||||||
noise_ee.as_bytes(),
|
noise_ee.as_bytes(),
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
|
@ -1166,7 +1172,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let reply: &mut RekeyAck = byte_array_as_proto_buffer_mut(&mut reply_buf).unwrap();
|
let reply: &mut RekeyAck = byte_array_as_proto_buffer_mut(&mut reply_buf).unwrap();
|
||||||
reply.session_protocol_version = SESSION_PROTOCOL_VERSION;
|
reply.session_protocol_version = SESSION_PROTOCOL_VERSION;
|
||||||
reply.bob_e = *bob_e_secret.public_key_bytes();
|
reply.bob_e = *bob_e_secret.public_key_bytes();
|
||||||
reply.next_key_fingerprint = SHA384::hash(noise_psk_se_ee_es.as_bytes());
|
reply.next_key_fingerprint = SHA512::hash(noise_psk_se_ee_es.as_bytes());
|
||||||
|
|
||||||
let counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?.get();
|
let counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?.get();
|
||||||
set_packet_header(
|
set_packet_header(
|
||||||
|
@ -1249,9 +1255,9 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let noise_es = alice_e_secret.agree(&session.static_public_key).ok_or(Error::FailedAuthentication)?;
|
let noise_es = alice_e_secret.agree(&session.static_public_key).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_ee = alice_e_secret.agree(&bob_e).ok_or(Error::FailedAuthentication)?;
|
let noise_ee = alice_e_secret.agree(&bob_e).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_se = app.get_local_s_keypair().agree(&bob_e).ok_or(Error::FailedAuthentication)?;
|
let noise_se = app.get_local_s_keypair().agree(&bob_e).ok_or(Error::FailedAuthentication)?;
|
||||||
let noise_psk_se_ee_es = hmac_sha512_secret::<BASE_KEY_SIZE>(
|
let noise_psk_se_ee_es = hmac_sha512_secret(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(
|
hmac_sha512_secret(
|
||||||
hmac_sha512_secret::<BASE_KEY_SIZE>(key.ratchet_key.as_bytes(), noise_es.as_bytes()).as_bytes(),
|
hmac_sha512_secret(key.ratchet_key.as_bytes(), noise_es.as_bytes()).as_bytes(),
|
||||||
noise_ee.as_bytes(),
|
noise_ee.as_bytes(),
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
|
@ -1260,7 +1266,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
// We need to check that the key Bob is acknowledging matches the latest sent offer.
|
// We need to check that the key Bob is acknowledging matches the latest sent offer.
|
||||||
// Because of OOO, it might not, in which case this rekey must be cancelled and retried.
|
// Because of OOO, it might not, in which case this rekey must be cancelled and retried.
|
||||||
if secure_eq(&pkt.next_key_fingerprint, &SHA384::hash(noise_psk_se_ee_es.as_bytes())) {
|
if secure_eq(&pkt.next_key_fingerprint, &SHA512::hash(noise_psk_se_ee_es.as_bytes())) {
|
||||||
if session.update_receive_window(incoming_counter) {
|
if session.update_receive_window(incoming_counter) {
|
||||||
// The new "Alice" knows Bob has the key since this is an ACK, so she can go
|
// The new "Alice" knows Bob has the key since this is an ACK, so she can go
|
||||||
// ahead and set current_key to the new key. Then when she sends something
|
// ahead and set current_key to the new key. Then when she sends something
|
||||||
|
@ -1391,10 +1397,10 @@ impl<Application: ApplicationLayer> Session<Application> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the ratchet count and a hash fingerprint of the current active key.
|
/// Get the ratchet count and a hash fingerprint of the current active key.
|
||||||
pub fn key_info(&self) -> Option<(u64, [u8; 48])> {
|
pub fn key_info(&self) -> Option<(u64, [u8; NOISE_HASHLEN])> {
|
||||||
let state = self.state.read().unwrap();
|
let state = self.state.read().unwrap();
|
||||||
if let Some(key) = state.keys[state.current_key].as_ref() {
|
if let Some(key) = state.keys[state.current_key].as_ref() {
|
||||||
Some((key.ratchet_count, SHA384::hash(key.ratchet_key.as_bytes())))
|
Some((key.ratchet_count, SHA512::hash(key.ratchet_key.as_bytes())))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1583,15 +1589,15 @@ fn assemble_fragments_into<A: ApplicationLayer>(fragments: &[A::IncomingPacketBu
|
||||||
|
|
||||||
impl SessionKey {
|
impl SessionKey {
|
||||||
fn new<Application: ApplicationLayer>(
|
fn new<Application: ApplicationLayer>(
|
||||||
key: Secret<BASE_KEY_SIZE>,
|
key: Secret<NOISE_HASHLEN>,
|
||||||
ratchet_count: u64,
|
ratchet_count: u64,
|
||||||
current_time: i64,
|
current_time: i64,
|
||||||
current_counter: u64,
|
current_counter: u64,
|
||||||
bob: bool,
|
bob: bool,
|
||||||
confirmed: bool,
|
confirmed: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let a2b = kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB>(key.as_bytes());
|
let a2b = kbkdf256::<KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB>(&key);
|
||||||
let b2a = kbkdf::<AES_256_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE>(key.as_bytes());
|
let b2a = kbkdf256::<KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE>(&key);
|
||||||
let (receive_key, send_key) = if bob {
|
let (receive_key, send_key) = if bob {
|
||||||
(a2b, b2a)
|
(a2b, b2a)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1600,7 +1606,7 @@ impl SessionKey {
|
||||||
let receive_cipher_pool = std::array::from_fn(|_| Mutex::new(AesGcm::new(&receive_key)));
|
let receive_cipher_pool = std::array::from_fn(|_| Mutex::new(AesGcm::new(&receive_key)));
|
||||||
let send_cipher_pool = std::array::from_fn(|_| Mutex::new(AesGcm::new(&send_key)));
|
let send_cipher_pool = std::array::from_fn(|_| Mutex::new(AesGcm::new(&send_key)));
|
||||||
Self {
|
Self {
|
||||||
ratchet_key: kbkdf::<BASE_KEY_SIZE, KBKDF_KEY_USAGE_LABEL_RATCHET>(key.as_bytes()),
|
ratchet_key: kbkdf512::<KBKDF_KEY_USAGE_LABEL_RATCHET>(&key),
|
||||||
receive_cipher_pool,
|
receive_cipher_pool,
|
||||||
send_cipher_pool,
|
send_cipher_pool,
|
||||||
rekey_at_time: current_time
|
rekey_at_time: current_time
|
||||||
|
@ -1680,8 +1686,8 @@ fn append_to_slice(s: &mut [u8], p: usize, d: &[u8]) -> Result<usize, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MixHash to update 'h' during negotiation.
|
/// MixHash to update 'h' during negotiation.
|
||||||
fn mix_hash(h: &[u8; SHA384_HASH_SIZE], m: &[u8]) -> [u8; SHA384_HASH_SIZE] {
|
fn mix_hash(h: &[u8; NOISE_HASHLEN], m: &[u8]) -> [u8; NOISE_HASHLEN] {
|
||||||
let mut hasher = SHA384::new();
|
let mut hasher = SHA512::new();
|
||||||
hasher.update(h);
|
hasher.update(h);
|
||||||
hasher.update(m);
|
hasher.update(m);
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
|
@ -1689,11 +1695,11 @@ fn mix_hash(h: &[u8; SHA384_HASH_SIZE], m: &[u8]) -> [u8; SHA384_HASH_SIZE] {
|
||||||
|
|
||||||
/// 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)
|
||||||
/// Cryptographically this isn't meaningfully different from HMAC(key, [label]) but this is how NIST rolls.
|
/// Cryptographically this isn't meaningfully different from HMAC(key, [label]) but this is how NIST rolls.
|
||||||
fn kbkdf<const OUTPUT_BYTES: usize, const LABEL: u8>(key: &[u8]) -> Secret<OUTPUT_BYTES> {
|
/// These are the values we have assigned to the 5 variables involved in https://csrc.nist.gov/publications/detail/sp/800-108/final:
|
||||||
//These are the values we have assigned to the 5 variables involved in https://csrc.nist.gov/publications/detail/sp/800-108/final:
|
/// K_in = key, i = 0x01, Label = 'Z'||'T'||LABEL, Context = 0x00, L = 512 or 256 as a u16
|
||||||
// K_in = key, i = 0x01, Label = 'Z'||'T'||LABEL, Context = 0x00, L = (OUTPUT_BYTES * 8)
|
fn kbkdf512<const LABEL: u8>(key: &Secret<NOISE_HASHLEN>) -> Secret<NOISE_HASHLEN> {
|
||||||
hmac_sha512_secret(
|
hmac_sha512_secret(
|
||||||
key,
|
key.as_bytes(),
|
||||||
&[
|
&[
|
||||||
1,
|
1,
|
||||||
b'Z',
|
b'Z',
|
||||||
|
@ -1701,11 +1707,26 @@ fn kbkdf<const OUTPUT_BYTES: usize, const LABEL: u8>(key: &[u8]) -> Secret<OUTPU
|
||||||
LABEL,
|
LABEL,
|
||||||
0x00,
|
0x00,
|
||||||
0,
|
0,
|
||||||
(((OUTPUT_BYTES * 8) >> 8) & 0xff) as u8,
|
2u8,
|
||||||
((OUTPUT_BYTES * 8) & 0xff) as u8,
|
0u8,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
fn kbkdf256<const LABEL: u8>(key: &Secret<NOISE_HASHLEN>) -> Secret<32> {
|
||||||
|
hmac_sha512_secret(
|
||||||
|
key.as_bytes(),
|
||||||
|
&[
|
||||||
|
1,
|
||||||
|
b'Z',
|
||||||
|
b'T',
|
||||||
|
LABEL,
|
||||||
|
0x00,
|
||||||
|
0,
|
||||||
|
1u8,
|
||||||
|
0u8,
|
||||||
|
],
|
||||||
|
).first_n_clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn prng32(mut x: u32) -> u32 {
|
fn prng32(mut x: u32) -> u32 {
|
||||||
// based on lowbias32 from https://nullprogram.com/blog/2018/07/31/
|
// based on lowbias32 from https://nullprogram.com/blog/2018/07/31/
|
||||||
|
|
Loading…
Add table
Reference in a new issue