use super::*;
#[test]
fn test_encrypt_with_counter_no_aad_roundtrip() {
let keypair1 = generate_keypair();
let keypair2 = generate_keypair();
let mut init = HandshakeState::new_initiator(keypair1, keypair2.public_key());
init.set_local_epoch(generate_epoch());
let mut resp = HandshakeState::new_responder(keypair2);
resp.set_local_epoch(generate_epoch());
let msg1 = init.write_message_1().unwrap();
resp.read_message_1(&msg1).unwrap();
let msg2 = resp.write_message_2().unwrap();
init.read_message_2(&msg2).unwrap();
let sender = init.into_session().unwrap();
let mut receiver = resp.into_session().unwrap();
let send_cipher = sender.send_cipher_handle().unwrap();
let counter = 0u64;
let plaintext = b"off-task encrypt";
let nonce = CipherState::counter_to_nonce(counter);
let mut buf = plaintext.to_vec();
send_cipher
.seal_in_place_append_tag(nonce, ring::aead::Aad::empty(), &mut buf)
.expect("worker AEAD encrypt");
let ciphertext = buf;
let decrypted = receiver
.decrypt_with_replay_check(&ciphertext, counter)
.unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_encrypt_with_counter_matches_internal_counter() {
let key = [0x42u8; 32];
let mut a = CipherState::new(key);
let b = CipherState::new(key);
let plaintext = b"same key, same counter, same output";
let counter_a = a.nonce();
let ct_a = a.encrypt(plaintext).unwrap();
let ct_b = b.encrypt_with_counter(plaintext, counter_a).unwrap();
assert_eq!(
ct_a, ct_b,
"explicit-counter encrypt must be byte-identical"
);
assert_eq!(b.nonce(), 0);
}
#[test]
fn test_encrypt_with_counter_and_aad_roundtrip_via_session() {
let keypair1 = generate_keypair();
let keypair2 = generate_keypair();
let mut init = HandshakeState::new_initiator(keypair1, keypair2.public_key());
init.set_local_epoch(generate_epoch());
let mut resp = HandshakeState::new_responder(keypair2);
resp.set_local_epoch(generate_epoch());
let msg1 = init.write_message_1().unwrap();
resp.read_message_1(&msg1).unwrap();
let msg2 = resp.write_message_2().unwrap();
init.read_message_2(&msg2).unwrap();
let mut sender = init.into_session().unwrap();
let mut receiver = resp.into_session().unwrap();
let aad = b"outer header bytes";
let plaintext = b"pipelined send";
let counter = sender.take_send_counter().unwrap();
assert_eq!(counter, 0);
assert_eq!(sender.send_nonce(), 1, "counter reserved → nonce advanced");
let cipher = sender.send_cipher_handle().unwrap();
let nonce = CipherState::counter_to_nonce(counter);
let mut buf = plaintext.to_vec();
cipher
.seal_in_place_append_tag(nonce, ring::aead::Aad::from(aad), &mut buf)
.unwrap();
let ciphertext = buf;
let decrypted = receiver
.decrypt_with_replay_check_and_aad(&ciphertext, counter, aad)
.unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_pipelined_send_counter_reservation_is_single_owner() {
let keypair1 = generate_keypair();
let keypair2 = generate_keypair();
let mut init = HandshakeState::new_initiator(keypair1, keypair2.public_key());
init.set_local_epoch(generate_epoch());
let mut resp = HandshakeState::new_responder(keypair2);
resp.set_local_epoch(generate_epoch());
let msg1 = init.write_message_1().unwrap();
resp.read_message_1(&msg1).unwrap();
let msg2 = resp.write_message_2().unwrap();
init.read_message_2(&msg2).unwrap();
let mut sender = init.into_session().unwrap();
let mut receiver = resp.into_session().unwrap();
let aad = b"reserved-counter header";
let first_counter = sender.take_send_counter().unwrap();
let second_counter = sender.take_send_counter().unwrap();
assert_eq!(first_counter, 0);
assert_eq!(second_counter, 1);
assert_eq!(
sender.current_send_counter(),
2,
"only the coordinator-owned reservation path advances the session counter"
);
let cipher = sender.send_cipher_handle().unwrap();
let mut first = b"first reserved packet".to_vec();
cipher
.seal_in_place_append_tag(
CipherState::counter_to_nonce(first_counter),
ring::aead::Aad::from(aad),
&mut first,
)
.unwrap();
let mut second = b"second reserved packet".to_vec();
cipher
.seal_in_place_append_tag(
CipherState::counter_to_nonce(second_counter),
ring::aead::Aad::from(aad),
&mut second,
)
.unwrap();
assert_eq!(
sender.current_send_counter(),
2,
"worker-side cipher-handle use must not mutate session counter ownership"
);
assert_eq!(
receiver
.decrypt_with_replay_check_and_aad(&first, first_counter, aad)
.unwrap(),
b"first reserved packet"
);
assert_eq!(
receiver
.decrypt_with_replay_check_and_aad(&second, second_counter, aad)
.unwrap(),
b"second reserved packet"
);
let third_counter = sender.current_send_counter();
let third = sender
.encrypt_with_aad(b"inline packet after workers", aad)
.unwrap();
assert_eq!(third_counter, 2);
assert_eq!(sender.current_send_counter(), 3);
assert_eq!(
receiver
.decrypt_with_replay_check_and_aad(&third, third_counter, aad)
.unwrap(),
b"inline packet after workers"
);
}
#[test]
fn test_recv_cipher_clone_matches_decrypt_with_counter_and_aad() {
let keypair1 = generate_keypair();
let keypair2 = generate_keypair();
let mut init = HandshakeState::new_initiator(keypair1, keypair2.public_key());
init.set_local_epoch(generate_epoch());
let mut resp = HandshakeState::new_responder(keypair2);
resp.set_local_epoch(generate_epoch());
let msg1 = init.write_message_1().unwrap();
resp.read_message_1(&msg1).unwrap();
let msg2 = resp.write_message_2().unwrap();
init.read_message_2(&msg2).unwrap();
let mut sender = init.into_session().unwrap();
let mut receiver = resp.into_session().unwrap();
let aad = b"AAD-bound transport header";
let plaintext = b"off-task decrypt";
let counter = sender.current_send_counter();
let ciphertext = sender.encrypt_with_aad(plaintext, aad).unwrap();
assert!(receiver.check_replay(counter).is_ok());
let cipher = receiver.recv_cipher_clone().unwrap();
let nonce = CipherState::counter_to_nonce(counter);
let mut buf = ciphertext.clone();
let worker_plaintext = cipher
.open_in_place(nonce, ring::aead::Aad::from(aad), &mut buf)
.unwrap()
.to_vec();
assert_eq!(worker_plaintext, plaintext);
receiver.accept_replay(counter);
assert!(receiver.check_replay(counter).is_err());
}