use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, OnceLock};
use super::policy::{AlgorithmId, SecurityPolicy};
use super::provider::CryptoProvider;
use super::rust_provider::RustCryptoProvider;
#[derive(Debug)]
pub enum SetProviderError {
AlreadySet,
}
impl std::fmt::Display for SetProviderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SetProviderError::AlreadySet => f.write_str(
"crypto provider already set — set_provider() must be called \
once at process startup, before any PDF operation",
),
}
}
}
impl std::error::Error for SetProviderError {}
static ACTIVE: OnceLock<Arc<dyn CryptoProvider>> = OnceLock::new();
pub fn set_provider(provider: Arc<dyn CryptoProvider>) -> Result<(), SetProviderError> {
ACTIVE
.set(provider)
.map_err(|_| SetProviderError::AlreadySet)
}
pub fn active() -> &'static Arc<dyn CryptoProvider> {
ACTIVE.get_or_init(|| Arc::new(RustCryptoProvider::new()))
}
pub fn is_set() -> bool {
ACTIVE.get().is_some()
}
#[derive(Debug)]
pub enum SetPolicyError {
AlreadySet,
}
impl std::fmt::Display for SetPolicyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SetPolicyError::AlreadySet => f.write_str(
"crypto policy already set — set_policy() must be called once at \
process startup, before any PDF crypto operation",
),
}
}
}
impl std::error::Error for SetPolicyError {}
static ACTIVE_POLICY: OnceLock<SecurityPolicy> = OnceLock::new();
pub fn set_policy(policy: SecurityPolicy) -> Result<(), SetPolicyError> {
ACTIVE_POLICY
.set(policy)
.map_err(|_| SetPolicyError::AlreadySet)
}
pub fn active_policy() -> &'static SecurityPolicy {
ACTIVE_POLICY.get_or_init(SecurityPolicy::compat)
}
pub fn is_policy_set() -> bool {
ACTIVE_POLICY.get().is_some()
}
static INVENTORY: AtomicU64 = AtomicU64::new(0);
pub fn record_algorithm_use(alg: AlgorithmId) {
INVENTORY.fetch_or(1u64 << alg.index(), Ordering::Relaxed);
}
pub fn inventory() -> Vec<AlgorithmId> {
let bits = INVENTORY.load(Ordering::Relaxed);
AlgorithmId::ALL
.into_iter()
.filter(|a| bits & (1u64 << a.index()) != 0)
.collect()
}
#[cfg(test)]
mod tests {
use super::super::HashAlgorithm;
use super::*;
#[test]
fn lazy_default_is_rust_crypto() {
let p = active();
assert_eq!(p.name(), "rust-crypto");
#[cfg(feature = "legacy-crypto")]
assert!(p.is_legacy_allowed());
#[cfg(not(feature = "legacy-crypto"))]
assert!(!p.is_legacy_allowed());
#[cfg(feature = "legacy-crypto")]
{
let mut h = p.hasher(HashAlgorithm::Md5).unwrap();
h.update(b"abc");
let out = h.finalize();
assert_eq!(out.len(), 16);
}
#[cfg(not(feature = "legacy-crypto"))]
{
assert!(p.hasher(HashAlgorithm::Md5).is_err());
}
}
#[test]
fn set_provider_after_lazy_init_fails() {
let _ = active();
let attempt = set_provider(Arc::new(RustCryptoProvider::new()));
assert!(matches!(attempt, Err(SetProviderError::AlreadySet)));
}
}