diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
commit | 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch) | |
tree | 033cc839730fda84ff08db877037977be94e5e3a /vendor/der/src/writer | |
parent | Initial commit. (diff) | |
download | cargo-upstream.tar.xz cargo-upstream.zip |
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/der/src/writer')
-rw-r--r-- | vendor/der/src/writer/pem.rs | 41 | ||||
-rw-r--r-- | vendor/der/src/writer/slice.rs | 149 |
2 files changed, 190 insertions, 0 deletions
diff --git a/vendor/der/src/writer/pem.rs b/vendor/der/src/writer/pem.rs new file mode 100644 index 0000000..87a6f8f --- /dev/null +++ b/vendor/der/src/writer/pem.rs @@ -0,0 +1,41 @@ +//! Streaming PEM writer. + +use super::Writer; +use crate::Result; +use pem_rfc7468::{Encoder, LineEnding}; + +/// `Writer` type which outputs PEM-encoded data. +pub struct PemWriter<'w>(Encoder<'static, 'w>); + +impl<'w> PemWriter<'w> { + /// Create a new PEM writer which outputs into the provided buffer. + /// + /// Uses the default 64-character line wrapping. + pub fn new( + type_label: &'static str, + line_ending: LineEnding, + out: &'w mut [u8], + ) -> Result<Self> { + Ok(Self(Encoder::new(type_label, line_ending, out)?)) + } + + /// Get the PEM label which will be used in the encapsulation boundaries + /// for this document. + pub fn type_label(&self) -> &'static str { + self.0.type_label() + } + + /// Finish encoding PEM, writing the post-encapsulation boundary. + /// + /// On success, returns the total number of bytes written to the output buffer. + pub fn finish(self) -> Result<usize> { + Ok(self.0.finish()?) + } +} + +impl Writer for PemWriter<'_> { + fn write(&mut self, slice: &[u8]) -> Result<()> { + self.0.encode(slice)?; + Ok(()) + } +} diff --git a/vendor/der/src/writer/slice.rs b/vendor/der/src/writer/slice.rs new file mode 100644 index 0000000..5dafe99 --- /dev/null +++ b/vendor/der/src/writer/slice.rs @@ -0,0 +1,149 @@ +//! 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)); + } +} |