image4 0.8.2

A no_std-friendly library for parsing and generation of Image4 images written in pure Rust.
Documentation
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,
                })
            }

            /// Returns the contents as a byte slice.
            pub fn as_bytes(&self) -> &[u8] {
                &self.inner
            }

            /// Returns the byte length of the contents.
            pub fn len(&self) -> Length {
                self.length
            }

            /// Returns `true` if the contents are empty.
            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!(
    /// A byte vector that is expected to be a valid DER-encoded X.509 certificate.
    Cert,
    CertRef,
    Certificate,
    "a [`Certificate`]"
);
def_owned_wrapper!(
    /// A byte vector that is expected to be a valid sequence of DER-encoded X.509 certificates.
    ///
    /// The actual data stored in the vector **MAY BE ARBITRARY**. It may not even be a valid DER
    /// encoding. Currently the only way to validate the contents is to decode the certificate chain
    /// using the [`CertChain::decode_body`] method.
    ///
    /// See [`CertChainRef`] for reasoning behind the existence of this type.
    CertChain,
    CertChainRef,
    Vec<Certificate>,
    "a vector of [`Certificate`]s"
);

impl CertChain {
    /// Creates a new [`CertChain`] by serializing [`Certificate`]s into a byte vector.
    #[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,
        })
    }

    /// Appends an encoded [`Certificate`] to the encoded certificate chain.
    #[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(())
    }

    /// Appends raw bytes to the end of the encoded certificate chain.
    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(())
    }

    /// Reserved the specified amount of bytes in the internal byte vector.
    pub fn reserve(&mut self, additional: Length) -> der::Result<()> {
        additional
            .try_into()
            .map(|additional| self.inner.reserve(additional))
    }

    /// Returns iterator over individual certificates in the chain.
    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);
    }
}