anubis-wormhole 1.0.0

A post-quantum secure file transfer tool based on the Magic Wormhole protocol.
Documentation
#[cfg(all(feature = "policy-l5", not(feature = "providers-pqclean")))]
compile_error!("policy-l5 requires providers-pqclean (ML-KEM-1024 / ML-DSA-87)");

#[derive(Debug, thiserror::Error)]
pub enum PolicyError {
    #[error("L5 policy: peer negotiated non-L5 KEM ({0})")] NonL5Kem(String),
    #[error("L5 policy: peer negotiated non-L5 signature ({0})")] NonL5Sig(String),
    #[error("L5 policy: sec_cat too low ({0})")] SecCatTooLow(u8),
    #[error("L5 policy: wrong KDF ({0})")] WrongKdf(String),
    #[error("L5 policy: wrong AEAD ({0})")] WrongAead(String),
}

#[cfg(feature = "policy-l5")]
pub fn assert_level5(kem_name: &str, sig_name: Option<&str>) -> Result<(), PolicyError> {
    if kem_name != "ML-KEM-1024" { return Err(PolicyError::NonL5Kem(kem_name.to_string())); }
    if let Some(s) = sig_name { if s != "ML-DSA-87" { return Err(PolicyError::NonL5Sig(s.to_string())); } }
    Ok(())
}

#[cfg(not(feature = "policy-l5"))]
pub fn assert_level5(_k: &str, _s: Option<&str>) -> Result<(), ()> { Ok(()) }

#[cfg(feature = "policy-l5")]
pub fn assert_level5_caps(caps: &serde_json::Value) -> Result<(), PolicyError> {
    let sec = caps.get("sec_cat").and_then(|v| v.as_u64()).unwrap_or(0) as u8;
    if sec < 5 { return Err(PolicyError::SecCatTooLow(sec)); }
    let kem  = caps.get("kem").and_then(|v| v.as_str()).unwrap_or("");
    if kem != "ML-KEM-1024" { return Err(PolicyError::NonL5Kem(kem.to_string())); }
    if let Some(sig) = caps.get("sig").and_then(|v| v.as_str()) {
        if sig != "ML-DSA-87" { return Err(PolicyError::NonL5Sig(sig.to_string())); }
    }
    let kdf  = caps.get("kdf").and_then(|v| v.as_str()).unwrap_or("");
    if kdf != "HKDF-SHA512" { return Err(PolicyError::WrongKdf(kdf.to_string())); }
    let aead = caps.get("aead").and_then(|v| v.as_str()).unwrap_or("");
    if aead != "AES-256-GCM-SIV" { return Err(PolicyError::WrongAead(aead.to_string())); }
    Ok(())
}

#[cfg(not(feature = "policy-l5"))]
pub fn assert_level5_caps(_caps: &serde_json::Value) -> Result<(), ()> { Ok(()) }

#[cfg(feature = "policy-l5")]
pub fn l5_enforcement_enabled() -> bool {
    match std::env::var("ANUBIS_RELAX_L5") {
        Ok(v) => !matches!(v.as_str(), "1"|"true"|"on"),
        Err(_) => true,
    }
}

#[cfg(not(feature = "policy-l5"))]
pub fn l5_enforcement_enabled() -> bool { false }