93 lines
3.5 KiB
Rust
93 lines
3.5 KiB
Rust
|
|
//! Phase 3 acceptance test for the Noise_PQ handshake: an initiator and a
|
||
|
|
//! responder exchange the three messages over a real `tokio::net::TcpStream`
|
||
|
|
//! loopback pair, and both ends derive byte-identical session keys.
|
||
|
|
//!
|
||
|
|
//! This validates that the wire bytes flow correctly through an async socket
|
||
|
|
//! (no framing assumption beyond size-prefixed fixed-length messages) and
|
||
|
|
//! that the handshake works in the actual transport environment a Montana
|
||
|
|
//! node would face. Full libp2p Swarm integration is the next step
|
||
|
|
//! (Phase 3 — libp2p custom upgrade) and is tracked in DEV-014.
|
||
|
|
|
||
|
|
use mt_crypto::{keypair_from_seed, keypair_from_seed_mlkem, KEYPAIR_SEED_SIZE};
|
||
|
|
use mt_noise_pq::*;
|
||
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||
|
|
use tokio::net::{TcpListener, TcpStream};
|
||
|
|
|
||
|
|
async fn run_initiator(
|
||
|
|
addr: std::net::SocketAddr,
|
||
|
|
rs_kem_pk: mt_crypto::MlkemPublicKey,
|
||
|
|
is_id_pk: mt_crypto::PublicKey,
|
||
|
|
is_id_sk: mt_crypto::SecretKey,
|
||
|
|
) -> ([u8; 32], [u8; 32], [u8; 32]) {
|
||
|
|
let mut sock = TcpStream::connect(addr).await.unwrap();
|
||
|
|
sock.set_nodelay(true).ok();
|
||
|
|
|
||
|
|
let (msg1, init_state) = initiator_send_msg1(&rs_kem_pk, is_id_sk, is_id_pk).unwrap();
|
||
|
|
sock.write_all(&msg1).await.unwrap();
|
||
|
|
|
||
|
|
let mut msg2 = vec![0u8; NOISE_PQ_MSG2_SIZE];
|
||
|
|
sock.read_exact(&mut msg2).await.unwrap();
|
||
|
|
|
||
|
|
let init_after_msg2 = initiator_receive_msg2(&msg2, init_state).unwrap();
|
||
|
|
let (msg3, session) = initiator_send_msg3(init_after_msg2).unwrap();
|
||
|
|
sock.write_all(&msg3).await.unwrap();
|
||
|
|
sock.shutdown().await.ok();
|
||
|
|
|
||
|
|
(
|
||
|
|
session.sk_i_to_r,
|
||
|
|
session.sk_r_to_i,
|
||
|
|
session.transcript_hash,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn run_responder(
|
||
|
|
listener: TcpListener,
|
||
|
|
rs_kem_sk: mt_crypto::MlkemSecretKey,
|
||
|
|
rs_id_pk: mt_crypto::PublicKey,
|
||
|
|
rs_id_sk: mt_crypto::SecretKey,
|
||
|
|
) -> ([u8; 32], [u8; 32], [u8; 32]) {
|
||
|
|
let (mut sock, _peer) = listener.accept().await.unwrap();
|
||
|
|
sock.set_nodelay(true).ok();
|
||
|
|
|
||
|
|
let mut msg1 = vec![0u8; NOISE_PQ_MSG1_SIZE];
|
||
|
|
sock.read_exact(&mut msg1).await.unwrap();
|
||
|
|
|
||
|
|
let resp_state = responder_receive_msg1(&msg1, &rs_kem_sk, rs_id_sk, rs_id_pk).unwrap();
|
||
|
|
let (msg2, resp_after_msg2) = responder_send_msg2(resp_state).unwrap();
|
||
|
|
sock.write_all(&msg2).await.unwrap();
|
||
|
|
|
||
|
|
let mut msg3 = vec![0u8; NOISE_PQ_MSG3_SIZE];
|
||
|
|
sock.read_exact(&mut msg3).await.unwrap();
|
||
|
|
|
||
|
|
let session = responder_receive_msg3(&msg3, resp_after_msg2).unwrap();
|
||
|
|
sock.shutdown().await.ok();
|
||
|
|
|
||
|
|
(
|
||
|
|
session.sk_i_to_r,
|
||
|
|
session.sk_r_to_i,
|
||
|
|
session.transcript_hash,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||
|
|
async fn tcp_loopback_full_handshake() {
|
||
|
|
// Fixed identities so the test is reproducible across runs.
|
||
|
|
let (rs_kem_pk, rs_kem_sk) =
|
||
|
|
keypair_from_seed_mlkem(&[0x42u8; mt_crypto::MLKEM_SEED_SIZE]).unwrap();
|
||
|
|
let (rs_id_pk, rs_id_sk) = keypair_from_seed(&[0x77u8; KEYPAIR_SEED_SIZE]).unwrap();
|
||
|
|
let (is_id_pk, is_id_sk) = keypair_from_seed(&[0xAAu8; KEYPAIR_SEED_SIZE]).unwrap();
|
||
|
|
|
||
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||
|
|
let addr = listener.local_addr().unwrap();
|
||
|
|
|
||
|
|
let responder = tokio::spawn(run_responder(listener, rs_kem_sk, rs_id_pk, rs_id_sk));
|
||
|
|
let initiator = tokio::spawn(run_initiator(addr, rs_kem_pk, is_id_pk, is_id_sk));
|
||
|
|
|
||
|
|
let init_out = initiator.await.unwrap();
|
||
|
|
let resp_out = responder.await.unwrap();
|
||
|
|
|
||
|
|
assert_eq!(init_out.0, resp_out.0, "sk_i_to_r mismatch over TCP");
|
||
|
|
assert_eq!(init_out.1, resp_out.1, "sk_r_to_i mismatch over TCP");
|
||
|
|
assert_eq!(init_out.2, resp_out.2, "transcript_hash mismatch over TCP");
|
||
|
|
}
|