use super::slots::{
Mlkem768X25519Slot, SealedEnvelope, SealedSlots, X25519Slot, AEAD_XCHACHA20_POLY1305,
KEM_MLKEM768X25519, KEM_X25519,
};
#[derive(Debug, Clone, Default)]
pub struct ParsedSlot {
pub epk: Option<Vec<u8>>,
pub kem_ct: Option<Vec<Vec<u8>>>,
pub wrap: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Default)]
pub struct ParsedEnvelope {
pub scheme: Option<i64>,
pub aead: Option<String>,
pub kem: Option<String>,
pub nonce: Option<Vec<u8>>,
pub slots: Option<Vec<ParsedSlot>>,
pub slots_mac: Option<Vec<u8>>,
}
#[must_use]
pub fn sealed_envelope_from_parsed(enc: &ParsedEnvelope) -> Option<SealedEnvelope> {
if enc.scheme != Some(1) || enc.aead.as_deref() != Some(AEAD_XCHACHA20_POLY1305) {
return None;
}
let nonce = enc.nonce.clone()?;
let slots_mac = enc.slots_mac.clone()?;
let slots = enc.slots.as_ref()?;
if slots.is_empty() {
return None;
}
let kem = enc.kem.as_deref()?;
let sealed_slots = match kem {
KEM_X25519 => {
let mut out = Vec::with_capacity(slots.len());
for s in slots {
let epk = s.epk.clone()?;
let wrap = s.wrap.clone()?;
out.push(X25519Slot { epk, wrap });
}
SealedSlots::X25519(out)
}
KEM_MLKEM768X25519 => {
let mut out = Vec::with_capacity(slots.len());
for s in slots {
let kem_ct = s.kem_ct.clone()?;
let wrap = s.wrap.clone()?;
out.push(Mlkem768X25519Slot { kem_ct, wrap });
}
SealedSlots::Mlkem768X25519(out)
}
_ => return None,
};
Some(SealedEnvelope {
scheme: 1,
aead: AEAD_XCHACHA20_POLY1305.to_string(),
kem: kem.to_string(),
nonce,
slots: sealed_slots,
slots_mac,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_none_for_passphrase_only_or_unknown_kem() {
let mut enc = ParsedEnvelope {
scheme: Some(1),
aead: Some(AEAD_XCHACHA20_POLY1305.to_string()),
kem: None,
nonce: Some(vec![0u8; 24]),
slots: Some(vec![ParsedSlot {
epk: Some(vec![0u8; 32]),
kem_ct: None,
wrap: Some(vec![0u8; 48]),
}]),
slots_mac: Some(vec![0u8; 32]),
};
assert!(sealed_envelope_from_parsed(&enc).is_none());
enc.kem = Some("rsa".to_string());
assert!(sealed_envelope_from_parsed(&enc).is_none());
enc.kem = Some(KEM_X25519.to_string());
enc.scheme = Some(2);
assert!(sealed_envelope_from_parsed(&enc).is_none());
}
#[test]
fn builds_a_classical_envelope() {
let enc = ParsedEnvelope {
scheme: Some(1),
aead: Some(AEAD_XCHACHA20_POLY1305.to_string()),
kem: Some(KEM_X25519.to_string()),
nonce: Some(vec![1u8; 24]),
slots: Some(vec![ParsedSlot {
epk: Some(vec![2u8; 32]),
kem_ct: None,
wrap: Some(vec![3u8; 48]),
}]),
slots_mac: Some(vec![4u8; 32]),
};
let env = sealed_envelope_from_parsed(&enc).expect("valid classical envelope");
assert_eq!(env.kem, KEM_X25519);
match env.slots {
SealedSlots::X25519(s) => assert_eq!(s.len(), 1),
SealedSlots::Mlkem768X25519(_) => panic!("expected classical slots"),
}
}
}