use alloc::string::String;
use alloc::vec;
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use core::fmt::{self, Debug};
use mbedtls::pk::{EcGroupId, ECDSA_MAX_LEN};
use mbedtls::rng::RngCallback;
use rustls::{pki_types, SignatureScheme};
use std::sync::Mutex;
use utils::error::mbedtls_err_into_rustls_err;
use utils::hash::{buffer_for_hash_type, rustls_signature_scheme_to_mbedtls_hash_type};
use utils::pk::{get_signature_schema_from_offered, pk_type_to_signature_algo, rustls_signature_scheme_to_mbedtls_pk_options};
struct MbedTlsSigner<T: RngCallback> {
pk: Arc<Mutex<mbedtls::pk::Pk>>,
signature_scheme: SignatureScheme,
rng_provider: fn() -> Option<T>,
}
impl<T: RngCallback> Debug for MbedTlsSigner<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("MbedTlsSigner")
.field(&"Arc<Mutex<mbedtls::pk::Pk>>")
.field(&self.signature_scheme)
.finish()
}
}
impl<T: RngCallback> rustls::sign::Signer for MbedTlsSigner<T> {
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, rustls::Error> {
let hash_type = rustls_signature_scheme_to_mbedtls_hash_type(self.signature_scheme);
let mut hash = buffer_for_hash_type(hash_type).ok_or_else(|| rustls::Error::General("unexpected hash type".into()))?;
let hash_size = mbedtls::hash::Md::hash(hash_type, message, &mut hash).map_err(mbedtls_err_into_rustls_err)?;
let mut pk = self
.pk
.lock()
.expect("poisoned PK lock!");
if let Some(opts) = rustls_signature_scheme_to_mbedtls_pk_options(self.signature_scheme) {
pk.set_options(opts);
}
fn sig_len_for_pk(pk: &mbedtls::pk::Pk) -> usize {
match pk.pk_type() {
mbedtls::pk::Type::Eckey | mbedtls::pk::Type::EckeyDh | mbedtls::pk::Type::Ecdsa => ECDSA_MAX_LEN,
_ => pk.len() / 8,
}
}
let mut sig = vec![0; sig_len_for_pk(&pk)];
let sig_len = pk
.sign(
hash_type,
&hash[..hash_size],
&mut sig,
&mut (self.rng_provider)().ok_or(rustls::Error::FailedToGetRandomBytes)?,
)
.map_err(mbedtls_err_into_rustls_err)?;
sig.truncate(sig_len);
Ok(sig)
}
fn scheme(&self) -> SignatureScheme {
self.signature_scheme
}
}
struct MbedTlsPkSigningKey {
pk: Arc<Mutex<mbedtls::pk::Pk>>,
pk_type: mbedtls::pk::Type,
signature_algorithm: rustls::SignatureAlgorithm,
ec_signature_scheme: Option<SignatureScheme>,
rsa_scheme_prefer_order_list: &'static [SignatureScheme],
}
impl Debug for MbedTlsPkSigningKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MbedTlsPkSigningKeyWrapper")
.field("pk", &"Arc<Mutex<mbedtls::pk::Pk>>")
.field("pk_type", &self.pk_type)
.field("signature_algorithm", &self.signature_algorithm)
.field("ec_signature_scheme", &self.ec_signature_scheme)
.finish()
}
}
pub struct MbedTlsPkSigningKeyWrapper<T: RngCallback> {
signing_key: MbedTlsPkSigningKey,
rng_provider: fn() -> Option<T>,
}
impl<T: RngCallback> Debug for MbedTlsPkSigningKeyWrapper<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MbedTlsPkSigningKeyWrapper")
.field("signing_key", &self.signing_key)
.finish()
}
}
impl<T: RngCallback> MbedTlsPkSigningKeyWrapper<T> {
pub fn new(der: &pki_types::PrivateKeyDer<'_>, rng_provider: fn() -> Option<T>) -> Result<Self, rustls::Error> {
let pk = mbedtls::pk::Pk::from_private_key(der.secret_der(), None)
.map_err(|err| rustls::Error::Other(rustls::OtherError(Arc::new(err))))?;
Self::from_pk(pk, rng_provider)
}
pub fn from_pk(pk: mbedtls::pk::Pk, rng_provider: fn() -> Option<T>) -> Result<Self, rustls::Error> {
let pk_type = pk.pk_type();
let signature_algorithm = pk_type_to_signature_algo(pk_type).ok_or(rustls::Error::General(String::from(
"MbedTlsPkSigningKeyWrapper: invalid pk type",
)))?;
let ec_signature_scheme = if signature_algorithm == rustls::SignatureAlgorithm::ECDSA {
Some(
match pk
.curve()
.map_err(|err| rustls::Error::Other(rustls::OtherError(Arc::new(err))))?
{
EcGroupId::SecP256R1 => SignatureScheme::ECDSA_NISTP256_SHA256,
EcGroupId::SecP384R1 => SignatureScheme::ECDSA_NISTP384_SHA384,
EcGroupId::SecP521R1 => SignatureScheme::ECDSA_NISTP521_SHA512,
_ => {
return Err(rustls::Error::General(String::from(
"MbedTlsPkSigningKeyWrapper: unsupported ec curve",
)))
}
},
)
} else {
None
};
Ok(Self {
signing_key: MbedTlsPkSigningKey {
pk: Arc::new(Mutex::new(pk)),
pk_type,
signature_algorithm,
ec_signature_scheme,
rsa_scheme_prefer_order_list: DEFAULT_RSA_SIGNATURE_SCHEME_PREFER_LIST,
},
rng_provider,
})
}
pub fn set_rsa_signature_scheme_prefer_list(&mut self, prefer_order_list: &'static [SignatureScheme]) {
self.signing_key
.rsa_scheme_prefer_order_list = prefer_order_list
}
}
pub const DEFAULT_RSA_SIGNATURE_SCHEME_PREFER_LIST: &[SignatureScheme] = &[
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::RSA_PKCS1_SHA256,
];
impl<T: RngCallback + 'static> rustls::sign::SigningKey for MbedTlsPkSigningKeyWrapper<T> {
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn rustls::sign::Signer>> {
let scheme = get_signature_schema_from_offered(
self.signing_key.pk_type,
offered,
self.signing_key.ec_signature_scheme,
self.signing_key
.rsa_scheme_prefer_order_list,
)?;
let signer = MbedTlsSigner {
pk: Arc::clone(&self.signing_key.pk),
signature_scheme: scheme,
rng_provider: self.rng_provider,
};
Some(Box::new(signer))
}
fn algorithm(&self) -> rustls::SignatureAlgorithm {
self.signing_key.signature_algorithm
}
}
#[cfg(test)]
mod tests {
use super::*;
use rustls::{sign::SigningKey, SignatureAlgorithm};
#[test]
fn test_signing_key() {
let ec_key_pem = include_str!("../../test-ca/ecdsa/end.key");
let der: pki_types::PrivateKeyDer<'static> =
rustls_pemfile::pkcs8_private_keys(&mut std::io::BufReader::new(ec_key_pem.as_bytes()))
.next()
.unwrap()
.unwrap()
.into();
let key = MbedTlsPkSigningKeyWrapper::new(&der, crate::rng::rng_new).unwrap();
assert_eq!(format!("{key:?}"), "MbedTlsPkSigningKeyWrapper { signing_key: MbedTlsPkSigningKeyWrapper { pk: \"Arc<Mutex<mbedtls::pk::Pk>>\", pk_type: Eckey, signature_algorithm: ECDSA, ec_signature_scheme: Some(ECDSA_NISTP256_SHA256) } }");
assert!(key
.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA1])
.is_none());
let res = key.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]);
assert!(res.is_some());
assert_eq!(
format!("{res:?}"),
"Some(MbedTlsSigner(\"Arc<Mutex<mbedtls::pk::Pk>>\", ECDSA_NISTP256_SHA256))",
);
}
#[test]
fn test_pk_type_to_signature_algo() {
assert_eq!(
pk_type_to_signature_algo(mbedtls::pk::Type::Rsa),
Some(SignatureAlgorithm::RSA)
);
assert_eq!(
pk_type_to_signature_algo(mbedtls::pk::Type::Ecdsa),
Some(SignatureAlgorithm::ECDSA)
);
assert_eq!(
pk_type_to_signature_algo(mbedtls::pk::Type::RsassaPss),
Some(SignatureAlgorithm::RSA)
);
assert_eq!(
pk_type_to_signature_algo(mbedtls::pk::Type::RsaAlt),
Some(SignatureAlgorithm::RSA)
);
assert_eq!(
pk_type_to_signature_algo(mbedtls::pk::Type::Eckey),
Some(SignatureAlgorithm::ECDSA)
);
assert_eq!(
pk_type_to_signature_algo(mbedtls::pk::Type::EckeyDh),
Some(SignatureAlgorithm::ECDSA)
);
assert_eq!(pk_type_to_signature_algo(mbedtls::pk::Type::Custom), None);
assert_eq!(pk_type_to_signature_algo(mbedtls::pk::Type::None), None);
}
}