Delete a bunch of commented out old Noise_IK code.

This commit is contained in:
Adam Ierymenko 2023-01-20 20:34:42 -05:00
commit 5763988e5b

View file

@ -953,667 +953,29 @@ impl<Application: ApplicationLayer> ReceiveContext<Application> {
&kbkdf512(noise_es_ee_se_hk_psk.as_bytes(), KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION).as_bytes() &kbkdf512(noise_es_ee_se_hk_psk.as_bytes(), KBKDF_KEY_USAGE_LABEL_KEX_AUTHENTICATION).as_bytes()
[..HMAC_SHA384_SIZE], [..HMAC_SHA384_SIZE],
&message_nonce, &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); return Err(Error::FailedAuthentication);
} }
todo!()
} else { } else {
return Err(Error::NewSessionRejected); 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); 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::<AES_KEY_SIZE>(),
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::<HEADER_CHECK_AES_KEY_SIZE>(),
);
(
Some(Session::<Application> {
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::<AES_KEY_SIZE>(),
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::<AES_KEY_SIZE>(),
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<SendFunction: FnMut(&mut [u8])>(
send: &mut SendFunction,
counter: u64,
alice_session_id: SessionId,
bob_session_id: Option<SessionId>,
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<EphemeralOffer>, // 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::<AES_KEY_SIZE>(),
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::<HEADER_CHECK_AES_KEY_SIZE>(),
));
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( fn set_packet_header(
packet: &mut [u8], packet: &mut [u8],
@ -1657,7 +1019,7 @@ fn set_packet_header(
#[repr(C, packed)] #[repr(C, packed)]
struct MessageNonce(u64, u32); 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 /// 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 /// packet type. The former makes this unique and the latter's inclusion authenticates
@ -1710,54 +1072,6 @@ fn send_with_fragmentation<SendFunction: FnMut(&mut [u8])>(
} }
/* /*
/// 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 { impl SessionKey {
/// Create a new symmetric shared session key and set its key expiration times, etc. /// 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 { 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); 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<usize, Error> {
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<usize, Error> {
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<u64, Error> {
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. /// Shortcut to HMAC data split into two slices.