use super::{AuthProtocol, PrivProtocol};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CryptoError {
BackendUnavailable,
BackendNotCompiled(CryptoBackend),
UnsupportedAlgorithm(&'static str),
InvalidKeyLength,
CipherError,
InvalidHmacTruncationLength {
requested: usize,
digest_length: usize,
},
PasswordTooShort,
InvalidUsmUsernameLength {
length: usize,
},
}
impl std::fmt::Display for CryptoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BackendUnavailable => write!(f, "no crypto backend is enabled"),
Self::BackendNotCompiled(backend) => {
write!(f, "cryptographic backend {backend:?} is not compiled")
}
Self::UnsupportedAlgorithm(name) => {
write!(f, "unsupported algorithm: {name}")
}
Self::InvalidKeyLength => write!(f, "invalid key length"),
Self::CipherError => write!(f, "cipher operation failed"),
Self::InvalidHmacTruncationLength {
requested,
digest_length,
} => write!(
f,
"invalid HMAC truncation length {requested}; maximum is {digest_length} octets"
),
Self::PasswordTooShort => write!(
f,
"password is shorter than the RFC 3414 minimum of 8 octets"
),
Self::InvalidUsmUsernameLength { length } => write!(
f,
"USM username must contain 1 through 32 octets (got {length})"
),
}
}
}
impl std::error::Error for CryptoError {}
pub type CryptoResult<T> = Result<T, CryptoError>;
#[cfg(feature = "crypto-rustcrypto")]
mod rustcrypto;
#[cfg(feature = "crypto-rustcrypto")]
pub(crate) use rustcrypto::RustCryptoProvider;
#[cfg(feature = "crypto-fips")]
mod fips;
#[cfg(feature = "crypto-fips")]
pub(crate) use fips::AwsLcFipsProvider;
pub(crate) trait CryptoProvider: Send + Sync + 'static {
fn validate_auth_protocol(&self, protocol: AuthProtocol) -> CryptoResult<()>;
fn validate_priv_protocol(&self, protocol: PrivProtocol) -> CryptoResult<()>;
fn password_to_key(&self, protocol: AuthProtocol, password: &[u8]) -> CryptoResult<Vec<u8>>;
fn localize_key(
&self,
protocol: AuthProtocol,
master_key: &[u8],
engine_id: &[u8],
) -> CryptoResult<Vec<u8>>;
fn compute_hmac(
&self,
protocol: AuthProtocol,
key: &[u8],
slices: &[&[u8]],
truncate_len: usize,
) -> CryptoResult<Vec<u8>>;
fn encrypt(
&self,
protocol: PrivProtocol,
key: &[u8],
iv: &[u8],
data: &mut Vec<u8>,
) -> CryptoResult<()>;
fn hash(&self, protocol: AuthProtocol, data: &[u8]) -> CryptoResult<Vec<u8>>;
fn decrypt(
&self,
protocol: PrivProtocol,
key: &[u8],
iv: &[u8],
data: &mut [u8],
) -> CryptoResult<()>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum CryptoBackend {
RustCrypto,
AwsLcFips,
}
impl CryptoBackend {
#[must_use]
pub const fn default_backend() -> Option<Self> {
#[cfg(feature = "crypto-rustcrypto")]
return Some(Self::RustCrypto);
#[cfg(all(not(feature = "crypto-rustcrypto"), feature = "crypto-fips"))]
return Some(Self::AwsLcFips);
#[cfg(not(any(feature = "crypto-rustcrypto", feature = "crypto-fips")))]
return None;
}
#[must_use]
pub const fn is_compiled(self) -> bool {
match self {
Self::RustCrypto => cfg!(feature = "crypto-rustcrypto"),
Self::AwsLcFips => cfg!(feature = "crypto-fips"),
}
}
pub(crate) fn require_default() -> CryptoResult<Self> {
Self::default_backend().ok_or(CryptoError::BackendUnavailable)
}
pub(crate) fn validate_auth_protocol(self, _protocol: AuthProtocol) -> CryptoResult<()> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => RustCryptoProvider.validate_auth_protocol(_protocol),
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => AwsLcFipsProvider.validate_auth_protocol(_protocol),
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
pub(crate) fn validate_priv_protocol(self, _protocol: PrivProtocol) -> CryptoResult<()> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => RustCryptoProvider.validate_priv_protocol(_protocol),
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => AwsLcFipsProvider.validate_priv_protocol(_protocol),
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
pub(crate) fn password_to_key(
self,
_protocol: AuthProtocol,
_password: &[u8],
) -> CryptoResult<Vec<u8>> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => RustCryptoProvider.password_to_key(_protocol, _password),
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => AwsLcFipsProvider.password_to_key(_protocol, _password),
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
pub(crate) fn localize_key(
self,
_protocol: AuthProtocol,
_master_key: &[u8],
_engine_id: &[u8],
) -> CryptoResult<Vec<u8>> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => RustCryptoProvider.localize_key(_protocol, _master_key, _engine_id),
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => AwsLcFipsProvider.localize_key(_protocol, _master_key, _engine_id),
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
pub(crate) fn compute_hmac(
self,
_protocol: AuthProtocol,
_key: &[u8],
_slices: &[&[u8]],
_truncate_len: usize,
) -> CryptoResult<Vec<u8>> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => {
RustCryptoProvider.compute_hmac(_protocol, _key, _slices, _truncate_len)
}
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => {
AwsLcFipsProvider.compute_hmac(_protocol, _key, _slices, _truncate_len)
}
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
pub(crate) fn hash(self, _protocol: AuthProtocol, _data: &[u8]) -> CryptoResult<Vec<u8>> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => RustCryptoProvider.hash(_protocol, _data),
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => AwsLcFipsProvider.hash(_protocol, _data),
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
#[cfg_attr(
not(any(feature = "crypto-rustcrypto", feature = "crypto-fips")),
allow(clippy::ptr_arg)
)]
pub(crate) fn encrypt(
self,
_protocol: PrivProtocol,
_key: &[u8],
_iv: &[u8],
_data: &mut Vec<u8>,
) -> CryptoResult<()> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => RustCryptoProvider.encrypt(_protocol, _key, _iv, _data),
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => AwsLcFipsProvider.encrypt(_protocol, _key, _iv, _data),
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
pub(crate) fn decrypt(
self,
_protocol: PrivProtocol,
_key: &[u8],
_iv: &[u8],
_data: &mut [u8],
) -> CryptoResult<()> {
match self {
#[cfg(feature = "crypto-rustcrypto")]
Self::RustCrypto => RustCryptoProvider.decrypt(_protocol, _key, _iv, _data),
#[cfg(feature = "crypto-fips")]
Self::AwsLcFips => AwsLcFipsProvider.decrypt(_protocol, _key, _iv, _data),
#[cfg(not(feature = "crypto-rustcrypto"))]
Self::RustCrypto => Err(CryptoError::BackendNotCompiled(Self::RustCrypto)),
#[cfg(not(feature = "crypto-fips"))]
Self::AwsLcFips => Err(CryptoError::BackendNotCompiled(Self::AwsLcFips)),
}
}
}