switched to sha512 everywhere

This commit is contained in:
mamoniot 2023-03-14 17:45:48 -04:00
parent faf4c9a5b1
commit 7a7703a268
No known key found for this signature in database
GPG key ID: ADCCDBBE0E3D3B3B
3 changed files with 88 additions and 70 deletions

View file

@ -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)]

View file

@ -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;
} }

View file

@ -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/