Skip to main content

uvb_mrvb/
lib.rs

1//! uvb-mrvb: MRVB Assertion Signing and Verification for UVB
2//!
3//! This crate provides cryptographic signing and verification for MRVB (Multi-Rail
4//! Verification Bus) assertions with support for:
5//!
6//! - **Classical cryptography**: Ed25519 (fast, widely supported)
7//! - **Post-quantum cryptography**: Dilithium3 (quantum-resistant, optional)
8//! - **Hybrid mode**: Ed25519 + Dilithium3 (best of both worlds, optional)
9//!
10//! ## Architecture
11//!
12//! This implementation matches the MRVB+KMS pack design:
13//! - `KeyPairSet` for managing keypairs with rotation support
14//! - `AssertionClaims` for structured JWT-like claims
15//! - `SignedAssertion` for signed assertion tokens
16//! - `MrvbAssertionSigner` trait for signing operations
17//! - `MrvbAssertionVerifier` trait for verification operations
18//!
19//! ## Example
20//!
21//! ```rust
22//! use uvb_mrvb::{MrvbConfig, MrvbMode, Ed25519AssertionSigner, AssertionClaims, MrvbAssertionSigner};
23//!
24//! # #[tokio::main]
25//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
26//! // Generate a new Ed25519 keypair
27//! let config = MrvbConfig {
28//!     mode: MrvbMode::ClassicalOnly,
29//!     keyset_id: "default".to_string(),
30//! };
31//!
32//! let signer = Ed25519AssertionSigner::generate(config)?;
33//!
34//! // Create assertion claims
35//! let claims = AssertionClaims {
36//!     session_id: "session_123".to_string(),
37//!     user_id: Some("user_456".to_string()),
38//!     rail: "email".to_string(),
39//!     verification_level: "high".to_string(),
40//!     issued_at: chrono::Utc::now(),
41//!     expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
42//!     metadata: Default::default(),
43//! };
44//!
45//! // Sign the claims (async)
46//! let assertion = signer.sign_assertion(&claims).await?;
47//!
48//! // Verify the assertion
49//! let verifier = signer.verifier();
50//! let verified_claims = verifier.verify_assertion(&assertion)?;
51//!
52//! assert_eq!(verified_claims.session_id, "session_123");
53//! # Ok(())
54//! # }
55//! ```
56
57mod ed25519_signer;
58mod error;
59mod types;
60
61// TODO: Implement post-quantum cryptography support
62// #[cfg(feature = "pqc")]
63// mod pqc_signer;
64
65// TODO: Implement hybrid cryptography support
66// #[cfg(feature = "hybrid")]
67// mod hybrid_signer;
68
69pub use ed25519_signer::{Ed25519AssertionSigner, Ed25519AssertionVerifier};
70pub use error::{MrvbError, MrvbResult};
71pub use types::{
72    AssertionClaims, ClassicalKeyPair, HybridSignature, KeyPairSet, MrvbConfig, MrvbMode,
73    PqcKeyPair, SignedAssertion,
74};
75
76// TODO: Re-export PQC signers once implemented
77// #[cfg(feature = "pqc")]
78// pub use pqc_signer::{PqcAssertionSigner, PqcAssertionVerifier};
79
80// TODO: Re-export hybrid signers once implemented
81// #[cfg(feature = "hybrid")]
82// pub use hybrid_signer::{HybridAssertionSigner, HybridAssertionVerifier};
83
84use async_trait::async_trait;
85
86/// Trait for MRVB assertion signing.
87///
88/// Implementations sign assertion claims to create JWT-like tokens.
89#[async_trait]
90pub trait MrvbAssertionSigner: Send + Sync {
91    /// Sign assertion claims to create a signed assertion token.
92    async fn sign_assertion(&self, claims: &AssertionClaims) -> MrvbResult<SignedAssertion>;
93
94    /// Get the current keyset ID being used for signing.
95    fn current_keyset_id(&self) -> &str;
96
97    /// Get the signing mode.
98    fn mode(&self) -> MrvbMode;
99
100    /// Create a verifier from this signer's public keys.
101    fn verifier(&self) -> Box<dyn MrvbAssertionVerifier>;
102}
103
104/// Trait for MRVB assertion verification.
105///
106/// Implementations verify signed assertion tokens and extract claims.
107pub trait MrvbAssertionVerifier: Send + Sync {
108    /// Verify a signed assertion and extract the claims.
109    fn verify_assertion(&self, assertion: &SignedAssertion) -> MrvbResult<AssertionClaims>;
110
111    /// Get the keyset ID this verifier expects.
112    fn keyset_id(&self) -> &str;
113
114    /// Get the verification mode.
115    fn mode(&self) -> MrvbMode;
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[tokio::test]
123    async fn test_ed25519_sign_and_verify() {
124        let config = MrvbConfig {
125            mode: MrvbMode::ClassicalOnly,
126            keyset_id: "test-keyset".to_string(),
127        };
128
129        let signer = Ed25519AssertionSigner::generate(config).unwrap();
130
131        let claims = AssertionClaims {
132            session_id: "session_123".to_string(),
133            user_id: Some("user_456".to_string()),
134            rail: "email".to_string(),
135            verification_level: "high".to_string(),
136            issued_at: chrono::Utc::now(),
137            expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
138            metadata: Default::default(),
139        };
140
141        let assertion = signer.sign_assertion(&claims).await.unwrap();
142
143        // Verify the assertion
144        let verifier = signer.verifier();
145        let verified_claims = verifier.verify_assertion(&assertion).unwrap();
146
147        assert_eq!(verified_claims.session_id, claims.session_id);
148        assert_eq!(verified_claims.user_id, claims.user_id);
149        assert_eq!(verified_claims.rail, claims.rail);
150    }
151
152    #[tokio::test]
153    async fn test_ed25519_invalid_signature_rejected() {
154        let config = MrvbConfig {
155            mode: MrvbMode::ClassicalOnly,
156            keyset_id: "test-keyset".to_string(),
157        };
158
159        let signer = Ed25519AssertionSigner::generate(config).unwrap();
160
161        let claims = AssertionClaims {
162            session_id: "session_123".to_string(),
163            user_id: Some("user_456".to_string()),
164            rail: "email".to_string(),
165            verification_level: "high".to_string(),
166            issued_at: chrono::Utc::now(),
167            expires_at: chrono::Utc::now() + chrono::Duration::hours(1),
168            metadata: Default::default(),
169        };
170
171        let mut assertion = signer.sign_assertion(&claims).await.unwrap();
172
173        // Corrupt the signature
174        if let Some(ref mut sig) = assertion.signature.classical_sig {
175            sig[0] ^= 0xFF; // Flip bits
176        }
177
178        // Verification should fail
179        let verifier = signer.verifier();
180        let result = verifier.verify_assertion(&assertion);
181        assert!(result.is_err());
182    }
183
184    #[tokio::test]
185    async fn test_keyset_id_tracking() {
186        let config = MrvbConfig {
187            mode: MrvbMode::ClassicalOnly,
188            keyset_id: "prod-keyset-42".to_string(),
189        };
190
191        let signer = Ed25519AssertionSigner::generate(config).unwrap();
192        assert_eq!(signer.current_keyset_id(), "prod-keyset-42");
193
194        let verifier = signer.verifier();
195        assert_eq!(verifier.keyset_id(), "prod-keyset-42");
196    }
197}