use std::fmt;
use crate::error::SamlError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum CryptoProvider {
RustCrypto,
AwsLc,
}
impl fmt::Display for CryptoProvider {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::RustCrypto => "rustcrypto",
Self::AwsLc => "aws-lc",
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum CryptoFipsStatus {
Disabled,
Uninitialized,
Active,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct CryptoProviderInfo {
provider: CryptoProvider,
fips: CryptoFipsStatus,
}
impl CryptoProviderInfo {
#[must_use]
pub const fn provider(&self) -> CryptoProvider {
self.provider
}
#[must_use]
pub const fn fips_status(&self) -> CryptoFipsStatus {
self.fips
}
}
fn provider_info(info: bergshamra::BackendInfo) -> CryptoProviderInfo {
let provider = match info.document {
bergshamra::BackendId::RustCrypto => CryptoProvider::RustCrypto,
bergshamra::BackendId::AwsLc => CryptoProvider::AwsLc,
};
let fips = match info.fips {
bergshamra::FipsStatus::Disabled => CryptoFipsStatus::Disabled,
bergshamra::FipsStatus::Uninitialized => CryptoFipsStatus::Uninitialized,
bergshamra::FipsStatus::Active => CryptoFipsStatus::Active,
};
CryptoProviderInfo { provider, fips }
}
fn provider_error(action: &str, error: impl fmt::Display) -> SamlError {
SamlError::Crypto(format!("crypto provider {action} failed: {error}"))
}
pub fn crypto_provider_info() -> Result<CryptoProviderInfo, SamlError> {
bergshamra::backend_info()
.map(provider_info)
.map_err(|error| provider_error("inspection", error))
}
pub fn initialize_crypto_provider() -> Result<CryptoProviderInfo, SamlError> {
bergshamra::initialize_backend()
.map(provider_info)
.map_err(|error| provider_error("initialization", error))
}
pub(crate) fn ensure_crypto_provider_initialized() -> Result<(), SamlError> {
initialize_crypto_provider().map(|_| ())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn initialization_reports_selected_provider() -> Result<(), Box<dyn std::error::Error>> {
let info = initialize_crypto_provider()?;
#[cfg(feature = "crypto-rustcrypto")]
assert_eq!(info.provider(), CryptoProvider::RustCrypto);
#[cfg(any(feature = "crypto-aws-lc", feature = "crypto-fips"))]
assert_eq!(info.provider(), CryptoProvider::AwsLc);
Ok(())
}
#[cfg(not(feature = "crypto-fips"))]
#[test]
fn initialization_reports_fips_disabled_without_fips_feature(
) -> Result<(), Box<dyn std::error::Error>> {
let info = initialize_crypto_provider()?;
assert_eq!(info.fips_status(), CryptoFipsStatus::Disabled);
Ok(())
}
#[cfg(feature = "crypto-fips")]
#[test]
fn initialization_attests_active_fips_mode() -> Result<(), Box<dyn std::error::Error>> {
let info = initialize_crypto_provider()?;
assert_eq!(info.fips_status(), CryptoFipsStatus::Active);
Ok(())
}
}