mod ed25519_signer;
mod error;
mod types;
pub use ed25519_signer::{Ed25519AssertionSigner, Ed25519AssertionVerifier};
pub use error::{MrvbError, MrvbResult};
pub use types::{
AssertionClaims, ClassicalKeyPair, HybridSignature, KeyPairSet, MrvbConfig, MrvbMode,
PqcKeyPair, SignedAssertion,
};
use async_trait::async_trait;
#[async_trait]
pub trait MrvbAssertionSigner: Send + Sync {
async fn sign_assertion(&self, claims: &AssertionClaims) -> MrvbResult<SignedAssertion>;
fn current_keyset_id(&self) -> &str;
fn mode(&self) -> MrvbMode;
fn verifier(&self) -> Box<dyn MrvbAssertionVerifier>;
}
pub trait MrvbAssertionVerifier: Send + Sync {
fn verify_assertion(&self, assertion: &SignedAssertion) -> MrvbResult<AssertionClaims>;
fn keyset_id(&self) -> &str;
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();
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();
if let Some(ref mut sig) = assertion.signature.classical_sig {
sig[0] ^= 0xFF; }
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");
}
}