use quantum_shield::{
seal_multi, Error, HybridCrypto, MultiRecipientEnvelope, CEK_COMMIT_LEN, HEADER_LEN,
MAX_RECIPIENTS, WRAP_LEN,
};
fn recipients(n: usize) -> Vec<HybridCrypto> {
(0..n).map(|_| HybridCrypto::generate().unwrap()).collect()
}
const WRAPS_OFFSET: usize = HEADER_LEN + 2 + CEK_COMMIT_LEN;
#[test]
fn every_recipient_can_open() {
let people = recipients(4);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let msg = b"one message, many recipients";
let env = seal_multi(msg, &pubs).unwrap();
assert_eq!(env.recipient_count(), 4);
let bytes = env.to_bytes();
let parsed = MultiRecipientEnvelope::from_bytes(&bytes).unwrap();
for person in &people {
assert_eq!(person.open_multi(&parsed).unwrap(), msg);
}
}
#[test]
fn non_recipient_cannot_open() {
let people = recipients(3);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let stranger = HybridCrypto::generate().unwrap();
let env = seal_multi(b"secret", &pubs).unwrap();
assert_eq!(
stranger.open_multi(&env).unwrap_err(),
Error::DecryptionFailed
);
}
#[test]
fn single_recipient_works() {
let bob = HybridCrypto::generate().unwrap();
let env = seal_multi(b"just bob", &[bob.public_keys()]).unwrap();
assert_eq!(bob.open_multi(&env).unwrap(), b"just bob");
}
#[test]
fn empty_and_oversized_recipient_lists_rejected() {
let msg = b"x";
assert_eq!(seal_multi(msg, &[]).unwrap_err(), Error::NoRecipients);
}
#[test]
fn reordering_wraps_fails() {
let people = recipients(3);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let bytes = seal_multi(b"order matters", &pubs).unwrap().to_bytes();
let base = WRAPS_OFFSET;
let mut swapped = bytes.clone();
let (a, b) = (base, base + WRAP_LEN);
swapped[a..a + WRAP_LEN].copy_from_slice(&bytes[b..b + WRAP_LEN]);
swapped[b..b + WRAP_LEN].copy_from_slice(&bytes[a..a + WRAP_LEN]);
let env = MultiRecipientEnvelope::from_bytes(&swapped).unwrap();
for person in &people {
assert_eq!(
person.open_multi(&env).unwrap_err(),
Error::DecryptionFailed
);
}
}
#[test]
fn dropping_a_wrap_fails() {
let people = recipients(3);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let bytes = seal_multi(b"complete set", &pubs).unwrap().to_bytes();
let base = WRAPS_OFFSET;
let mut tampered = Vec::new();
tampered.extend_from_slice(&bytes[..HEADER_LEN]);
tampered.extend_from_slice(&2u16.to_be_bytes()); tampered.extend_from_slice(&bytes[HEADER_LEN + 2..base]); tampered.extend_from_slice(&bytes[base..base + 2 * WRAP_LEN]); tampered.extend_from_slice(&bytes[base + 3 * WRAP_LEN..]); let env = MultiRecipientEnvelope::from_bytes(&tampered).unwrap();
assert_eq!(env.recipient_count(), 2);
for person in &people {
assert!(person.open_multi(&env).is_err());
}
}
#[test]
fn duplicating_a_wrap_fails() {
let people = recipients(2);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let bytes = seal_multi(b"no dupes", &pubs).unwrap().to_bytes();
let base = WRAPS_OFFSET;
let wrap0 = &bytes[base..base + WRAP_LEN];
let mut tampered = Vec::new();
tampered.extend_from_slice(&bytes[..HEADER_LEN]);
tampered.extend_from_slice(&3u16.to_be_bytes());
tampered.extend_from_slice(&bytes[HEADER_LEN + 2..base]); tampered.extend_from_slice(&bytes[base..base + 2 * WRAP_LEN]);
tampered.extend_from_slice(wrap0);
tampered.extend_from_slice(&bytes[base + 2 * WRAP_LEN..]);
let env = MultiRecipientEnvelope::from_bytes(&tampered).unwrap();
for person in &people {
assert!(person.open_multi(&env).is_err());
}
}
#[test]
fn count_mismatch_rejected_at_parse() {
let people = recipients(2);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let bytes = seal_multi(b"x", &pubs).unwrap().to_bytes();
let mut lying = bytes.clone();
lying[HEADER_LEN..HEADER_LEN + 2].copy_from_slice(&3u16.to_be_bytes());
assert!(MultiRecipientEnvelope::from_bytes(&lying).is_err());
}
#[test]
fn zero_count_rejected() {
let people = recipients(1);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let bytes = seal_multi(b"x", &pubs).unwrap().to_bytes();
let mut zero = bytes.clone();
zero[HEADER_LEN..HEADER_LEN + 2].copy_from_slice(&0u16.to_be_bytes());
assert_eq!(
MultiRecipientEnvelope::from_bytes(&zero).unwrap_err(),
Error::NoRecipients
);
}
#[test]
fn too_many_recipients_rejected_at_parse() {
let people = recipients(1);
let pubs: Vec<_> = people.iter().map(|p| p.public_keys()).collect();
let bytes = seal_multi(b"x", &pubs).unwrap().to_bytes();
let mut huge = bytes.clone();
huge[HEADER_LEN..HEADER_LEN + 2].copy_from_slice(&((MAX_RECIPIENTS + 1) as u16).to_be_bytes());
assert!(matches!(
MultiRecipientEnvelope::from_bytes(&huge).unwrap_err(),
Error::TooManyRecipients { .. }
));
}
#[test]
fn empty_plaintext_roundtrips() {
let bob = HybridCrypto::generate().unwrap();
let env = seal_multi(b"", &[bob.public_keys()]).unwrap();
assert_eq!(bob.open_multi(&env).unwrap(), b"");
}
#[test]
fn corrupting_the_cek_commitment_fails() {
let bob = HybridCrypto::generate().unwrap();
let mut bytes = seal_multi(b"committed", &[bob.public_keys()])
.unwrap()
.to_bytes();
bytes[HEADER_LEN + 2] ^= 0x01; let env = MultiRecipientEnvelope::from_bytes(&bytes).unwrap();
assert_eq!(bob.open_multi(&env).unwrap_err(), Error::DecryptionFailed);
}