use alloc::sync::Arc;
use std::sync::OnceLock;
use rustls::{
Error, SignatureScheme, SupportedCipherSuite,
crypto::{CryptoProvider, SupportedKxGroup},
pki_types::PrivateKeyDer,
server::ProducesTickets,
sign::SigningKey,
};
static CRYPTO_EXT: OnceLock<CryptoExt> = OnceLock::new();
#[derive(Clone, Copy)]
pub struct CryptoExt {
pub all_cipher_suites: Option<&'static [SupportedCipherSuite]>,
pub default_cipher_suites: Option<&'static [SupportedCipherSuite]>,
pub all_kx_groups: Option<&'static [&'static dyn SupportedKxGroup]>,
#[allow(clippy::type_complexity)]
pub any_supported_key: Option<fn(&PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error>>,
pub ticketer: fn() -> Result<Arc<dyn ProducesTickets>, Error>,
}
impl CryptoExt {
#[inline]
#[must_use]
pub fn get_ext() -> &'static Self {
CRYPTO_EXT
.get()
.expect("A CryptoProvider must be set before TLS")
}
#[inline]
#[must_use]
pub fn get_provider() -> &'static CryptoProvider {
CryptoProvider::get_default().expect("A CryptoProvider must be set before TLS")
}
#[must_use]
pub fn all_ciphers_or_default(&self) -> &'static [SupportedCipherSuite] {
self.all_cipher_suites
.unwrap_or_else(|| self.default_ciphers_or_provider())
}
#[must_use]
pub fn default_ciphers_or_provider(&self) -> &'static [SupportedCipherSuite] {
self.default_cipher_suites.unwrap_or_else(|| {
CryptoProvider::get_default()
.expect("A CryptoProvider has been set if CryptoExt is set")
.cipher_suites
.as_slice()
})
}
#[must_use]
pub fn all_kx_or_default(&self) -> &'static [&'static dyn SupportedKxGroup] {
self.all_kx_groups.unwrap_or_else(|| {
CryptoProvider::get_default()
.expect("A CryptoProvider has been set if CryptoExt is set")
.kx_groups
.as_slice()
})
}
pub fn any_supported_key(&self, der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error> {
self.any_supported_key.map_or_else(
|| {
let provider = CryptoProvider::get_default()
.expect("A CryptoProvider has been set if CryptoExt is set");
let key = provider.key_provider.load_private_key(der.clone_key())?;
for scheme in provider
.signature_verification_algorithms
.mapping
.iter()
.filter_map(|(scheme, _)| {
(!matches!(
scheme,
SignatureScheme::RSA_PKCS1_SHA1
| SignatureScheme::ECDSA_SHA1_Legacy
| SignatureScheme::Unknown(_),
))
.then_some(*scheme)
})
{
if key.choose_scheme(&[scheme]).is_some() {
return Ok(key);
}
}
Err(Error::General(
"failed to parse private key as RSA, ECDSA, or EdDSA".into(),
))
},
|f| f(der),
)
}
pub fn set_provider(provider: CryptoProvider, extension: Self) -> Result<(), Error> {
provider
.install_default()
.map_err(|_| Error::General("A default CryptoProvider is already set".into()))?;
CRYPTO_EXT
.set(extension)
.map_err(|_| Error::General("A CryptoExt is already set".into()))
}
}