use super::{iter, CertChainRef, CertRef};
use alloc::vec::Vec;
use core::ops::Add;
use der::Length;
#[cfg(feature = "x509-cert")]
use {
der::{Encode, SliceWriter},
x509_cert::Certificate,
};
macro_rules! def_owned_wrapper {
($(#[$m:meta])* $name:ident, $borrowed:ident, $decoded:ty, $decodes_as:literal) => {
#[derive(Default, Clone, Eq, Debug)]
$(#[$m])*
pub struct $name {
pub(super) inner: Vec<u8>,
pub(super) position: Length,
pub(super) length: Length,
}
impl PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl $name {
#[doc = concat!(
"Creates a new [`", stringify!($name), " from an arbitrary byte vector."
)]
pub fn new(
inner: impl ::core::convert::Into<::alloc::vec::Vec<u8>>
) -> ::der::Result<Self> {
let inner = inner.into();
let length = ::der::Length::try_from(inner.len())?;
::core::result::Result::Ok(Self {
inner,
position: ::der::Length::ZERO,
length,
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.inner
}
pub fn len(&self) -> Length {
self.length
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[doc = concat!("Decodes the contents as ", $decodes_as, ".")]
#[cfg(feature = "x509-cert")]
pub fn decode_body(&self) -> ::der::Result<$decoded> {
$borrowed::from(self).decode_body()
}
}
impl ::der::FixedTag for $name {
const TAG: ::der::Tag = ::der::Tag::Sequence;
}
impl<'a> ::der::DecodeValue<'a> for $name {
fn decode_value<R: ::der::Reader<'a>>(
reader: &mut R,
header: ::der::Header
) -> ::der::Result<Self> {
$borrowed::decode_value(reader, header).map(::core::convert::Into::into)
}
}
impl ::der::EncodeValue for $name {
fn value_len(&self) -> ::der::Result<::der::Length> {
::core::result::Result::Ok(self.length)
}
fn encode_value(&self, encoder: &mut impl ::der::Writer) -> ::der::Result<()> {
encoder.write(&self.inner)
}
}
impl ::core::convert::From<$borrowed<'_>> for $name {
fn from(value: $borrowed<'_>) -> Self {
(&value).into()
}
}
impl ::core::convert::From<&'_ $borrowed<'_>> for $name {
fn from(value: &'_ $borrowed<'_>) -> Self {
Self {
inner: value.inner.to_vec(),
position: value.position,
length: value.length,
}
}
}
impl ::der::referenced::OwnedToRef for $name {
type Borrowed<'a> = $borrowed<'a>;
fn owned_to_ref(&self) -> Self::Borrowed<'_> {
self.into()
}
}
};
}
def_owned_wrapper!(
Cert,
CertRef,
Certificate,
"a [`Certificate`]"
);
def_owned_wrapper!(
CertChain,
CertChainRef,
Vec<Certificate>,
"a vector of [`Certificate`]s"
);
impl CertChain {
#[cfg(feature = "x509-cert")]
pub fn from_certs(certs: impl AsRef<[Certificate]>) -> der::Result<Self> {
let mut inner = Vec::new();
let certs = certs.as_ref();
let mut length = Length::ZERO;
for cert in certs {
length = length.add(cert.encoded_len()?)?;
}
inner.resize(length.try_into()?, 0);
let mut writer = SliceWriter::new(&mut inner);
for cert in certs {
cert.encode(&mut writer)?;
}
assert_eq!(writer.finish()?.len(), inner.len());
Ok(Self {
inner,
position: Length::ZERO,
length,
})
}
#[cfg(feature = "x509-cert")]
pub fn append_cert(&mut self, cert: &Certificate) -> der::Result<()> {
let length = cert.encoded_len()?;
let full_length = (self.length + length)?;
self.inner.resize(full_length.try_into()?, 0);
let mut writer = SliceWriter::new(&mut self.inner[self.length.try_into()?..]);
cert.encode(&mut writer)?;
assert_eq!(writer.finish()?.len(), TryInto::<usize>::try_into(length)?);
self.length = full_length;
Ok(())
}
pub fn append_bytes(&mut self, bytes: &[u8]) -> der::Result<()> {
let length = Length::try_from(bytes.len())?;
self.length = self.length.add(length)?;
self.inner.extend_from_slice(bytes);
Ok(())
}
pub fn reserve(&mut self, additional: Length) -> der::Result<()> {
additional
.try_into()
.map(|additional| self.inner.reserve(additional))
}
pub fn iter(&self) -> iter::CertIter<'_> {
iter::CertIter {
inner: self.inner.as_slice(),
position: self.position,
}
}
}
#[cfg(all(test, feature = "x509-cert"))]
mod tests {
use der::DecodePem;
use super::*;
const TEST_CERT_PEM: &'static [u8] = include_bytes!("../../../tests/data/test-cert.pem");
const TEST_CHAIN_DER: &'static [u8] = include_bytes!("../../../tests/data/test-chain.der");
const TEST_CHAIN_PEM: &'static [u8] = include_bytes!("../../../tests/data/test-chain.pem");
#[test]
fn test_from_certs() {
let certs = Certificate::load_pem_chain(TEST_CHAIN_PEM).unwrap();
let chain = CertChain::from_certs(certs).unwrap();
assert_eq!(&chain.inner, TEST_CHAIN_DER);
assert_eq!(
chain.inner.len(),
TryInto::<usize>::try_into(chain.length).unwrap()
);
}
#[test]
fn test_append_cert() {
let cert = Certificate::from_pem(TEST_CERT_PEM).unwrap();
let mut chain = CertChain::from_certs(&[cert.clone()]).unwrap();
chain.append_cert(&cert).unwrap();
assert_eq!(&chain.inner, TEST_CHAIN_DER);
}
}