use std::fmt::{self, Debug, Formatter};
use rustls::sign;
use serde::{Deserialize, Serialize, Serializer};
use zeroize::Zeroize;
use crate::{Error, Result};
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Certificate(Vec<u8>);
impl AsRef<[u8]> for Certificate {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl From<Certificate> for Vec<u8> {
fn from(certificate: Certificate) -> Self {
certificate.0
}
}
impl Certificate {
#[cfg(feature = "certificate")]
#[cfg_attr(doc, doc(cfg(feature = "certificate")))]
pub fn from_der(certificate: Vec<u8>) -> Result<Self> {
use std::time::Duration;
use webpki::EndEntityCert;
use x509_parser::certificate::X509Certificate;
use crate::error::ParseCertificate;
let _ = match EndEntityCert::from(&certificate) {
Ok(parsed) => parsed,
Err(error) =>
return Err(Error::ParseCertificate {
certificate,
error: ParseCertificate::WebPki(error),
}),
};
let (trailing, parsed) = match X509Certificate::from_der(&certificate) {
Ok((trailing, bytes)) => (trailing, bytes),
Err(error) =>
return Err(Error::ParseCertificate {
certificate,
error: ParseCertificate::X509(error),
}),
};
if !trailing.is_empty() {
return Err(Error::DanglingCertificate {
dangling: trailing.to_owned(),
certificate,
});
}
if let Some(duration) = parsed.validity().time_to_expiration() {
if duration <= Duration::from_secs(1_728_000) {
}
} else {
return Err(Error::ExpiredCertificate(certificate));
}
if parsed
.tbs_certificate
.subject_alternative_name()
.filter(|name| !name.1.general_names.is_empty())
.is_none()
{
return Err(Error::DomainCertificate(certificate));
}
Ok(Self(certificate))
}
#[must_use]
pub fn unchecked_from_der(certificate: Vec<u8>) -> Self {
Self(certificate)
}
}
#[derive(Clone, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Serialize, Zeroize)]
#[zeroize(drop)]
pub struct PrivateKey(Option<Vec<u8>>);
impl Debug for PrivateKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("[[redacted]]")
}
}
impl PrivateKey {
pub fn from_der(private_key: Vec<u8>) -> Result<Self> {
let private_key = rustls::PrivateKey(private_key);
#[allow(box_pointers)]
let _key = sign::any_supported_type(&private_key).map_err(|_error| Error::ParsePrivateKey)?;
Ok(Self(Some(private_key.0)))
}
#[must_use]
pub fn unchecked_from_der(private_key: Vec<u8>) -> Self {
Self(Some(private_key))
}
}
pub trait Dangerous {
#[must_use]
fn as_ref(private_key: &Self) -> &[u8];
#[must_use]
fn into(private_key: Self) -> Vec<u8>;
fn serialize<S: Serializer>(private_key: &Self, serializer: S) -> Result<S::Ok, S::Error>;
}
impl Dangerous for PrivateKey {
fn as_ref(private_key: &Self) -> &[u8] {
#[allow(clippy::expect_used)]
private_key.0.as_deref().expect("value already dropped")
}
fn into(mut private_key: Self) -> Vec<u8> {
#[allow(clippy::expect_used)]
private_key.0.take().expect("value already dropped")
}
fn serialize<S: Serializer>(private_key: &Self, serializer: S) -> Result<S::Ok, S::Error> {
Serializer::serialize_newtype_struct(serializer, "PrivateKey", &private_key.0)
}
}
#[cfg(feature = "certificate")]
#[cfg_attr(doc, doc(cfg(feature = "certificate")))]
pub fn generate_self_signed<S: Into<String>>(domain: S) -> (Certificate, PrivateKey) {
#[allow(clippy::expect_used)]
let key_pair = rcgen::generate_simple_self_signed([domain.into()])
.expect("`rcgen` failed generating a self-signed certificate");
(
#[allow(clippy::expect_used)]
Certificate::unchecked_from_der(
key_pair
.serialize_der()
.expect("`rcgen` failed serializing a certificate"),
),
PrivateKey::unchecked_from_der(key_pair.serialize_private_key_der()),
)
}
#[test]
fn validate() -> anyhow::Result<()> {
let (certificate, private_key) = generate_self_signed("test");
assert_eq!(
certificate,
Certificate::from_der(certificate.clone().into())?
);
assert_eq!(
private_key,
PrivateKey::from_der(Dangerous::into(private_key.clone()))?,
);
Ok(())
}
#[test]
#[allow(box_pointers)]
fn serialize() -> anyhow::Result<()> {
use bincode::{config::DefaultOptions, Options, Serializer};
let (_, private_key) = generate_self_signed("test");
let mut buffer = Vec::new();
Dangerous::serialize(
&private_key,
&mut Serializer::new(
&mut buffer,
DefaultOptions::default()
.with_fixint_encoding()
.allow_trailing_bytes(),
),
)?;
assert_eq!(private_key, bincode::deserialize(&buffer)?);
Ok(())
}
#[test]
fn debug() {
let (_, private_key) = generate_self_signed("test");
assert_eq!("[[redacted]]", format!("{:?}", private_key));
}