uvb-mrvb 0.2.1

Multi-Rail Verification Bus (MRVB) with post-quantum cryptography support
Documentation
//! uvb-mrvb: MRVB Assertion Signing and Verification for UVB
//!
//! This crate provides cryptographic signing and verification for MRVB (Multi-Rail
//! Verification Bus) assertions with support for:
//!
//! - **Classical cryptography**: Ed25519 (fast, widely supported)
//! - **Post-quantum cryptography**: Dilithium3 (quantum-resistant, optional)
//! - **Hybrid mode**: Ed25519 + Dilithium3 (best of both worlds, optional)
//!
//! ## Architecture
//!
//! This implementation matches the MRVB+KMS pack design:
//! - `KeyPairSet` for managing keypairs with rotation support
//! - `AssertionClaims` for structured JWT-like claims
//! - `SignedAssertion` for signed assertion tokens
//! - `MrvbAssertionSigner` trait for signing operations
//! - `MrvbAssertionVerifier` trait for verification operations
//!
//! ## Example
//!
//! ```rust
//! use uvb_mrvb::{MrvbConfig, MrvbMode, Ed25519AssertionSigner, AssertionClaims, MrvbAssertionSigner};
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Generate a new Ed25519 keypair
//! let config = MrvbConfig {
//!     mode: MrvbMode::ClassicalOnly,
//!     keyset_id: "default".to_string(),
//! };
//!
//! let signer = Ed25519AssertionSigner::generate(config)?;
//!
//! // Create assertion claims
//! let claims = AssertionClaims {
//!     session_id: "session_123".to_string(),
//!     user_id: Some("user_456".to_string()),
//!     rail: "email".to_string(),
//!     verification_level: "high".to_string(),
//!     issued_at: chrono::Utc::now(),
//!     expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
//!     metadata: Default::default(),
//! };
//!
//! // Sign the claims (async)
//! let assertion = signer.sign_assertion(&claims).await?;
//!
//! // Verify the assertion
//! let verifier = signer.verifier();
//! let verified_claims = verifier.verify_assertion(&assertion)?;
//!
//! assert_eq!(verified_claims.session_id, "session_123");
//! # Ok(())
//! # }
//! ```

mod ed25519_signer;
mod error;
mod types;

// TODO: Implement post-quantum cryptography support
// #[cfg(feature = "pqc")]
// mod pqc_signer;

// TODO: Implement hybrid cryptography support
// #[cfg(feature = "hybrid")]
// mod hybrid_signer;

pub use ed25519_signer::{Ed25519AssertionSigner, Ed25519AssertionVerifier};
pub use error::{MrvbError, MrvbResult};
pub use types::{
    AssertionClaims, ClassicalKeyPair, HybridSignature, KeyPairSet, MrvbConfig, MrvbMode,
    PqcKeyPair, SignedAssertion,
};

// TODO: Re-export PQC signers once implemented
// #[cfg(feature = "pqc")]
// pub use pqc_signer::{PqcAssertionSigner, PqcAssertionVerifier};

// TODO: Re-export hybrid signers once implemented
// #[cfg(feature = "hybrid")]
// pub use hybrid_signer::{HybridAssertionSigner, HybridAssertionVerifier};

use async_trait::async_trait;

/// Trait for MRVB assertion signing.
///
/// Implementations sign assertion claims to create JWT-like tokens.
#[async_trait]
pub trait MrvbAssertionSigner: Send + Sync {
    /// Sign assertion claims to create a signed assertion token.
    async fn sign_assertion(&self, claims: &AssertionClaims) -> MrvbResult<SignedAssertion>;

    /// Get the current keyset ID being used for signing.
    fn current_keyset_id(&self) -> &str;

    /// Get the signing mode.
    fn mode(&self) -> MrvbMode;

    /// Create a verifier from this signer's public keys.
    fn verifier(&self) -> Box<dyn MrvbAssertionVerifier>;
}

/// Trait for MRVB assertion verification.
///
/// Implementations verify signed assertion tokens and extract claims.
pub trait MrvbAssertionVerifier: Send + Sync {
    /// Verify a signed assertion and extract the claims.
    fn verify_assertion(&self, assertion: &SignedAssertion) -> MrvbResult<AssertionClaims>;

    /// Get the keyset ID this verifier expects.
    fn keyset_id(&self) -> &str;

    /// Get the verification mode.
    fn mode(&self) -> MrvbMode;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_ed25519_sign_and_verify() {
        let config = MrvbConfig {
            mode: MrvbMode::ClassicalOnly,
            keyset_id: "test-keyset".to_string(),
        };

        let signer = Ed25519AssertionSigner::generate(config).unwrap();

        let claims = AssertionClaims {
            session_id: "session_123".to_string(),
            user_id: Some("user_456".to_string()),
            rail: "email".to_string(),
            verification_level: "high".to_string(),
            issued_at: chrono::Utc::now(),
            expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
            metadata: Default::default(),
        };

        let assertion = signer.sign_assertion(&claims).await.unwrap();

        // Verify the assertion
        let verifier = signer.verifier();
        let verified_claims = verifier.verify_assertion(&assertion).unwrap();

        assert_eq!(verified_claims.session_id, claims.session_id);
        assert_eq!(verified_claims.user_id, claims.user_id);
        assert_eq!(verified_claims.rail, claims.rail);
    }

    #[tokio::test]
    async fn test_ed25519_invalid_signature_rejected() {
        let config = MrvbConfig {
            mode: MrvbMode::ClassicalOnly,
            keyset_id: "test-keyset".to_string(),
        };

        let signer = Ed25519AssertionSigner::generate(config).unwrap();

        let claims = AssertionClaims {
            session_id: "session_123".to_string(),
            user_id: Some("user_456".to_string()),
            rail: "email".to_string(),
            verification_level: "high".to_string(),
            issued_at: chrono::Utc::now(),
            expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
            metadata: Default::default(),
        };

        let mut assertion = signer.sign_assertion(&claims).await.unwrap();

        // Corrupt the signature
        if let Some(ref mut sig) = assertion.signature.classical_sig {
            sig[0] ^= 0xFF; // Flip bits
        }

        // Verification should fail
        let verifier = signer.verifier();
        let result = verifier.verify_assertion(&assertion);
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_keyset_id_tracking() {
        let config = MrvbConfig {
            mode: MrvbMode::ClassicalOnly,
            keyset_id: "prod-keyset-42".to_string(),
        };

        let signer = Ed25519AssertionSigner::generate(config).unwrap();
        assert_eq!(signer.current_keyset_id(), "prod-keyset-42");

        let verifier = signer.verifier();
        assert_eq!(verifier.keyset_id(), "prod-keyset-42");
    }
}