blob: 28d7cba77e06cfb639ffea759935764eff6deeab [file] [log] [blame]
//! Trait definition for [`Encode`].
use crate::{Header, Length, Result, SliceWriter, Tagged, Writer};
use core::marker::PhantomData;
#[cfg(feature = "alloc")]
use {alloc::boxed::Box, alloc::vec::Vec, core::iter};
#[cfg(feature = "pem")]
use {
crate::PemWriter,
alloc::string::String,
pem_rfc7468::{self as pem, LineEnding, PemLabel},
};
#[cfg(any(feature = "alloc", feature = "pem"))]
use crate::ErrorKind;
#[cfg(doc)]
use crate::Tag;
/// Encoding trait.
pub trait Encode {
/// Compute the length of this value in bytes when encoded as ASN.1 DER.
fn encoded_len(&self) -> Result<Length>;
/// Encode this value as ASN.1 DER using the provided [`Writer`].
fn encode(&self, encoder: &mut impl Writer) -> Result<()>;
/// Encode this value to the provided byte slice, returning a sub-slice
/// containing the encoded message.
fn encode_to_slice<'a>(&self, buf: &'a mut [u8]) -> Result<&'a [u8]> {
let mut writer = SliceWriter::new(buf);
self.encode(&mut writer)?;
writer.finish()
}
/// Encode this message as ASN.1 DER, appending it to the provided
/// byte vector.
#[cfg(feature = "alloc")]
fn encode_to_vec(&self, buf: &mut Vec<u8>) -> Result<Length> {
let expected_len = usize::try_from(self.encoded_len()?)?;
buf.reserve(expected_len);
buf.extend(iter::repeat(0).take(expected_len));
let mut writer = SliceWriter::new(buf);
self.encode(&mut writer)?;
let actual_len = writer.finish()?.len();
if expected_len != actual_len {
return Err(ErrorKind::Incomplete {
expected_len: expected_len.try_into()?,
actual_len: actual_len.try_into()?,
}
.into());
}
actual_len.try_into()
}
/// Encode this type as DER, returning a byte vector.
#[cfg(feature = "alloc")]
fn to_der(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
self.encode_to_vec(&mut buf)?;
Ok(buf)
}
}
impl<T> Encode for T
where
T: EncodeValue + Tagged,
{
/// Compute the length of this value in bytes when encoded as ASN.1 DER.
fn encoded_len(&self) -> Result<Length> {
self.value_len().and_then(|len| len.for_tlv())
}
/// Encode this value as ASN.1 DER using the provided [`Writer`].
fn encode(&self, writer: &mut impl Writer) -> Result<()> {
self.header()?.encode(writer)?;
self.encode_value(writer)
}
}
/// Dummy implementation for [`PhantomData`] which allows deriving
/// implementations on structs with phantom fields.
impl<T> Encode for PhantomData<T>
where
T: ?Sized,
{
fn encoded_len(&self) -> Result<Length> {
Ok(Length::ZERO)
}
fn encode(&self, _writer: &mut impl Writer) -> Result<()> {
Ok(())
}
}
/// PEM encoding trait.
///
/// This trait is automatically impl'd for any type which impls both
/// [`Encode`] and [`PemLabel`].
#[cfg(feature = "pem")]
pub trait EncodePem: Encode + PemLabel {
/// Try to encode this type as PEM.
fn to_pem(&self, line_ending: LineEnding) -> Result<String>;
}
#[cfg(feature = "pem")]
impl<T: Encode + PemLabel> EncodePem for T {
fn to_pem(&self, line_ending: LineEnding) -> Result<String> {
let der_len = usize::try_from(self.encoded_len()?)?;
let pem_len = pem::encapsulated_len(Self::PEM_LABEL, line_ending, der_len)?;
let mut buf = vec![0u8; pem_len];
let mut writer = PemWriter::new(Self::PEM_LABEL, line_ending, &mut buf)?;
self.encode(&mut writer)?;
let actual_len = writer.finish()?;
buf.truncate(actual_len);
Ok(String::from_utf8(buf)?)
}
}
/// Encode the value part of a Tag-Length-Value encoded field, sans the [`Tag`]
/// and [`Length`].
pub trait EncodeValue {
/// Get the [`Header`] used to encode this value.
fn header(&self) -> Result<Header>
where
Self: Tagged,
{
Header::new(self.tag(), self.value_len()?)
}
/// Compute the length of this value (sans [`Tag`]+[`Length`] header) when
/// encoded as ASN.1 DER.
fn value_len(&self) -> Result<Length>;
/// Encode value (sans [`Tag`]+[`Length`] header) as ASN.1 DER using the
/// provided [`Writer`].
fn encode_value(&self, encoder: &mut impl Writer) -> Result<()>;
}
#[cfg(feature = "alloc")]
impl<T> EncodeValue for Box<T>
where
T: EncodeValue,
{
fn value_len(&self) -> Result<Length> {
T::value_len(self)
}
fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
T::encode_value(self, writer)
}
}