#![cfg_attr(not(feature = "std"), no_std)]
#![warn(
missing_docs,
clippy::exhaustive_enums,
clippy::exhaustive_structs,
clippy::use_self
)]
#![cfg_attr(rustls_pki_types_docsrs, feature(doc_cfg))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::fmt;
use core::ops::Deref;
use core::time::Duration;
#[cfg(feature = "alloc")]
use pem::{PemObject, PemObjectFilter, SectionKind};
#[cfg(all(
feature = "std",
not(all(target_family = "wasm", target_os = "unknown"))
))]
use std::time::SystemTime;
#[cfg(all(target_family = "wasm", target_os = "unknown", feature = "web"))]
use web_time::SystemTime;
pub mod alg_id;
mod base64;
mod server_name;
#[cfg(feature = "alloc")]
pub mod pem;
pub use alg_id::AlgorithmIdentifier;
pub use server_name::{
AddrParseError, DnsName, InvalidDnsNameError, IpAddr, Ipv4Addr, Ipv6Addr, ServerName,
};
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq)]
pub enum PrivateKeyDer<'a> {
Pkcs1(PrivatePkcs1KeyDer<'a>),
Sec1(PrivateSec1KeyDer<'a>),
Pkcs8(PrivatePkcs8KeyDer<'a>),
}
#[cfg(feature = "alloc")]
impl zeroize::Zeroize for PrivateKeyDer<'static> {
fn zeroize(&mut self) {
match self {
Self::Pkcs1(key) => key.zeroize(),
Self::Sec1(key) => key.zeroize(),
Self::Pkcs8(key) => key.zeroize(),
}
}
}
impl PrivateKeyDer<'_> {
#[cfg(feature = "alloc")]
pub fn clone_key(&self) -> PrivateKeyDer<'static> {
use PrivateKeyDer::*;
match self {
Pkcs1(key) => Pkcs1(key.clone_key()),
Sec1(key) => Sec1(key.clone_key()),
Pkcs8(key) => Pkcs8(key.clone_key()),
}
}
pub fn secret_der(&self) -> &[u8] {
match self {
PrivateKeyDer::Pkcs1(key) => key.secret_pkcs1_der(),
PrivateKeyDer::Sec1(key) => key.secret_sec1_der(),
PrivateKeyDer::Pkcs8(key) => key.secret_pkcs8_der(),
}
}
}
#[cfg(feature = "alloc")]
impl PemObject for PrivateKeyDer<'static> {
fn from_pem(kind: SectionKind, value: Vec<u8>) -> Option<Self> {
match kind {
SectionKind::RsaPrivateKey => Some(Self::Pkcs1(value.into())),
SectionKind::EcPrivateKey => Some(Self::Sec1(value.into())),
SectionKind::PrivateKey => Some(Self::Pkcs8(value.into())),
_ => None,
}
}
}
impl<'a> From<PrivatePkcs1KeyDer<'a>> for PrivateKeyDer<'a> {
fn from(key: PrivatePkcs1KeyDer<'a>) -> Self {
Self::Pkcs1(key)
}
}
impl<'a> From<PrivateSec1KeyDer<'a>> for PrivateKeyDer<'a> {
fn from(key: PrivateSec1KeyDer<'a>) -> Self {
Self::Sec1(key)
}
}
impl<'a> From<PrivatePkcs8KeyDer<'a>> for PrivateKeyDer<'a> {
fn from(key: PrivatePkcs8KeyDer<'a>) -> Self {
Self::Pkcs8(key)
}
}
impl<'a> TryFrom<&'a [u8]> for PrivateKeyDer<'a> {
type Error = &'static str;
fn try_from(key: &'a [u8]) -> Result<Self, Self::Error> {
const SHORT_FORM_LEN_MAX: u8 = 128;
const TAG_SEQUENCE: u8 = 0x30;
const TAG_INTEGER: u8 = 0x02;
if key.first() != Some(&TAG_SEQUENCE) || key.len() < 2 {
return Err(INVALID_KEY_DER_ERR);
}
let skip_len = match key[1] >= SHORT_FORM_LEN_MAX {
false => 2,
true => 2 + (key[1] - SHORT_FORM_LEN_MAX) as usize,
};
let key_bytes = key.get(skip_len..).ok_or(INVALID_KEY_DER_ERR)?;
if matches!(key_bytes, [TAG_INTEGER, 0x01, _, TAG_SEQUENCE, ..]) {
return Ok(Self::Pkcs8(key.into()));
}
if key_bytes.starts_with(&[TAG_INTEGER, 0x01, 0x00]) {
return Ok(Self::Pkcs1(key.into()));
}
if key_bytes.starts_with(&[TAG_INTEGER, 0x01, 0x01]) {
return Ok(Self::Sec1(key.into()));
}
Err(INVALID_KEY_DER_ERR)
}
}
static INVALID_KEY_DER_ERR: &str = "unknown or invalid key format";
#[cfg(feature = "alloc")]
impl TryFrom<Vec<u8>> for PrivateKeyDer<'_> {
type Error = &'static str;
fn try_from(key: Vec<u8>) -> Result<Self, Self::Error> {
Ok(match PrivateKeyDer::try_from(&key[..])? {
PrivateKeyDer::Pkcs1(_) => Self::Pkcs1(key.into()),
PrivateKeyDer::Sec1(_) => Self::Sec1(key.into()),
PrivateKeyDer::Pkcs8(_) => Self::Pkcs8(key.into()),
})
}
}
#[derive(PartialEq, Eq)]
pub struct PrivatePkcs1KeyDer<'a>(Der<'a>);
impl PrivatePkcs1KeyDer<'_> {
#[cfg(feature = "alloc")]
pub fn clone_key(&self) -> PrivatePkcs1KeyDer<'static> {
PrivatePkcs1KeyDer::from(self.0.as_ref().to_vec())
}
pub fn secret_pkcs1_der(&self) -> &[u8] {
self.0.as_ref()
}
}
#[cfg(feature = "alloc")]
impl zeroize::Zeroize for PrivatePkcs1KeyDer<'static> {
fn zeroize(&mut self) {
self.0.0.zeroize()
}
}
#[cfg(feature = "alloc")]
impl PemObjectFilter for PrivatePkcs1KeyDer<'static> {
const KIND: SectionKind = SectionKind::RsaPrivateKey;
}
impl<'a> From<&'a [u8]> for PrivatePkcs1KeyDer<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(Der(BytesInner::Borrowed(slice)))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for PrivatePkcs1KeyDer<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(Der(BytesInner::Owned(vec)))
}
}
impl fmt::Debug for PrivatePkcs1KeyDer<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PrivatePkcs1KeyDer")
.field(&"[secret key elided]")
.finish()
}
}
#[derive(PartialEq, Eq)]
pub struct PrivateSec1KeyDer<'a>(Der<'a>);
impl PrivateSec1KeyDer<'_> {
#[cfg(feature = "alloc")]
pub fn clone_key(&self) -> PrivateSec1KeyDer<'static> {
PrivateSec1KeyDer::from(self.0.as_ref().to_vec())
}
pub fn secret_sec1_der(&self) -> &[u8] {
self.0.as_ref()
}
}
#[cfg(feature = "alloc")]
impl zeroize::Zeroize for PrivateSec1KeyDer<'static> {
fn zeroize(&mut self) {
self.0.0.zeroize()
}
}
#[cfg(feature = "alloc")]
impl PemObjectFilter for PrivateSec1KeyDer<'static> {
const KIND: SectionKind = SectionKind::EcPrivateKey;
}
impl<'a> From<&'a [u8]> for PrivateSec1KeyDer<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(Der(BytesInner::Borrowed(slice)))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for PrivateSec1KeyDer<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(Der(BytesInner::Owned(vec)))
}
}
impl fmt::Debug for PrivateSec1KeyDer<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PrivateSec1KeyDer")
.field(&"[secret key elided]")
.finish()
}
}
#[derive(PartialEq, Eq)]
pub struct PrivatePkcs8KeyDer<'a>(Der<'a>);
impl PrivatePkcs8KeyDer<'_> {
#[cfg(feature = "alloc")]
pub fn clone_key(&self) -> PrivatePkcs8KeyDer<'static> {
PrivatePkcs8KeyDer::from(self.0.as_ref().to_vec())
}
pub fn secret_pkcs8_der(&self) -> &[u8] {
self.0.as_ref()
}
}
#[cfg(feature = "alloc")]
impl zeroize::Zeroize for PrivatePkcs8KeyDer<'static> {
fn zeroize(&mut self) {
self.0.0.zeroize()
}
}
#[cfg(feature = "alloc")]
impl PemObjectFilter for PrivatePkcs8KeyDer<'static> {
const KIND: SectionKind = SectionKind::PrivateKey;
}
impl<'a> From<&'a [u8]> for PrivatePkcs8KeyDer<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(Der(BytesInner::Borrowed(slice)))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for PrivatePkcs8KeyDer<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(Der(BytesInner::Owned(vec)))
}
}
impl fmt::Debug for PrivatePkcs8KeyDer<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PrivatePkcs8KeyDer")
.field(&"[secret key elided]")
.finish()
}
}
#[allow(clippy::exhaustive_structs)]
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct TrustAnchor<'a> {
pub subject: Der<'a>,
pub subject_public_key_info: Der<'a>,
pub name_constraints: Option<Der<'a>>,
}
impl TrustAnchor<'_> {
#[cfg(feature = "alloc")]
pub fn to_owned(&self) -> TrustAnchor<'static> {
#[cfg(not(feature = "std"))]
use alloc::borrow::ToOwned;
TrustAnchor {
subject: self.subject.as_ref().to_owned().into(),
subject_public_key_info: self.subject_public_key_info.as_ref().to_owned().into(),
name_constraints: self
.name_constraints
.as_ref()
.map(|nc| nc.as_ref().to_owned().into()),
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct CertificateRevocationListDer<'a>(Der<'a>);
#[cfg(feature = "alloc")]
impl PemObjectFilter for CertificateRevocationListDer<'static> {
const KIND: SectionKind = SectionKind::Crl;
}
impl AsRef<[u8]> for CertificateRevocationListDer<'_> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Deref for CertificateRevocationListDer<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> From<&'a [u8]> for CertificateRevocationListDer<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(Der::from(slice))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for CertificateRevocationListDer<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(Der::from(vec))
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct CertificateSigningRequestDer<'a>(Der<'a>);
#[cfg(feature = "alloc")]
impl PemObjectFilter for CertificateSigningRequestDer<'static> {
const KIND: SectionKind = SectionKind::Csr;
}
impl AsRef<[u8]> for CertificateSigningRequestDer<'_> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Deref for CertificateSigningRequestDer<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> From<&'a [u8]> for CertificateSigningRequestDer<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(Der::from(slice))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for CertificateSigningRequestDer<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(Der::from(vec))
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct CertificateDer<'a>(Der<'a>);
impl<'a> CertificateDer<'a> {
pub const fn from_slice(bytes: &'a [u8]) -> Self {
Self(Der::from_slice(bytes))
}
}
#[cfg(feature = "alloc")]
impl PemObjectFilter for CertificateDer<'static> {
const KIND: SectionKind = SectionKind::Certificate;
}
impl AsRef<[u8]> for CertificateDer<'_> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Deref for CertificateDer<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> From<&'a [u8]> for CertificateDer<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(Der::from(slice))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for CertificateDer<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(Der::from(vec))
}
}
impl CertificateDer<'_> {
#[cfg(feature = "alloc")]
pub fn into_owned(self) -> CertificateDer<'static> {
CertificateDer(Der(self.0.0.into_owned()))
}
}
#[deprecated(since = "1.7.0", note = "Prefer `SubjectPublicKeyInfoDer` instead")]
pub type SubjectPublicKeyInfo<'a> = SubjectPublicKeyInfoDer<'a>;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct SubjectPublicKeyInfoDer<'a>(Der<'a>);
#[cfg(feature = "alloc")]
impl PemObjectFilter for SubjectPublicKeyInfoDer<'static> {
const KIND: SectionKind = SectionKind::PublicKey;
}
impl AsRef<[u8]> for SubjectPublicKeyInfoDer<'_> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Deref for SubjectPublicKeyInfoDer<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> From<&'a [u8]> for SubjectPublicKeyInfoDer<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(Der::from(slice))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for SubjectPublicKeyInfoDer<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(Der::from(vec))
}
}
impl SubjectPublicKeyInfoDer<'_> {
#[cfg(feature = "alloc")]
pub fn into_owned(self) -> SubjectPublicKeyInfoDer<'static> {
SubjectPublicKeyInfoDer(Der(self.0.0.into_owned()))
}
}
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct EchConfigListBytes<'a>(BytesInner<'a>);
impl EchConfigListBytes<'_> {
#[cfg(feature = "alloc")]
pub fn into_owned(self) -> EchConfigListBytes<'static> {
EchConfigListBytes(self.0.into_owned())
}
}
#[cfg(feature = "alloc")]
impl EchConfigListBytes<'static> {
pub fn config_and_key_from_iter(
iter: impl Iterator<Item = Result<(SectionKind, Vec<u8>), pem::Error>>,
) -> Result<(Self, PrivatePkcs8KeyDer<'static>), pem::Error> {
let mut key = None;
let mut config = None;
for item in iter {
let (kind, data) = item?;
match kind {
SectionKind::PrivateKey => {
key = PrivatePkcs8KeyDer::from_pem(kind, data);
}
SectionKind::EchConfigList => {
config = Self::from_pem(kind, data);
}
_ => continue,
};
if let (Some(_key), Some(_config)) = (&key, &config) {
return Ok((config.take().unwrap(), key.take().unwrap()));
}
}
Err(pem::Error::NoItemsFound)
}
}
#[cfg(feature = "alloc")]
impl PemObjectFilter for EchConfigListBytes<'static> {
const KIND: SectionKind = SectionKind::EchConfigList;
}
impl fmt::Debug for EchConfigListBytes<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
hex(f, self.as_ref())
}
}
impl AsRef<[u8]> for EchConfigListBytes<'_> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Deref for EchConfigListBytes<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> From<&'a [u8]> for EchConfigListBytes<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(BytesInner::Borrowed(slice))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for EchConfigListBytes<'_> {
fn from(vec: Vec<u8>) -> Self {
Self(BytesInner::Owned(vec))
}
}
pub trait SignatureVerificationAlgorithm: Send + Sync + fmt::Debug {
fn verify_signature(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), InvalidSignature>;
fn public_key_alg_id(&self) -> AlgorithmIdentifier;
fn signature_alg_id(&self) -> AlgorithmIdentifier;
fn fips_status(&self) -> FipsStatus {
match self.fips() {
true => FipsStatus::Pending,
false => FipsStatus::Unvalidated,
}
}
fn fips(&self) -> bool {
false
}
}
#[allow(clippy::exhaustive_structs)]
#[derive(Debug, Copy, Clone)]
pub struct InvalidSignature;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UnixTime(u64);
impl UnixTime {
#[cfg(any(
all(
feature = "std",
not(all(target_family = "wasm", target_os = "unknown"))
),
all(target_family = "wasm", target_os = "unknown", feature = "web")
))]
pub fn now() -> Self {
Self::since_unix_epoch(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap(), )
}
pub const fn since_unix_epoch(duration: Duration) -> Self {
Self(duration.as_secs())
}
pub const fn as_secs(&self) -> u64 {
self.0
}
}
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Der<'a>(BytesInner<'a>);
impl<'a> Der<'a> {
pub const fn from_slice(der: &'a [u8]) -> Self {
Self(BytesInner::Borrowed(der))
}
}
impl AsRef<[u8]> for Der<'_> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Deref for Der<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> From<&'a [u8]> for Der<'a> {
fn from(slice: &'a [u8]) -> Self {
Self(BytesInner::Borrowed(slice))
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for Der<'static> {
fn from(vec: Vec<u8>) -> Self {
Self(BytesInner::Owned(vec))
}
}
impl fmt::Debug for Der<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
hex(f, self.as_ref())
}
}
#[derive(Debug, Clone)]
enum BytesInner<'a> {
#[cfg(feature = "alloc")]
Owned(Vec<u8>),
Borrowed(&'a [u8]),
}
#[cfg(feature = "alloc")]
impl BytesInner<'_> {
fn into_owned(self) -> BytesInner<'static> {
BytesInner::Owned(match self {
Self::Owned(vec) => vec,
Self::Borrowed(slice) => slice.to_vec(),
})
}
}
#[cfg(feature = "alloc")]
impl zeroize::Zeroize for BytesInner<'static> {
fn zeroize(&mut self) {
match self {
BytesInner::Owned(vec) => vec.zeroize(),
BytesInner::Borrowed(_) => (),
}
}
}
impl AsRef<[u8]> for BytesInner<'_> {
fn as_ref(&self) -> &[u8] {
match &self {
#[cfg(feature = "alloc")]
BytesInner::Owned(vec) => vec.as_ref(),
BytesInner::Borrowed(slice) => slice,
}
}
}
impl core::hash::Hash for BytesInner<'_> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
state.write(self.as_ref());
}
}
impl PartialEq for BytesInner<'_> {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl Eq for BytesInner<'_> {}
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum FipsStatus {
Unvalidated,
Pending,
#[non_exhaustive]
Certified {
certificate: &'static str,
},
}
fn hex<'a>(f: &mut fmt::Formatter<'_>, payload: impl IntoIterator<Item = &'a u8>) -> fmt::Result {
for (i, b) in payload.into_iter().enumerate() {
if i == 0 {
write!(f, "0x")?;
}
write!(f, "{b:02x}")?;
}
Ok(())
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn der_debug() {
let der = Der::from_slice(&[0x01, 0x02, 0x03]);
assert_eq!(format!("{der:?}"), "0x010203");
}
#[test]
fn alg_id_debug() {
let alg_id = AlgorithmIdentifier::from_slice(&[0x01, 0x02, 0x03]);
assert_eq!(format!("{alg_id:?}"), "0x010203");
}
#[test]
fn bytes_inner_equality() {
let owned_a = BytesInner::Owned(vec![1, 2, 3]);
let owned_b = BytesInner::Owned(vec![4, 5]);
let borrowed_a = BytesInner::Borrowed(&[1, 2, 3]);
let borrowed_b = BytesInner::Borrowed(&[99]);
assert_eq!(owned_a, owned_a);
assert_eq!(owned_b, owned_b);
assert_eq!(borrowed_a, borrowed_a);
assert_eq!(borrowed_b, borrowed_b);
assert_eq!(owned_a, borrowed_a);
assert_eq!(borrowed_a, owned_a);
assert_ne!(owned_a, owned_b);
assert_ne!(owned_b, owned_a);
assert_ne!(borrowed_a, borrowed_b);
assert_ne!(borrowed_b, borrowed_a);
assert_ne!(owned_a, borrowed_b);
assert_ne!(borrowed_b, owned_a);
}
}