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}