diff --git a/zssp/src/zssp.rs b/zssp/src/zssp.rs index b06c0b804..c15e97ded 100644 --- a/zssp/src/zssp.rs +++ b/zssp/src/zssp.rs @@ -953,668 +953,30 @@ impl ReceiveContext { &kbkdf512(noise_es_ee_se_hk_psk.as_bytes(), KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION).as_bytes() [..HMAC_SHA384_SIZE], &message_nonce, - &pkt_assembled[HEADER_SIZE..pkt_assembled_enc_end + HMAC_SHA384_SIZE], + &pkt_saved_for_final_hmac[HEADER_SIZE..pkt_assembled_enc_end + HMAC_SHA384_SIZE], ), ) { return Err(Error::FailedAuthentication); } + + todo!() } else { return Err(Error::NewSessionRejected); } } - PACKET_TYPE_ALICE_REKEY_INIT => {} + PACKET_TYPE_ALICE_REKEY_INIT => todo!(), - PACKET_TYPE_BOB_REKEY_ACK => {} + PACKET_TYPE_BOB_REKEY_ACK => todo!(), _ => { return Err(Error::InvalidPacket); } } - todo!() - /* - // To greatly simplify logic handling key exchange packets, assemble these first. - // Handling KEX packets isn't the fast path so the extra copying isn't significant. - const KEX_BUF_LEN: usize = 4096; - let mut kex_packet = [0_u8; KEX_BUF_LEN]; - let mut kex_packet_len = 0; - for i in 0..fragments.len() { - let mut ff = fragments[i].as_ref(); - if ff.len() < MIN_PACKET_SIZE { - return Err(Error::InvalidPacket); - } - if i > 0 { - ff = &ff[HEADER_SIZE..]; - } - let j = kex_packet_len + ff.len(); - if j > KEX_BUF_LEN { - return Err(Error::InvalidPacket); - } - kex_packet[kex_packet_len..j].copy_from_slice(ff); - kex_packet_len = j; - } - let kex_packet_saved_ciphertext = kex_packet.clone(); // save for HMAC check later - - // Key exchange packets begin (after header) with the session protocol version. This could be - // changed in the future to support a different cipher suite. - if kex_packet[HEADER_SIZE] != SESSION_PROTOCOL_VERSION { - return Err(Error::UnknownProtocolVersion); - } - - match packet_type { - PACKET_TYPE_INITIAL_KEY_OFFER => { - // alice (remote) -> bob (local) - - //////////////////////////////////////////////////////////////// - // packet decoding for noise initial key offer - // -> e, es, s, ss - //////////////////////////////////////////////////////////////// - - if kex_packet_len < (HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE + AES_GCM_TAG_SIZE + HMAC_SIZE + HMAC_SIZE) { - return Err(Error::InvalidPacket); - } - - let plaintext_end = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE; - let payload_end = kex_packet_len - (AES_GCM_TAG_SIZE + HMAC_SIZE + HMAC_SIZE); - let aes_gcm_tag_end = kex_packet_len - (HMAC_SIZE + HMAC_SIZE); - let hmac1_end = kex_packet_len - HMAC_SIZE; - - // Check the secondary HMAC first, which proves that the sender knows the recipient's full static identity. - if !secure_eq( - &hmac_sha384_2( - app.get_local_s_public_blob_hash(), - &message_nonce, - &kex_packet[HEADER_SIZE..hmac1_end], - ), - &kex_packet[hmac1_end..kex_packet_len], - ) { - return Err(Error::FailedAuthentication); - } - - // Check rate limits. - if let Some(session) = session.as_ref() { - if (session.state.read().unwrap().last_remote_offer + Application::REKEY_RATE_LIMIT_MS) > current_time { - return Err(Error::RateLimited); - } - } else { - if !app.check_new_session(self, remote_address) { - return Err(Error::RateLimited); - } - } - - // Key agreement: alice (remote) ephemeral NIST P-384 <> local static NIST P-384 - let alice_e_public = - P384PublicKey::from_bytes(&kex_packet[(HEADER_SIZE + 1)..plaintext_end]).ok_or(Error::FailedAuthentication)?; - let noise_es = app - .get_local_s_keypair() - .agree(&alice_e_public) - .ok_or(Error::FailedAuthentication)?; - - // Initial key derivation from starting point, mixing in alice's ephemeral public and the es. - let noise_ik_incomplete_es = Secret(hmac_sha512( - &hmac_sha512(&INITIAL_KEY, alice_e_public.as_bytes()), - noise_es.as_bytes(), - )); - - // Decrypt the encrypted part of the packet payload and authenticate the above key exchange via AES-GCM auth. - let mut c = AesGcm::new( - kbkdf512(noise_ik_incomplete_es.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::(), - false, - ); - c.reset_init_gcm(&message_nonce); - c.crypt_in_place(&mut kex_packet[plaintext_end..payload_end]); - let gcm_tag = &kex_packet[payload_end..aes_gcm_tag_end]; - if !c.finish_decrypt(gcm_tag) { - return Err(Error::FailedAuthentication); - } - - // Parse payload and get alice's session ID, alice's public blob, metadata, and (if present) Alice's Kyber1024 public. - let ( - offer_id, - alice_session_id, - alice_s_public_blob, - alice_metadata, - alice_hk_public_raw, - alice_ratchet_key_fingerprint, - ) = parse_dec_key_offer_after_header(&kex_packet[plaintext_end..kex_packet_len], packet_type)?; - - // We either have a session, in which case they should have supplied a ratchet key fingerprint, or - // we don't and they should not have supplied one. - if session.is_some() != alice_ratchet_key_fingerprint.is_some() { - return Err(Error::FailedAuthentication); - } - - // Extract alice's static NIST P-384 public key from her public blob. - let alice_s_public = Application::extract_s_public_from_raw(alice_s_public_blob).ok_or(Error::InvalidPacket)?; - - // Key agreement: both sides' static P-384 keys. - let noise_ss = app - .get_local_s_keypair() - .agree(&alice_s_public) - .ok_or(Error::FailedAuthentication)?; - - // Mix result of 'ss' agreement into master key. - let noise_ik_incomplete_es_ss = Secret(hmac_sha512(noise_ik_incomplete_es.as_bytes(), noise_ss.as_bytes())); - drop(noise_ik_incomplete_es); - - // Authenticate entire packet with HMAC-SHA384, verifying alice's identity via 'ss' secret that was - // just mixed into the key. - if !secure_eq( - &hmac_sha384_2( - kbkdf512(noise_ik_incomplete_es_ss.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), - &message_nonce, - &kex_packet_saved_ciphertext[HEADER_SIZE..aes_gcm_tag_end], - ), - &kex_packet[aes_gcm_tag_end..hmac1_end], - ) { - return Err(Error::FailedAuthentication); - } - - // Alice's offer has been verified and her current key state reconstructed. - - // Perform checks and match ratchet key if there's an existing session, or gate (via host) and - // then create new sessions. - let (new_session, ratchet_key, last_ratchet_count) = if let Some(session) = session.as_ref() { - // Existing session identity must match the one in this offer. - if !secure_eq(&session.remote_s_public_blob_hash, &SHA384::hash(&alice_s_public_blob)) { - return Err(Error::FailedAuthentication); - } - - // Match ratchet key fingerprint and fail if no match, which likely indicates an old offer packet. - let alice_ratchet_key_fingerprint = alice_ratchet_key_fingerprint.unwrap(); - let mut ratchet_key = None; - let mut last_ratchet_count = 0; - let state = session.state.read().unwrap(); - for k in state.session_keys.iter() { - if let Some(k) = k.as_ref() { - if public_fingerprint_of_secret(k.ratchet_key.as_bytes())[..16].eq(alice_ratchet_key_fingerprint) { - ratchet_key = Some(k.ratchet_key.clone()); - last_ratchet_count = k.ratchet_count; - break; - } - } - } - if ratchet_key.is_none() { - return Ok(ReceiveResult::Ignored); // old packet? - } - - (None, ratchet_key, last_ratchet_count) - } else { - if let Some((new_session_id, psk, associated_object)) = - app.accept_new_session(self, remote_address, alice_s_public_blob, alice_metadata) - { - let header_check_cipher = Aes::new( - kbkdf512(noise_ss.as_bytes(), KBKDF_KEY_USAGE_LABEL_HEADER_CHECK).first_n::(), - ); - ( - Some(Session:: { - id: new_session_id, - application_data: associated_object, - receive_window: std::array::from_fn(|_| AtomicU64::new(0)), - send_counter: AtomicU64::new(1), - psk, - noise_ss, - header_check_cipher, - state: RwLock::new(SessionMutableState { - remote_session_id: Some(alice_session_id), - session_keys: [None, None], - cur_session_key_idx: 0, - offer: None, - last_remote_offer: current_time, - }), - remote_s_public_blob_hash: SHA384::hash(&alice_s_public_blob), - remote_s_public_p384_bytes: alice_s_public.as_bytes().clone(), - defrag: Mutex::new(RingBufferMap::new(random::xorshift64_random() as u32)), - }), - None, - 0, - ) - } else { - return Err(Error::NewSessionRejected); - } - }; - - // Set 'session' to a reference to either the existing or the new session. - let existing_session = session; - let session = existing_session.as_ref().map_or_else(|| new_session.as_ref().unwrap(), |s| &*s); - - if !session.update_receive_window(counter) { - return Ok(ReceiveResult::Ignored); - } - - // Generate our ephemeral NIST P-384 key pair. - let bob_e_keypair = P384KeyPair::generate(); - - // Key agreement: both sides' ephemeral P-384 public keys. - let noise_ee = bob_e_keypair.agree(&alice_e_public).ok_or(Error::FailedAuthentication)?; - - // Key agreement: bob (local) static NIST P-384, alice (remote) ephemeral P-384. - let noise_se = bob_e_keypair.agree(&alice_s_public).ok_or(Error::FailedAuthentication)?; - - // Mix in the psk, the key to this point, our ephemeral public, ee, and se, completing Noise_IK. - // - // FIPS note: the order of HMAC parameters are flipped here from the usual Noise HMAC(key, X). That's because - // NIST/FIPS allows HKDF with HMAC(salt, key) and salt is allowed to be anything. This way if the PSK is not - // FIPS compliant the compliance of the entire key derivation is not invalidated. Both inputs are secrets of - // fixed size so this shouldn't matter cryptographically. - let noise_ik_complete = Secret(hmac_sha512( - session.psk.as_bytes(), - &hmac_sha512( - &hmac_sha512( - &hmac_sha512(noise_ik_incomplete_es_ss.as_bytes(), bob_e_keypair.public_key_bytes()), - noise_ee.as_bytes(), - ), - noise_se.as_bytes(), - ), - )); - drop(noise_ik_incomplete_es_ss); - drop(noise_ee); - drop(noise_se); - - // At this point we've completed Noise_IK key derivation with NIST P-384 ECDH, but now for hybrid and ratcheting... - - // Generate a Kyber encapsulated ciphertext if Kyber is enabled and the other side sent us a public key. - let (bob_hk_public, hybrid_kk) = if JEDI && alice_hk_public_raw.len() > 0 { - if let Ok((bob_hk_public, hybrid_kk)) = - pqc_kyber::encapsulate(alice_hk_public_raw, &mut random::SecureRandom::default()) - { - (Some(bob_hk_public), Some(Secret(hybrid_kk))) - } else { - return Err(Error::FailedAuthentication); - } - } else { - (None, None) - }; - - //////////////////////////////////////////////////////////////// - // packet encoding for noise key counter offer - // <- e, ee, se - //////////////////////////////////////////////////////////////// - - let next_ratchet_count = last_ratchet_count + 1; - - let mut reply_buf = [0_u8; KEX_BUF_LEN]; - let reply_counter = session.send_counter.fetch_add(1, Ordering::SeqCst); - let mut idx = HEADER_SIZE; - - idx = safe_write_all(&mut reply_buf, idx, &[SESSION_PROTOCOL_VERSION])?; - idx = safe_write_all(&mut reply_buf, idx, bob_e_keypair.public_key_bytes())?; - let plaintext_end = idx; - - idx = safe_write_all(&mut reply_buf, idx, offer_id)?; - idx = safe_write_all(&mut reply_buf, idx, session.id.as_bytes())?; - idx = varint_safe_write(&mut reply_buf, idx, 0)?; // they don't need our static public; they have it - idx = varint_safe_write(&mut reply_buf, idx, 0)?; // no meta-data in counter-offers (could be used in the future) - if let Some(bob_hk_public) = bob_hk_public.as_ref() { - idx = safe_write_all(&mut reply_buf, idx, &[HYBRID_KEY_TYPE_KYBER1024])?; - idx = safe_write_all(&mut reply_buf, idx, bob_hk_public)?; - } else { - idx = safe_write_all(&mut reply_buf, idx, &[HYBRID_KEY_TYPE_NONE])?; - } - if ratchet_key.is_some() { - idx = safe_write_all(&mut reply_buf, idx, &[0x01])?; - idx = safe_write_all(&mut reply_buf, idx, alice_ratchet_key_fingerprint.unwrap())?; - } else { - idx = safe_write_all(&mut reply_buf, idx, &[0x00])?; - } - let payload_end = idx; - - let reply_message_nonce = create_message_nonce(PACKET_TYPE_KEY_COUNTER_OFFER, reply_counter); - - // Encrypt reply packet using final Noise_IK key BEFORE mixing hybrid or ratcheting, since the other side - // must decrypt before doing these things. - let mut c = AesGcm::new( - kbkdf512(noise_ik_complete.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE).first_n::(), - true, - ); - c.reset_init_gcm(&reply_message_nonce); - c.crypt_in_place(&mut reply_buf[plaintext_end..payload_end]); - let gcm_tag = c.finish_encrypt(); - - idx = safe_write_all(&mut reply_buf, idx, &gcm_tag)?; - let aes_gcm_tag_end = idx; - - // Mix ratchet key from previous session key (if any) and Kyber1024 hybrid shared key (if any). - let mut session_key = noise_ik_complete; - if let Some(ratchet_key) = ratchet_key { - session_key = Secret(hmac_sha512(ratchet_key.as_bytes(), session_key.as_bytes())); - } - if let Some(hybrid_kk) = hybrid_kk.as_ref() { - session_key = Secret(hmac_sha512(hybrid_kk.as_bytes(), session_key.as_bytes())); - } - - // Authenticate packet using HMAC-SHA384 with final key. Note that while the final key now has the Kyber secret - // mixed in, this doesn't constitute session authentication with Kyber because there's no static Kyber key - // associated with the remote identity. An attacker who can break NIST P-384 (and has the psk) could MITM the - // Kyber exchange, but you'd need a not-yet-existing quantum computer for that. - let hmac = hmac_sha384_2( - kbkdf512(session_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), - &reply_message_nonce, - &reply_buf[HEADER_SIZE..aes_gcm_tag_end], - ); - idx = safe_write_all(&mut reply_buf, idx, &hmac)?; - let packet_end = idx; - - let session_key = SessionKey::new( - session_key, - Role::Bob, - current_time, - reply_counter, - next_ratchet_count, - false, // Bob can't know yet if Alice got the counter offer - hybrid_kk.is_some(), - ); - - let next_key_index = (next_ratchet_count as usize) & 1; - - let mut state = session.state.write().unwrap(); - let _ = state.remote_session_id.replace(alice_session_id); - let _ = state.session_keys[next_key_index].replace(session_key); - state.last_remote_offer = current_time; - drop(state); - - // Bob now has final key state for this exchange. Yay! Now reply to Alice so she can construct it. - - send_with_fragmentation( - send, - &mut reply_buf[..packet_end], - mtu, - PACKET_TYPE_KEY_COUNTER_OFFER, - u64::from(alice_session_id), - next_ratchet_count, - reply_counter, - &session.header_check_cipher, - )?; - - if new_session.is_some() { - return Ok(ReceiveResult::OkNewSession(new_session.unwrap())); - } else { - return Ok(ReceiveResult::Ok); - } - } - - PACKET_TYPE_KEY_COUNTER_OFFER => { - // bob (remote) -> alice (local) - - //////////////////////////////////////////////////////////////// - // packet decoding for noise key counter offer - // <- e, ee, se - //////////////////////////////////////////////////////////////// - - if kex_packet_len < (HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE + AES_GCM_TAG_SIZE + HMAC_SIZE) { - return Err(Error::InvalidPacket); - } - let plaintext_end = HEADER_SIZE + 1 + P384_PUBLIC_KEY_SIZE; - let payload_end = kex_packet_len - (AES_GCM_TAG_SIZE + HMAC_SIZE); - let aes_gcm_tag_end = kex_packet_len - HMAC_SIZE; - - if let Some(session) = session { - let state = session.state.read().unwrap(); - if let Some(offer) = state.offer.as_ref() { - let bob_e_public = P384PublicKey::from_bytes(&kex_packet[(HEADER_SIZE + 1)..plaintext_end]) - .ok_or(Error::FailedAuthentication)?; - let noise_ee = offer.alice_e_keypair.agree(&bob_e_public).ok_or(Error::FailedAuthentication)?; - let noise_se = app.get_local_s_keypair().agree(&bob_e_public).ok_or(Error::FailedAuthentication)?; - - let noise_ik_complete = Secret(hmac_sha512( - session.psk.as_bytes(), - &hmac_sha512( - &hmac_sha512(&hmac_sha512(offer.ss_key.as_bytes(), bob_e_public.as_bytes()), noise_ee.as_bytes()), - noise_se.as_bytes(), - ), - )); - drop(noise_ee); - drop(noise_se); - - let mut c = AesGcm::new( - kbkdf512(noise_ik_complete.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_BOB_TO_ALICE) - .first_n::(), - false, - ); - c.reset_init_gcm(&message_nonce); - c.crypt_in_place(&mut kex_packet[plaintext_end..payload_end]); - let gcm_tag = &kex_packet[payload_end..aes_gcm_tag_end]; - if !c.finish_decrypt(gcm_tag) { - return Err(Error::FailedAuthentication); - } - - // Alice has now completed Noise_IK with NIST P-384 and verified with GCM auth, but now for hybrid... - - let (offer_id, bob_session_id, _, _, bob_hk_public_raw, bob_ratchet_key_id) = - parse_dec_key_offer_after_header(&kex_packet[plaintext_end..kex_packet_len], packet_type)?; - - // Check that this is a counter offer to the original offer we sent. - if !offer.id.eq(offer_id) { - return Ok(ReceiveResult::Ignored); - } - - // Kyber1024 key agreement if enabled. - let hybrid_kk = if JEDI && bob_hk_public_raw.len() > 0 && offer.alice_hk_keypair.is_some() { - if let Ok(hybrid_kk) = - pqc_kyber::decapsulate(bob_hk_public_raw, &offer.alice_hk_keypair.as_ref().unwrap().secret) - { - Some(Secret(hybrid_kk)) - } else { - return Err(Error::FailedAuthentication); - } - } else { - None - }; - - // The session key starts with the final noise_ik key and may have other things mixed into it below. - let mut session_key = noise_ik_complete; - - // Mix ratchet key from previous session key (if any) and Kyber1024 hybrid shared key (if any). - let last_ratchet_count = if bob_ratchet_key_id.is_some() && offer.ratchet_key.is_some() { - session_key = Secret(hmac_sha512(offer.ratchet_key.as_ref().unwrap().as_bytes(), session_key.as_bytes())); - offer.ratchet_count - } else { - 0 - }; - if let Some(hybrid_kk) = hybrid_kk.as_ref() { - session_key = Secret(hmac_sha512(hybrid_kk.as_bytes(), session_key.as_bytes())); - } - - // Check main packet HMAC for full validation of session key. - if !secure_eq( - &hmac_sha384_2( - kbkdf512(session_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), - &message_nonce, - &kex_packet_saved_ciphertext[HEADER_SIZE..aes_gcm_tag_end], - ), - &kex_packet[aes_gcm_tag_end..kex_packet_len], - ) { - return Err(Error::FailedAuthentication); - } - - // Alice has now completed and validated the full hybrid exchange. - - let reply_counter = session.send_counter.fetch_add(1, Ordering::SeqCst); - let next_ratchet_count = last_ratchet_count + 1; - - let session_key = SessionKey::new( - session_key, - Role::Alice, - current_time, - reply_counter, - next_ratchet_count, - true, // Alice knows Bob got the offer - hybrid_kk.is_some(), - ); - - drop(state); - let mut state = session.state.write().unwrap(); - - let _ = state.remote_session_id.replace(bob_session_id); - let next_key_index = (next_ratchet_count as usize) & 1; - let _ = state.session_keys[next_key_index].replace(session_key); - state.cur_session_key_idx = next_key_index; - let _ = state.offer.take(); - - return Ok(ReceiveResult::Ok); - } - } - - // Just ignore counter-offers that are out of place. They probably indicate that this side - // restarted and needs to establish a new session. - return Ok(ReceiveResult::Ignored); - } - - _ => return Err(Error::InvalidPacket), - } - */ } } } -/* -/// Create an send an ephemeral offer, populating ret_ephemeral_offer on success. -fn send_ephemeral_offer( - send: &mut SendFunction, - counter: u64, - alice_session_id: SessionId, - bob_session_id: Option, - alice_s_public_blob: &[u8], - alice_metadata: &[u8], - bob_s_public: &P384PublicKey, - bob_s_public_blob_hash: &[u8], - noise_ss: &Secret<48>, - current_key: Option<&SessionKey>, - header_check_cipher: Option<&Aes>, // None to use one based on the recipient's public key for initial contact - mtu: usize, - current_time: i64, - ret_ephemeral_offer: &mut Option, // We want to prevent copying the EphemeralOffer up the stack because it's very big. ret_ephemeral_offer will be overwritten with the returned EphemeralOffer when the call completes. -) -> Result<(), Error> { - // Generate a NIST P-384 pair. - let alice_e_keypair = P384KeyPair::generate(); - - // Perform key agreement with the other side's static P-384 public key. - let noise_es = alice_e_keypair.agree(bob_s_public).ok_or(Error::InvalidPacket)?; - - // Generate a Kyber1024 (hybrid PQ crypto) pair if enabled. - let alice_hk_keypair = if JEDI { - Some(pqc_kyber::keypair(&mut random::SecureRandom::get())) - } else { - None - }; - - // Get ratchet key for current key if one exists. - let (ratchet_key, ratchet_count) = if let Some(current_key) = current_key { - (Some(current_key.ratchet_key.clone()), current_key.ratchet_count) - } else { - (None, 0) - }; - - // Random ephemeral offer ID - let id: [u8; 16] = random::get_bytes_secure(); - - //////////////////////////////////////////////////////////////// - // packet encoding for noise initial key offer and for noise rekeying - // -> e, es, s, ss - //////////////////////////////////////////////////////////////// - - // Create ephemeral offer packet (not fragmented yet). - let mut packet_buf = [0_u8; 4096]; - let mut idx = HEADER_SIZE; - - idx = safe_write_all(&mut packet_buf, idx, &[SESSION_PROTOCOL_VERSION])?; - //TODO: check this, the below line is supposed to be the blob, not just the key, right? - idx = safe_write_all(&mut packet_buf, idx, alice_e_keypair.public_key_bytes())?; - let plaintext_end = idx; - - idx = safe_write_all(&mut packet_buf, idx, &id)?; - idx = safe_write_all(&mut packet_buf, idx, alice_session_id.as_bytes())?; - idx = varint_safe_write(&mut packet_buf, idx, alice_s_public_blob.len() as u64)?; - idx = safe_write_all(&mut packet_buf, idx, alice_s_public_blob)?; - idx = varint_safe_write(&mut packet_buf, idx, alice_metadata.len() as u64)?; - idx = safe_write_all(&mut packet_buf, idx, alice_metadata)?; - if let Some(hkp) = alice_hk_keypair { - idx = safe_write_all(&mut packet_buf, idx, &[HYBRID_KEY_TYPE_KYBER1024])?; - idx = safe_write_all(&mut packet_buf, idx, &hkp.public)?; - } else { - idx = safe_write_all(&mut packet_buf, idx, &[HYBRID_KEY_TYPE_NONE])?; - } - if let Some(ratchet_key) = ratchet_key.as_ref() { - idx = safe_write_all(&mut packet_buf, idx, &[0x01])?; - idx = safe_write_all(&mut packet_buf, idx, &public_fingerprint_of_secret(ratchet_key.as_bytes())[..16])?; - } else { - idx = safe_write_all(&mut packet_buf, idx, &[0x00])?; - } - let payload_end = idx; - - // Create ephemeral agreement secret. - let es_key = Secret(hmac_sha512( - &hmac_sha512(&INITIAL_KEY, alice_e_keypair.public_key_bytes()), - noise_es.as_bytes(), - )); - - let bob_session_id = bob_session_id.map_or(0u64, |i| u64::from(i)); - - let message_nonce = create_message_nonce(PACKET_TYPE_INITIAL_KEY_OFFER, counter); - - // Encrypt packet and attach AES-GCM tag. - let gcm_tag = { - let mut c = AesGcm::new( - kbkdf512(es_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_AES_GCM_ALICE_TO_BOB).first_n::(), - true, - ); - c.reset_init_gcm(&message_nonce); - c.crypt_in_place(&mut packet_buf[plaintext_end..payload_end]); - c.finish_encrypt() - }; - - idx = safe_write_all(&mut packet_buf, idx, &gcm_tag)?; - let aes_gcm_tag_end = idx; - - // Mix in static secret. - let ss_key = Secret(hmac_sha512(es_key.as_bytes(), noise_ss.as_bytes())); - drop(es_key); - - // HMAC packet using static + ephemeral key. - let hmac1 = hmac_sha384_2( - kbkdf512(ss_key.as_bytes(), KBKDF_KEY_USAGE_LABEL_HMAC).first_n::<48>(), - &message_nonce, - &packet_buf[HEADER_SIZE..aes_gcm_tag_end], - ); - idx = safe_write_all(&mut packet_buf, idx, &hmac1)?; - let hmac1_end = idx; - - // Add secondary HMAC to verify that the caller knows the recipient's full static public identity. - let hmac2 = hmac_sha384_2(bob_s_public_blob_hash, &message_nonce, &packet_buf[HEADER_SIZE..hmac1_end]); - idx = safe_write_all(&mut packet_buf, idx, &hmac2)?; - let packet_end = idx; - - let mut init_header_check_cipher_tmp = None; - send_with_fragmentation( - send, - &mut packet_buf[..packet_end], - mtu, - PACKET_TYPE_INITIAL_KEY_OFFER, - bob_session_id, - ratchet_count, - counter, - header_check_cipher.unwrap_or_else(|| { - init_header_check_cipher_tmp = Some(Aes::new( - kbkdf512(&bob_s_public_blob_hash, KBKDF_KEY_USAGE_LABEL_HEADER_CHECK).first_n::(), - )); - init_header_check_cipher_tmp.as_ref().unwrap() - }), - )?; - - *ret_ephemeral_offer = Some(EphemeralOffer { - id, - creation_time: current_time, - ratchet_count, - ratchet_key, - ss_key, - alice_e_keypair, - alice_hk_keypair, - }); - - Ok(()) -} -*/ - fn set_packet_header( packet: &mut [u8], fragment_count: usize, @@ -1657,7 +1019,7 @@ fn set_packet_header( #[repr(C, packed)] struct MessageNonce(u64, u32); -/// Create a 12-bit AES-GCM nonce. +/// Create a 96-bit AES-GCM nonce. /// /// The primary information that we want to be contained here is the counter and the /// packet type. The former makes this unique and the latter's inclusion authenticates @@ -1710,54 +1072,6 @@ fn send_with_fragmentation( } /* -/// Parse KEY_OFFER and KEY_COUNTER_OFFER starting after the unencrypted public key part. -fn parse_dec_key_offer_after_header( - incoming_packet: &[u8], - packet_type: u8, -) -> Result<(&[u8], SessionId, &[u8], &[u8], &[u8], Option<&[u8]>), Error> { - let mut p = &incoming_packet[..]; - let offer_id = safe_read_exact(&mut p, 16)?; - - let mut session_id_buf = 0_u64.to_ne_bytes(); - session_id_buf[..SESSION_ID_SIZE].copy_from_slice(safe_read_exact(&mut p, SESSION_ID_SIZE)?); - let alice_session_id = SessionId::new_from_u64_le(u64::from_ne_bytes(session_id_buf)).ok_or(Error::InvalidPacket)?; - - let alice_s_public_blob_len = varint_safe_read(&mut p)?; - let alice_s_public_blob = safe_read_exact(&mut p, alice_s_public_blob_len as usize)?; - - let alice_metadata_len = varint_safe_read(&mut p)?; - let alice_metadata = safe_read_exact(&mut p, alice_metadata_len as usize)?; - - let alice_hk_public_raw = match safe_read_exact(&mut p, 1)?[0] { - HYBRID_KEY_TYPE_KYBER1024 => { - if packet_type == PACKET_TYPE_INITIAL_KEY_OFFER { - safe_read_exact(&mut p, pqc_kyber::KYBER_PUBLICKEYBYTES)? - } else { - safe_read_exact(&mut p, pqc_kyber::KYBER_CIPHERTEXTBYTES)? - } - } - _ => &[], - }; - - if p.is_empty() { - return Err(Error::InvalidPacket); - } - let alice_ratchet_key_fingerprint = if safe_read_exact(&mut p, 1)?[0] == 0x01 { - Some(safe_read_exact(&mut p, 16)?) - } else { - None - }; - - Ok(( - offer_id, //always 16 bytes - alice_session_id, - alice_s_public_blob, - alice_metadata, - alice_hk_public_raw, - alice_ratchet_key_fingerprint, //always 16 bytes - )) -} - impl SessionKey { /// Create a new symmetric shared session key and set its key expiration times, etc. fn new(key: Secret<64>, role: Role, current_time: i64, current_counter: u64, ratchet_count: u64, confirmed: bool, jedi: bool) -> Self { @@ -1822,46 +1136,6 @@ impl SessionKey { self.receive_cipher_pool.lock().unwrap().push(c); } } - -/// Write src into buffer starting at the index idx. If buffer cannot fit src at that location, nothing at all is written and Error::UnexpectedBufferOverrun is returned. No other errors can be returned by this function. An idx incremented by the amount written is returned. -fn safe_write_all(buffer: &mut [u8], idx: usize, src: &[u8]) -> Result { - let dest = &mut buffer[idx..]; - let amt = src.len(); - if dest.len() >= amt { - dest[..amt].copy_from_slice(src); - Ok(idx + amt) - } else { - unlikely_branch(); - Err(Error::UnexpectedBufferOverrun) - } -} - -/// Write a variable length integer, which can consume up to 10 bytes. Uses safe_write_all to do so. -fn varint_safe_write(buffer: &mut [u8], idx: usize, v: u64) -> Result { - let mut b = [0_u8; varint::VARINT_MAX_SIZE_BYTES]; - let i = varint::encode(&mut b, v); - safe_write_all(buffer, idx, &b[0..i]) -} - -/// Read exactly amt bytes from src and return the slice those bytes reside in. If src is smaller than amt, Error::InvalidPacket is returned. if the read was successful src is incremented to start at the first unread byte. -fn safe_read_exact<'a>(src: &mut &'a [u8], amt: usize) -> Result<&'a [u8], Error> { - if src.len() >= amt { - let (a, b) = src.split_at(amt); - *src = b; - Ok(a) - } else { - unlikely_branch(); - Err(Error::InvalidPacket) - } -} - -/// Read a variable length integer, which can consume up to 10 bytes. Uses varint_safe_read to do so. -fn varint_safe_read(src: &mut &[u8]) -> Result { - let (v, amt) = varint::decode(*src).ok_or(Error::InvalidPacket)?; - let (_, b) = src.split_at(amt); - *src = b; - Ok(v) -} */ /// Shortcut to HMAC data split into two slices.