diff --git a/crypto/src/hash.rs b/crypto/src/hash.rs index e71472b9c..c9a7a2327 100644 --- a/crypto/src/hash.rs +++ b/crypto/src/hash.rs @@ -269,19 +269,19 @@ pub fn hmac_sha512(key: &[u8], msg: &[u8]) -> [u8; HMAC_SHA512_SIZE] { hm.finish() } -#[inline(always)] -pub fn hmac_sha512_into(key: &[u8], msg: &[u8], md: &mut [u8]) { +pub fn hmac_sha512_secret256(key: &[u8], msg: &[u8]) -> Secret<32> { let mut hm = HMACSHA512::new(key); hm.update(msg); - hm.finish_into(md); + let mut md = [0u8; HMAC_SHA512_SIZE]; + hm.finish_into(&mut md); + // With such a simple procedure hopefully the compiler implements the following line as a move from md and not a copy + // If not we ought to change this code so we don't leak a secret value on the stack + unsafe { Secret::from_bytes(&md[0..32]) } } - -pub fn hmac_sha512_secret(key: &[u8], msg: &[u8]) -> Secret { - debug_assert!(C <= HMAC_SHA512_SIZE); +pub fn hmac_sha512_secret(key: &[u8], msg: &[u8]) -> Secret { let mut hm = HMACSHA512::new(key); hm.update(msg); - let buff = hm.finish(); - unsafe { Secret::from_bytes(&buff[..C]) } + Secret::move_bytes(hm.finish()) } #[inline(always)] @@ -290,10 +290,3 @@ pub fn hmac_sha384(key: &[u8], msg: &[u8]) -> [u8; HMAC_SHA384_SIZE] { hm.update(msg); hm.finish() } - -#[inline(always)] -pub fn hmac_sha384_into(key: &[u8], msg: &[u8], md: &mut [u8]) { - let mut hm = HMACSHA384::new(key); - hm.update(msg); - hm.finish_into(md); -} diff --git a/zssp/Cargo.toml b/zssp/Cargo.toml index d4888b865..1d381576a 100644 --- a/zssp/Cargo.toml +++ b/zssp/Cargo.toml @@ -25,3 +25,4 @@ doc = false zerotier-utils = { path = "../utils" } zerotier-crypto = { path = "../crypto" } pqc_kyber = { version = "0.4.0", default-features = false, features = ["kyber1024", "std"] } +hex-literal = "0.3.4" diff --git a/zssp/src/proto.rs b/zssp/src/proto.rs index 7952c9d30..826371983 100644 --- a/zssp/src/proto.rs +++ b/zssp/src/proto.rs @@ -8,8 +8,9 @@ use std::mem::size_of; +use hex_literal::hex; 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 crate::error::Error; @@ -25,11 +26,13 @@ pub const MIN_TRANSPORT_MTU: usize = 128; pub const MAX_INIT_PAYLOAD_SIZE: usize = MAX_NOISE_HANDSHAKE_SIZE - ALICE_NOISE_XK_ACK_MIN_SIZE; /// Initial value of 'h' -/// echo -n 'Noise_XKpsk3_P384_AESGCM_SHA384_hybridKyber1024' | shasum -a 384 -pub(crate) const INITIAL_H: [u8; SHA384_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, - 0x2c, 0x3c, 0xcb, 0x7f, 0xca, 0x20, 0x06, 0x91, 0x20, 0x55, 0x52, 0x8e, 0xd4, 0x3c, 0x97, 0xc3, 0xd5, 0x6c, 0xb4, 0x13, 0x02, 0x54, 0x83, 0x12, -]; +/// echo -n 'Noise_XKpsk3_P384_AESGCM_SHA512_hybridKyber1024' | shasum -a 512 +pub(crate) const INITIAL_H: [u8; SHA512_HASH_SIZE] = + hex!("12ae70954e8d93bf7f73d0fe48d487155666f541e532f9461af5ef52ab90c8fd9259ef9e48f5adcf9af63f869805a570004ae095655dcaddbc226a50623b2b25"); +/// Initial value of 'h' +/// echo -n 'Noise_KKpsk0_P384_AESGCM_SHA512' | shasum -a 512 +pub(crate) const INITIAL_H_REKEY: [u8; SHA512_HASH_SIZE] = + hex!("daeedd651ac9c5173f2eaaff996beebac6f3f1bfe9a70bb1cc54fa1fb2bf46260d71a3c4fb4d4ee36f654c31773a8a15e5d5be974a0668dc7db70f4e13ed172e"); /// Version 0: Noise_XK with NIST P-384 plus Kyber1024 hybrid exchange on session init. pub(crate) const SESSION_PROTOCOL_VERSION: u8 = 0x00; @@ -65,7 +68,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; /// 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_HEADER_PROTECTION_KEY_SIZE: usize = 16; @@ -160,14 +163,14 @@ pub(crate) struct RekeyAck { pub session_protocol_version: u8, // -- start AES-GCM encrypted portion (using current key) 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 pub gcm_tag: [u8; AES_GCM_TAG_SIZE], } impl RekeyAck { 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; } diff --git a/zssp/src/zssp.rs b/zssp/src/zssp.rs index 4db210db4..660b3e65d 100644 --- a/zssp/src/zssp.rs +++ b/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 zerotier_crypto::aes::{Aes, AesGcm}; -use zerotier_crypto::hash::{hmac_sha512_secret, SHA384, SHA384_HASH_SIZE}; -use zerotier_crypto::p384::{P384KeyPair, P384PublicKey, P384_ECDH_SHARED_SECRET_SIZE}; +use zerotier_crypto::hash::{hmac_sha512_secret, hmac_sha512_secret256, SHA512}; +use zerotier_crypto::p384::{P384KeyPair, P384PublicKey}; use zerotier_crypto::secret::Secret; use zerotier_crypto::{random, secure_eq}; @@ -106,8 +106,8 @@ struct IncomingIncompleteSession { timestamp: i64, alice_session_id: SessionId, bob_session_id: SessionId, - noise_h: [u8; SHA384_HASH_SIZE], - noise_es_ee: Secret, + noise_h: [u8; NOISE_HASHLEN], + noise_ck_es_ee: Secret, hk: Secret, header_protection_key: Secret, bob_noise_e_secret: P384KeyPair, @@ -116,9 +116,9 @@ struct IncomingIncompleteSession { struct OutgoingSessionOffer { last_retry_time: AtomicI64, - psk: Secret, - noise_h: [u8; SHA384_HASH_SIZE], - noise_es: Secret, + psk: Secret, + noise_h: [u8; NOISE_HASHLEN], + noise_ck_es: Secret, alice_noise_e_secret: P384KeyPair, alice_hk_secret: Secret, metadata: Option>, @@ -139,7 +139,7 @@ enum Offer { } struct SessionKey { - ratchet_key: Secret, // Key used in derivation of the next session key + ratchet_key: Secret, // Key used in derivation of the next session key receive_cipher_pool: [Mutex>; GCM_CIPHER_POOL_SIZE], // Pool of reusable sending ciphers send_cipher_pool: [Mutex>; GCM_CIPHER_POOL_SIZE], // Pool of reusable receiving ciphers rekey_at_time: i64, // Rekey at or after this time (ticks) @@ -289,7 +289,7 @@ impl Context { mtu: usize, remote_s_public_blob: &[u8], remote_s_public_p384: P384PublicKey, - psk: Secret, + psk: Secret, metadata: Option>, application_data: Application::Data, current_time: i64, @@ -301,9 +301,14 @@ impl Context { let alice_noise_e_secret = P384KeyPair::generate(); 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_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 header_protection_key: Secret = Secret(random::get_bytes_secure()); + // Init aesgcm before we move noise_ck_es + let mut gcm = AesGcm::new(&kbkdf256::(&noise_ck_es)); + let (local_session_id, session) = { let mut sessions = self.sessions.write().unwrap(); @@ -330,8 +335,8 @@ impl Context { outgoing_offer: Offer::NoiseXKInit(Box::new(OutgoingSessionOffer { last_retry_time: AtomicI64::new(current_time), psk, - noise_h: mix_hash(&mix_hash(&INITIAL_H, remote_s_public_blob), &alice_noise_e), - noise_es: noise_es.clone(), + noise_h, + noise_ck_es, alice_noise_e_secret, alice_hk_secret: Secret(alice_hk_secret.secret), metadata, @@ -366,7 +371,6 @@ impl Context { } // Encrypt and add authentication tag. - let mut gcm = AesGcm::new(&kbkdf::(noise_es.as_bytes())); gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_ALICE_NOISE_XK_INIT, 1)); gcm.aad(&offer.noise_h); gcm.crypt_in_place(&mut init_packet[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]); @@ -735,9 +739,11 @@ impl Context { 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_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. - let mut gcm = AesGcm::new(&kbkdf::(noise_es.as_bytes())); + let mut gcm = AesGcm::new(&kbkdf256::(&noise_ck_es)); gcm.reset_init_gcm(&incoming_message_nonce); gcm.aad(&noise_h); gcm.crypt_in_place(&mut pkt_assembled[AliceNoiseXKInit::ENC_START..AliceNoiseXKInit::AUTH_START]); @@ -754,12 +760,12 @@ impl Context { 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); - // 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. let bob_noise_e_secret = P384KeyPair::generate(); let bob_noise_e = bob_noise_e_secret.public_key_bytes().clone(); - let noise_es_ee = hmac_sha512_secret( - noise_es.as_bytes(), + let noise_ck_es_ee = hmac_sha512_secret( + noise_ck_es.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()) @@ -786,7 +792,7 @@ impl Context { ack.bob_hk_ciphertext = bob_hk_ciphertext; // Encrypt main section of reply and attach tag. - let mut gcm = AesGcm::new(&kbkdf::(noise_es_ee.as_bytes())); + let mut gcm = AesGcm::new(&kbkdf256::(&noise_ck_es_ee)); gcm.reset_init_gcm(&create_message_nonce(PACKET_TYPE_BOB_NOISE_XK_ACK, 1)); gcm.aad(&noise_h_next); gcm.crypt_in_place(&mut ack_packet[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]); @@ -819,7 +825,7 @@ impl Context { alice_session_id, bob_session_id, 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, bob_noise_e_secret, header_protection_key: Secret(pkt.header_protection_key), @@ -874,8 +880,8 @@ impl Context { // 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 noise_es_ee = hmac_sha512_secret::( - outgoing_offer.noise_es.as_bytes(), + let noise_ck_es_ee = hmac_sha512_secret( + outgoing_offer.noise_ck_es.as_bytes(), outgoing_offer .alice_noise_e_secret .agree(&bob_noise_e) @@ -887,7 +893,7 @@ impl Context { 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. - let mut gcm = AesGcm::new(&kbkdf::(noise_es_ee.as_bytes())); + let mut gcm = AesGcm::new(&kbkdf256::(&noise_ck_es_ee)); gcm.reset_init_gcm(&incoming_message_nonce); gcm.aad(&outgoing_offer.noise_h); gcm.crypt_in_place(&mut pkt_assembled[BobNoiseXKAck::ENC_START..BobNoiseXKAck::AUTH_START]); @@ -909,9 +915,9 @@ impl Context { // Packet fully authenticated if session.update_receive_window(incoming_counter) { - let noise_es_ee_se_hk_psk = hmac_sha512_secret::( - hmac_sha512_secret::(noise_es_ee.as_bytes(), noise_se.as_bytes()).as_bytes(), - hmac_sha512_secret::(outgoing_offer.psk.as_bytes(), hk.as_bytes()).as_bytes(), + let noise_ck_es_ee_se_hk_psk = hmac_sha512_secret( + hmac_sha512_secret(noise_ck_es_ee.as_bytes(), noise_se.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); @@ -928,9 +934,10 @@ impl Context { let mut enc_start = ack_len; ack_len = append_to_slice(&mut ack, ack_len, alice_s_public_blob)?; - let mut gcm = AesGcm::new(&kbkdf::( - hmac_sha512_secret::(noise_es_ee.as_bytes(), hk.as_bytes()).as_bytes(), - )); + let mut gcm = AesGcm::new(&kbkdf256::(&hmac_sha512_secret( + noise_ck_es_ee.as_bytes(), + hk.as_bytes(), + ))); gcm.reset_init_gcm(&reply_message_nonce); gcm.aad(&noise_h_next); gcm.crypt_in_place(&mut ack[enc_start..ack_len]); @@ -946,9 +953,7 @@ impl Context { enc_start = ack_len; ack_len = append_to_slice(&mut ack, ack_len, metadata)?; - let mut gcm = AesGcm::new(&kbkdf::( - noise_es_ee_se_hk_psk.as_bytes(), - )); + let mut gcm = AesGcm::new(&kbkdf256::(&noise_ck_es_ee_se_hk_psk)); gcm.reset_init_gcm(&reply_message_nonce); gcm.aad(&noise_h_next); gcm.crypt_in_place(&mut ack[enc_start..ack_len]); @@ -961,7 +966,7 @@ impl Context { let mut state = session.state.write().unwrap(); let _ = state.remote_session_id.insert(bob_session_id); let _ = state.keys[0].insert(SessionKey::new::( - noise_es_ee_se_hk_psk, + noise_ck_es_ee_se_hk_psk, 1, current_time, 2, @@ -1034,9 +1039,10 @@ impl Context { let alice_static_public_blob = r.read_decrypt_auth( alice_static_public_blob_size, - kbkdf::( - hmac_sha512_secret::(incoming.noise_es_ee.as_bytes(), incoming.hk.as_bytes()).as_bytes(), - ), + kbkdf256::(&hmac_sha512_secret( + incoming.noise_ck_es_ee.as_bytes(), + incoming.hk.as_bytes(), + )), &incoming.noise_h, &incoming_message_nonce, )?; @@ -1052,9 +1058,9 @@ impl Context { let noise_h_next = mix_hash(&noise_h_next, psk.as_bytes()); // Complete Noise_XKpsk3 on Bob's side. - let noise_es_ee_se_hk_psk = hmac_sha512_secret::( - hmac_sha512_secret::( - incoming.noise_es_ee.as_bytes(), + let noise_ck_es_ee_se_hk_psk = hmac_sha512_secret( + hmac_sha512_secret( + incoming.noise_ck_es_ee.as_bytes(), incoming .bob_noise_e_secret .agree(&alice_noise_s) @@ -1062,7 +1068,7 @@ impl Context { .as_bytes(), ) .as_bytes(), - hmac_sha512_secret::(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 @@ -1070,7 +1076,7 @@ impl Context { let alice_meta_data_size = r.read_u16()? as usize; let alice_meta_data = r.read_decrypt_auth( alice_meta_data_size, - kbkdf::(noise_es_ee_se_hk_psk.as_bytes()), + kbkdf256::(&noise_ck_es_ee_se_hk_psk), &noise_h_next, &incoming_message_nonce, )?; @@ -1090,7 +1096,7 @@ impl Context { physical_mtu: self.default_physical_mtu.load(Ordering::Relaxed), remote_session_id: Some(incoming.alice_session_id), keys: [ - Some(SessionKey::new::(noise_es_ee_se_hk_psk, 1, current_time, 2, true, true)), + Some(SessionKey::new::(noise_ck_es_ee_se_hk_psk, 1, current_time, 2, true, true)), None, ], current_key: 0, @@ -1150,9 +1156,13 @@ impl Context { 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_se = bob_e_secret.agree(&session.static_public_key).ok_or(Error::FailedAuthentication)?; - let noise_psk_se_ee_es = hmac_sha512_secret::( - hmac_sha512_secret::( - hmac_sha512_secret::(key.ratchet_key.as_bytes(), noise_es.as_bytes()).as_bytes(), + let noise_ck_psk_es_ee_se = hmac_sha512_secret( + hmac_sha512_secret( + hmac_sha512_secret( + hmac_sha512_secret(&INITIAL_H_REKEY, key.ratchet_key.as_bytes()).as_bytes(), + noise_es.as_bytes(), + ) + .as_bytes(), noise_ee.as_bytes(), ) .as_bytes(), @@ -1165,7 +1175,7 @@ impl Context { let reply: &mut RekeyAck = byte_array_as_proto_buffer_mut(&mut reply_buf).unwrap(); reply.session_protocol_version = SESSION_PROTOCOL_VERSION; 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_ck_psk_es_ee_se.as_bytes()); let counter = session.get_next_outgoing_counter().ok_or(Error::MaxKeyLifetimeExceeded)?.get(); set_packet_header( @@ -1197,7 +1207,7 @@ impl Context { drop(state); let mut state = session.state.write().unwrap(); let _ = state.keys[key_index ^ 1].replace(SessionKey::new::( - noise_psk_se_ee_es, + noise_ck_psk_es_ee_se, next_ratchet_count, current_time, counter, @@ -1248,9 +1258,13 @@ impl Context { 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_se = app.get_local_s_keypair().agree(&bob_e).ok_or(Error::FailedAuthentication)?; - let noise_psk_se_ee_es = hmac_sha512_secret::( - hmac_sha512_secret::( - hmac_sha512_secret::(key.ratchet_key.as_bytes(), noise_es.as_bytes()).as_bytes(), + let noise_ck_psk_es_ee_se = hmac_sha512_secret( + hmac_sha512_secret( + hmac_sha512_secret( + hmac_sha512_secret(&INITIAL_H_REKEY, key.ratchet_key.as_bytes()).as_bytes(), + noise_es.as_bytes(), + ) + .as_bytes(), noise_ee.as_bytes(), ) .as_bytes(), @@ -1259,7 +1273,7 @@ impl Context { // 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. - 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_ck_psk_es_ee_se.as_bytes())) { if session.update_receive_window(incoming_counter) { // 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 @@ -1269,7 +1283,7 @@ impl Context { let next_key_index = key_index ^ 1; let mut state = session.state.write().unwrap(); let _ = state.keys[next_key_index].replace(SessionKey::new::( - noise_psk_se_ee_es, + noise_ck_psk_es_ee_se, next_ratchet_count, current_time, session.send_counter.load(Ordering::Relaxed), @@ -1390,10 +1404,10 @@ impl Session { } /// 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(); 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 { None } @@ -1582,15 +1596,15 @@ fn assemble_fragments_into(fragments: &[A::IncomingPacketBu impl SessionKey { fn new( - key: Secret, + key: Secret, ratchet_count: u64, current_time: i64, current_counter: u64, bob: bool, confirmed: bool, ) -> Self { - let a2b = kbkdf::(key.as_bytes()); - let b2a = kbkdf::(key.as_bytes()); + let a2b = kbkdf256::(&key); + let b2a = kbkdf256::(&key); let (receive_key, send_key) = if bob { (a2b, b2a) } else { @@ -1599,7 +1613,7 @@ impl SessionKey { 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))); Self { - ratchet_key: kbkdf::(key.as_bytes()), + ratchet_key: kbkdf512::(&key), receive_cipher_pool, send_cipher_pool, rekey_at_time: current_time @@ -1679,8 +1693,8 @@ fn append_to_slice(s: &mut [u8], p: usize, d: &[u8]) -> Result { } /// MixHash to update 'h' during negotiation. -fn mix_hash(h: &[u8; SHA384_HASH_SIZE], m: &[u8]) -> [u8; SHA384_HASH_SIZE] { - let mut hasher = SHA384::new(); +fn mix_hash(h: &[u8; NOISE_HASHLEN], m: &[u8]) -> [u8; NOISE_HASHLEN] { + let mut hasher = SHA512::new(); hasher.update(h); hasher.update(m); hasher.finish() @@ -1688,22 +1702,13 @@ 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) /// Cryptographically this isn't meaningfully different from HMAC(key, [label]) but this is how NIST rolls. -fn kbkdf(key: &[u8]) -> Secret { - //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 = (OUTPUT_BYTES * 8) - hmac_sha512_secret( - key, - &[ - 1, - b'Z', - b'T', - LABEL, - 0x00, - 0, - (((OUTPUT_BYTES * 8) >> 8) & 0xff) as u8, - ((OUTPUT_BYTES * 8) & 0xff) as u8, - ], - ) +/// These are the values we have assigned to the 5 variables involved in their KDF: +/// K_in = key, i = 1u8, Label = b'Z'||b'T'||LABEL, Context = 0u8, L = 512u16 or 256u16 +fn kbkdf512(key: &Secret) -> Secret { + hmac_sha512_secret(key.as_bytes(), &[1, b'Z', b'T', LABEL, 0x00, 0, 2u8, 0u8]) +} +fn kbkdf256(key: &Secret) -> Secret<32> { + hmac_sha512_secret256(key.as_bytes(), &[1, b'Z', b'T', LABEL, 0x00, 0, 1u8, 0u8]) } fn prng32(mut x: u32) -> u32 {