use anyhow::Result;
use async_trait::async_trait;
use zeroize::Zeroize;
use crate::voprf::core::Server as VoprfServer;
use super::CryptoProvider;
pub struct SoftwareCryptoProvider {
server: VoprfServer,
secret_key: [u8; 32],
public_key: [u8; 33],
key_id: String,
context: Vec<u8>,
}
impl SoftwareCryptoProvider {
pub fn new(
secret_key: [u8; 32],
key_id: String,
context: Vec<u8>,
) -> Result<Self> {
let server = VoprfServer::from_secret_key(secret_key, &context)
.map_err(|_| anyhow::anyhow!("invalid secret key for VOPRF"))?;
let public_key = server.public_key_sec1_compressed();
Ok(Self {
server,
secret_key,
public_key,
key_id,
context,
})
}
}
#[async_trait]
impl CryptoProvider for SoftwareCryptoProvider {
async fn voprf_evaluate(&self, blinded: &[u8]) -> Result<Vec<u8>> {
self.server
.evaluate(blinded)
.map_err(|e| anyhow::anyhow!("VOPRF evaluation failed: {:?}", e))
}
async fn derive_mac_key(
&self,
issuer_id: &str,
kid: &str,
epoch: u32,
) -> Result<[u8; 32]> {
Ok(crate::derive_mac_key_v2(
&self.secret_key,
issuer_id,
kid,
epoch,
))
}
async fn sign_token_metadata(
&self,
token_bytes: &[u8],
kid: &str,
exp: i64,
issuer_id: &str,
) -> Result<[u8; 64]> {
crate::compute_token_signature(
&self.secret_key,
token_bytes,
kid,
exp,
issuer_id,
)
.map_err(|e| anyhow::anyhow!("signature generation failed: {:?}", e))
}
fn public_key(&self) -> &[u8] {
&self.public_key
}
fn key_id(&self) -> &str {
&self.key_id
}
fn context(&self) -> &[u8] {
&self.context
}
}
impl Drop for SoftwareCryptoProvider {
fn drop(&mut self) {
self.secret_key.zeroize();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_software_provider_creation() {
let sk = [42u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let provider = SoftwareCryptoProvider::new(sk, kid.clone(), ctx.clone()).unwrap();
assert_eq!(provider.key_id(), "test-key-001");
assert_eq!(provider.context(), b"test-context");
assert_eq!(provider.public_key().len(), 33);
}
#[tokio::test]
async fn test_voprf_evaluation() {
let sk = [42u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let provider = SoftwareCryptoProvider::new(sk, kid, ctx).unwrap();
use crate::voprf::core::Server as VoprfServer;
let test_server = VoprfServer::from_secret_key([1u8; 32], b"ctx").unwrap();
let test_pk = test_server.public_key_sec1_compressed();
let result = provider.voprf_evaluate(&test_pk).await;
assert!(result.is_ok());
let token = result.unwrap();
assert_eq!(token.len(), 131);
assert_eq!(token[0], 0x01); }
#[tokio::test]
async fn test_mac_key_derivation() {
let sk = [42u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let provider = SoftwareCryptoProvider::new(sk, kid, ctx).unwrap();
let key_epoch0 = provider.derive_mac_key("issuer1", "kid1", 0).await.unwrap();
let key_epoch1 = provider.derive_mac_key("issuer1", "kid1", 1).await.unwrap();
let key_epoch0_again = provider.derive_mac_key("issuer1", "kid1", 0).await.unwrap();
assert_eq!(key_epoch0, key_epoch0_again);
assert_ne!(key_epoch0, key_epoch1);
let key_issuer2 = provider.derive_mac_key("issuer2", "kid1", 0).await.unwrap();
assert_ne!(key_epoch0, key_issuer2);
}
#[tokio::test]
async fn test_signature_signing_and_verification() {
let sk = [42u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let provider = SoftwareCryptoProvider::new(sk, kid, ctx).unwrap();
let token = vec![1, 2, 3, 4, 5];
let kid_str = "test-kid";
let exp = 1234567890i64;
let issuer_id = "test-issuer";
let signature = provider
.sign_token_metadata(&token, kid_str, exp, issuer_id)
.await
.unwrap();
assert_eq!(signature.len(), 64);
let pubkey = provider.public_key();
let valid = crate::verify_token_signature(pubkey, &token, &signature, kid_str, exp, issuer_id);
assert!(valid);
let mut bad_token = token.clone();
bad_token[0] ^= 1;
let invalid = crate::verify_token_signature(pubkey, &bad_token, &signature, kid_str, exp, issuer_id);
assert!(!invalid);
}
#[tokio::test]
async fn test_signature_determinism() {
let sk = [42u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let provider = SoftwareCryptoProvider::new(sk, kid, ctx).unwrap();
let token = vec![1, 2, 3];
let kid_str = "kid";
let exp = 123i64;
let issuer_id = "issuer";
let sig1 = provider.sign_token_metadata(&token, kid_str, exp, issuer_id).await.unwrap();
let sig2 = provider.sign_token_metadata(&token, kid_str, exp, issuer_id).await.unwrap();
assert_eq!(sig1, sig2);
}
#[tokio::test]
async fn test_zero_scalar_rejection() {
let sk = [0u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let result = SoftwareCryptoProvider::new(sk, kid, ctx);
assert!(result.is_err());
}
#[tokio::test]
async fn test_suite_id() {
let sk = [42u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let provider = SoftwareCryptoProvider::new(sk, kid, ctx).unwrap();
assert_eq!(provider.suite_id(), "OPRF(P-256, SHA-256)-verifiable");
}
#[tokio::test]
async fn test_secret_key_zeroization() {
let sk = [42u8; 32];
let kid = "test-key-001".to_string();
let ctx = b"test-context".to_vec();
let sk_copy = sk;
{
let provider = SoftwareCryptoProvider::new(sk, kid, ctx).unwrap();
assert_eq!(provider.key_id(), "test-key-001");
}
assert_eq!(sk_copy, [42u8; 32]); }
#[tokio::test]
async fn test_mac_key_zeroization() {
use zeroize::Zeroizing;
let test_key = [0xAAu8; 32];
{
let mac_key = Zeroizing::new(test_key);
assert_eq!(*mac_key, [0xAAu8; 32]);
}
assert_eq!(test_key, [0xAAu8; 32]);
}
}