#![cfg(feature = "reference-frame")]
use std::fs;
use std::path::Path;
use xenia_wire::Session;
const FIXED_KEY: [u8; 32] = *b"xenia-wire-test-vector-key-2026!";
const FIXED_SOURCE_ID: [u8; 8] = *b"XENIATST";
const FIXED_EPOCH: u8 = 0x42;
fn read_hex(path: &Path) -> Vec<u8> {
let s = fs::read_to_string(path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
let mut out = Vec::new();
for line in s.lines() {
for i in (0..line.len()).step_by(2) {
out.push(u8::from_str_radix(&line[i..i + 2], 16).unwrap());
}
}
out
}
fn fresh_receiver() -> Session {
let mut s = Session::with_source_id(FIXED_SOURCE_ID, FIXED_EPOCH);
s.install_key(FIXED_KEY);
s
}
fn vectors_dir() -> &'static Path {
Path::new("test-vectors")
}
#[test]
fn vector_01_hello_frame_opens_to_expected_plaintext() {
let envelope = read_hex(&vectors_dir().join("01_hello_frame.envelope.hex"));
let expected = read_hex(&vectors_dir().join("01_hello_frame.input.hex"));
let mut receiver = fresh_receiver();
let plaintext = receiver.open(&envelope).expect("vector 01 must open");
assert_eq!(plaintext, expected);
}
#[test]
fn vector_02_input_pointer_opens_to_expected_plaintext() {
let envelope = read_hex(&vectors_dir().join("02_input_pointer.envelope.hex"));
let expected = read_hex(&vectors_dir().join("02_input_pointer.input.hex"));
let mut receiver = fresh_receiver();
let plaintext = receiver.open(&envelope).expect("vector 02 must open");
assert_eq!(plaintext, expected);
}
#[test]
fn vector_03_empty_payload_opens_to_expected_plaintext() {
let envelope = read_hex(&vectors_dir().join("03_empty_payload.envelope.hex"));
let expected = read_hex(&vectors_dir().join("03_empty_payload.input.hex"));
let mut receiver = fresh_receiver();
let plaintext = receiver.open(&envelope).expect("vector 03 must open");
assert_eq!(plaintext, expected);
}
#[test]
fn vector_04_long_payload_opens_to_expected_plaintext() {
let envelope = read_hex(&vectors_dir().join("04_long_payload.envelope.hex"));
let expected = read_hex(&vectors_dir().join("04_long_payload.input.hex"));
let mut receiver = fresh_receiver();
let plaintext = receiver.open(&envelope).expect("vector 04 must open");
assert_eq!(plaintext, expected);
}
#[test]
fn vector_05_nonce_structure_has_incrementing_sequence() {
let raw = fs::read_to_string(vectors_dir().join("05_nonce_structure.envelopes.hex"))
.expect("read vector 05");
let mut envelopes: Vec<Vec<u8>> = Vec::new();
let mut current = Vec::<u8>::new();
for line in raw.lines() {
if line.starts_with("--") {
if !current.is_empty() {
envelopes.push(std::mem::take(&mut current));
}
continue;
}
for i in (0..line.len()).step_by(2) {
current.push(u8::from_str_radix(&line[i..i + 2], 16).unwrap());
}
}
if !current.is_empty() {
envelopes.push(current);
}
assert_eq!(envelopes.len(), 3, "vector 05 must contain 3 envelopes");
let mut receiver = fresh_receiver();
for (i, env) in envelopes.iter().enumerate() {
assert!(
receiver.open(env).is_ok(),
"vector 05 envelope {i} must open"
);
let seq = u32::from_le_bytes(env[8..12].try_into().unwrap());
assert_eq!(seq, i as u32, "envelope {i} must have seq {i}");
assert_eq!(env[..8], envelopes[0][..8]);
}
}
#[cfg(feature = "consent")]
#[test]
fn vector_07_consent_request_opens_and_verifies() {
use xenia_wire::consent::ConsentRequest;
let envelope = read_hex(&vectors_dir().join("07_consent_request.envelope.hex"));
let mut receiver = fresh_receiver();
let plaintext = receiver.open(&envelope).expect("vector 07 must open");
let request: ConsentRequest =
bincode::deserialize(&plaintext).expect("vector 07 must deserialize");
assert!(request.verify(None), "vector 07 signature must verify");
assert_eq!(request.core.request_id, 7);
let expected_fp = receiver
.session_fingerprint(request.core.request_id)
.expect("derive fingerprint");
assert_eq!(
request.core.session_fingerprint, expected_fp,
"vector 07 session_fingerprint must match HKDF derivation"
);
assert!(
receiver.verify_consent_request(&request, None),
"vector 07 must pass session-bound verify"
);
}
#[cfg(feature = "consent")]
#[test]
fn vector_08_consent_response_opens_and_verifies() {
use xenia_wire::consent::ConsentResponse;
let envelope = read_hex(&vectors_dir().join("08_consent_response.envelope.hex"));
let mut receiver = fresh_receiver();
let plaintext = receiver.open(&envelope).expect("vector 08 must open");
let response: ConsentResponse =
bincode::deserialize(&plaintext).expect("vector 08 must deserialize");
assert!(response.verify(None), "vector 08 signature must verify");
assert_eq!(response.core.request_id, 7);
assert!(response.core.approved);
assert!(
receiver.verify_consent_response(&response, None),
"vector 08 must pass session-bound verify"
);
}
#[cfg(feature = "consent")]
#[test]
fn vector_09_consent_revocation_opens_and_verifies() {
use xenia_wire::consent::ConsentRevocation;
let envelope = read_hex(&vectors_dir().join("09_consent_revocation.envelope.hex"));
let mut receiver = fresh_receiver();
let plaintext = receiver.open(&envelope).expect("vector 09 must open");
let revocation: ConsentRevocation =
bincode::deserialize(&plaintext).expect("vector 09 must deserialize");
assert!(revocation.verify(None), "vector 09 signature must verify");
assert_eq!(revocation.core.request_id, 7);
assert_eq!(revocation.core.revoker_pubkey.len(), 32);
assert!(
receiver.verify_consent_revocation(&revocation, None),
"vector 09 must pass session-bound verify"
);
}
#[cfg(feature = "consent")]
#[test]
fn vectors_07_08_09_share_session_fingerprint() {
use xenia_wire::consent::{ConsentRequest, ConsentResponse, ConsentRevocation};
let env07 = read_hex(&vectors_dir().join("07_consent_request.envelope.hex"));
let env08 = read_hex(&vectors_dir().join("08_consent_response.envelope.hex"));
let env09 = read_hex(&vectors_dir().join("09_consent_revocation.envelope.hex"));
let mut rx = fresh_receiver();
let req: ConsentRequest = bincode::deserialize(&rx.open(&env07).unwrap()).unwrap();
let resp: ConsentResponse = bincode::deserialize(&rx.open(&env08).unwrap()).unwrap();
let rev: ConsentRevocation = bincode::deserialize(&rx.open(&env09).unwrap()).unwrap();
assert_eq!(
req.core.session_fingerprint, resp.core.session_fingerprint,
"07 and 08 must share fingerprint",
);
assert_eq!(
resp.core.session_fingerprint, rev.core.session_fingerprint,
"08 and 09 must share fingerprint",
);
}
#[cfg(feature = "consent")]
#[test]
fn vectors_08_and_09_share_signing_identity() {
use xenia_wire::consent::{ConsentResponse, ConsentRevocation};
let env08 = read_hex(&vectors_dir().join("08_consent_response.envelope.hex"));
let env09 = read_hex(&vectors_dir().join("09_consent_revocation.envelope.hex"));
let mut receiver = fresh_receiver();
let pt08 = receiver.open(&env08).unwrap();
let pt09 = receiver.open(&env09).unwrap();
let resp: ConsentResponse = bincode::deserialize(&pt08).unwrap();
let rev: ConsentRevocation = bincode::deserialize(&pt09).unwrap();
assert_eq!(resp.core.responder_pubkey, rev.core.revoker_pubkey);
}
#[cfg(feature = "lz4")]
#[test]
fn vector_06_lz4_frame_roundtrips() {
use xenia_wire::{open_frame_lz4, Frame};
let envelope = read_hex(&vectors_dir().join("06_lz4_frame.envelope.hex"));
let mut receiver = fresh_receiver();
let opened: Frame = open_frame_lz4(&envelope, &mut receiver).expect("vector 06 must open");
assert_eq!(opened.frame_id, 9);
assert_eq!(opened.timestamp_ms, 1_700_000_000_500);
assert_eq!(opened.payload, vec![0x5A; 2048]);
}