| //! Slice writer. | |
| use crate::{ | |
| asn1::*, Encode, EncodeValue, ErrorKind, Header, Length, Result, Tag, TagMode, TagNumber, | |
| Tagged, Writer, | |
| }; | |
| /// [`Writer`] which encodes DER into a mutable output byte slice. | |
| #[derive(Debug)] | |
| pub struct SliceWriter<'a> { | |
| /// Buffer into which DER-encoded message is written | |
| bytes: &'a mut [u8], | |
| /// Has the encoding operation failed? | |
| failed: bool, | |
| /// Total number of bytes written to buffer so far | |
| position: Length, | |
| } | |
| impl<'a> SliceWriter<'a> { | |
| /// Create a new encoder with the given byte slice as a backing buffer. | |
| pub fn new(bytes: &'a mut [u8]) -> Self { | |
| Self { | |
| bytes, | |
| failed: false, | |
| position: Length::ZERO, | |
| } | |
| } | |
| /// Encode a value which impls the [`Encode`] trait. | |
| pub fn encode<T: Encode>(&mut self, encodable: &T) -> Result<()> { | |
| if self.is_failed() { | |
| self.error(ErrorKind::Failed)?; | |
| } | |
| encodable.encode(self).map_err(|e| { | |
| self.failed = true; | |
| e.nested(self.position) | |
| }) | |
| } | |
| /// Return an error with the given [`ErrorKind`], annotating it with | |
| /// context about where the error occurred. | |
| pub fn error<T>(&mut self, kind: ErrorKind) -> Result<T> { | |
| self.failed = true; | |
| Err(kind.at(self.position)) | |
| } | |
| /// Did the decoding operation fail due to an error? | |
| pub fn is_failed(&self) -> bool { | |
| self.failed | |
| } | |
| /// Finish encoding to the buffer, returning a slice containing the data | |
| /// written to the buffer. | |
| pub fn finish(self) -> Result<&'a [u8]> { | |
| let position = self.position; | |
| if self.is_failed() { | |
| return Err(ErrorKind::Failed.at(position)); | |
| } | |
| self.bytes | |
| .get(..usize::try_from(position)?) | |
| .ok_or_else(|| ErrorKind::Overlength.at(position)) | |
| } | |
| /// Encode a `CONTEXT-SPECIFIC` field with the provided tag number and mode. | |
| pub fn context_specific<T>( | |
| &mut self, | |
| tag_number: TagNumber, | |
| tag_mode: TagMode, | |
| value: &T, | |
| ) -> Result<()> | |
| where | |
| T: EncodeValue + Tagged, | |
| { | |
| ContextSpecificRef { | |
| tag_number, | |
| tag_mode, | |
| value, | |
| } | |
| .encode(self) | |
| } | |
| /// Encode an ASN.1 `SEQUENCE` of the given length. | |
| /// | |
| /// Spawns a nested slice writer which is expected to be exactly the | |
| /// specified length upon completion. | |
| pub fn sequence<F>(&mut self, length: Length, f: F) -> Result<()> | |
| where | |
| F: FnOnce(&mut SliceWriter<'_>) -> Result<()>, | |
| { | |
| Header::new(Tag::Sequence, length).and_then(|header| header.encode(self))?; | |
| let mut nested_encoder = SliceWriter::new(self.reserve(length)?); | |
| f(&mut nested_encoder)?; | |
| if nested_encoder.finish()?.len() == usize::try_from(length)? { | |
| Ok(()) | |
| } else { | |
| self.error(ErrorKind::Length { tag: Tag::Sequence }) | |
| } | |
| } | |
| /// Reserve a portion of the internal buffer, updating the internal cursor | |
| /// position and returning a mutable slice. | |
| fn reserve(&mut self, len: impl TryInto<Length>) -> Result<&mut [u8]> { | |
| if self.is_failed() { | |
| return Err(ErrorKind::Failed.at(self.position)); | |
| } | |
| let len = len | |
| .try_into() | |
| .or_else(|_| self.error(ErrorKind::Overflow))?; | |
| let end = (self.position + len).or_else(|e| self.error(e.kind()))?; | |
| let slice = self | |
| .bytes | |
| .get_mut(self.position.try_into()?..end.try_into()?) | |
| .ok_or_else(|| ErrorKind::Overlength.at(end))?; | |
| self.position = end; | |
| Ok(slice) | |
| } | |
| } | |
| impl<'a> Writer for SliceWriter<'a> { | |
| fn write(&mut self, slice: &[u8]) -> Result<()> { | |
| self.reserve(slice.len())?.copy_from_slice(slice); | |
| Ok(()) | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::SliceWriter; | |
| use crate::{Encode, ErrorKind, Length}; | |
| #[test] | |
| fn overlength_message() { | |
| let mut buffer = []; | |
| let mut writer = SliceWriter::new(&mut buffer); | |
| let err = false.encode(&mut writer).err().unwrap(); | |
| assert_eq!(err.kind(), ErrorKind::Overlength); | |
| assert_eq!(err.position(), Some(Length::ONE)); | |
| } | |
| } |