Skip to content

Commit

Permalink
der: initial Writer trait (#605)
Browse files Browse the repository at this point in the history
Begins moving core encoding functionality into a `Writer` trait.

The longer-term goal is to make the `Encode` trait accept a generic type
as a backend, which can enable things like 1-pass PEM encoding or
computing key fingerprints by impl'ing `Writer` for a hash function.
  • Loading branch information
tarcieri authored Apr 23, 2022
1 parent 677bc84 commit ad4a4fc
Show file tree
Hide file tree
Showing 19 changed files with 73 additions and 67 deletions.
4 changes: 2 additions & 2 deletions der/src/asn1/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
asn1::*, ByteSlice, Choice, Decode, DecodeValue, Decoder, DerOrd, EncodeValue, Encoder, Error,
ErrorKind, FixedTag, Header, Length, Result, Tag, Tagged, ValueOrd,
ErrorKind, FixedTag, Header, Length, Result, Tag, Tagged, ValueOrd, Writer,
};
use core::cmp::Ordering;

Expand Down Expand Up @@ -168,7 +168,7 @@ impl EncodeValue for Any<'_> {
}

fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.bytes(self.value())
encoder.write(self.value())
}
}

Expand Down
6 changes: 3 additions & 3 deletions der/src/asn1/bit_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
asn1::Any, ByteSlice, DecodeValue, Decoder, DerOrd, EncodeValue, Encoder, Error, ErrorKind,
FixedTag, Header, Length, Result, Tag, ValueOrd,
FixedTag, Header, Length, Result, Tag, ValueOrd, Writer,
};
use core::{cmp::Ordering, iter::FusedIterator};

Expand Down Expand Up @@ -134,8 +134,8 @@ impl EncodeValue for BitString<'_> {
}

fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.byte(self.unused_bits)?;
encoder.bytes(self.raw_bytes())
encoder.write_byte(self.unused_bits)?;
encoder.write(self.raw_bytes())
}
}

Expand Down
4 changes: 2 additions & 2 deletions der/src/asn1/boolean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
asn1::Any, ord::OrdIsValueOrd, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error,
ErrorKind, FixedTag, Header, Length, Result, Tag,
ErrorKind, FixedTag, Header, Length, Result, Tag, Writer,
};

/// Byte used to encode `true` in ASN.1 DER. From X.690 Section 11.1:
Expand Down Expand Up @@ -34,7 +34,7 @@ impl EncodeValue for bool {
}

fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.byte(if *self { TRUE_OCTET } else { FALSE_OCTET })
encoder.write_byte(if *self { TRUE_OCTET } else { FALSE_OCTET })
}
}

Expand Down
4 changes: 2 additions & 2 deletions der/src/asn1/generalized_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
datetime::{self, DateTime},
ord::OrdIsValueOrd,
ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, ErrorKind, FixedTag, Header,
Length, Result, Tag,
Length, Result, Tag, Writer,
};
use core::time::Duration;

Expand Down Expand Up @@ -115,7 +115,7 @@ impl EncodeValue for GeneralizedTime {
datetime::encode_decimal(encoder, Self::TAG, self.0.hour())?;
datetime::encode_decimal(encoder, Self::TAG, self.0.minutes())?;
datetime::encode_decimal(encoder, Self::TAG, self.0.seconds())?;
encoder.byte(b'Z')
encoder.write_byte(b'Z')
}
}

Expand Down
6 changes: 3 additions & 3 deletions der/src/asn1/integer/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use super::uint;
use crate::{
asn1::Any, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, ErrorKind, FixedTag,
Header, Length, Result, Tag,
Header, Length, Result, Tag, Writer,
};

/// "Big" unsigned ASN.1 `INTEGER` type.
Expand Down Expand Up @@ -67,10 +67,10 @@ impl<'a> EncodeValue for UIntBytes<'a> {
fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
// Add leading `0x00` byte if required
if self.value_len()? > self.len() {
encoder.byte(0)?;
encoder.write_byte(0)?;
}

encoder.bytes(self.as_bytes())
encoder.write(self.as_bytes())
}
}

Expand Down
4 changes: 2 additions & 2 deletions der/src/asn1/integer/int.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Support for encoding negative integers
use super::is_highest_bit_set;
use crate::{Encoder, ErrorKind, Length, Result};
use crate::{Encoder, ErrorKind, Length, Result, Writer};

/// Decode an unsigned integer of the specified size.
///
Expand All @@ -28,7 +28,7 @@ pub(super) fn decode_to_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> {

/// Encode the given big endian bytes representing an integer as ASN.1 DER.
pub(super) fn encode_bytes(encoder: &mut Encoder<'_>, bytes: &[u8]) -> Result<()> {
encoder.bytes(strip_leading_ones(bytes))
encoder.write(strip_leading_ones(bytes))
}

/// Get the encoded length for the given unsigned integer serialized as bytes.
Expand Down
6 changes: 3 additions & 3 deletions der/src/asn1/integer/uint.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Unsigned integer decoders/encoders.
use crate::{Encoder, Length, Result, Tag};
use crate::{Encoder, Length, Result, Tag, Writer};

/// Decode an unsigned integer into a big endian byte slice with all leading
/// zeroes removed.
Expand Down Expand Up @@ -44,10 +44,10 @@ pub(crate) fn encode_bytes(encoder: &mut Encoder<'_>, bytes: &[u8]) -> Result<()
let bytes = strip_leading_zeroes(bytes);

if needs_leading_zero(bytes) {
encoder.byte(0)?;
encoder.write_byte(0)?;
}

encoder.bytes(bytes)
encoder.write(bytes)
}

/// Get the encoded length for the given unsigned integer serialized as bytes.
Expand Down
4 changes: 2 additions & 2 deletions der/src/asn1/oid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
asn1::Any, ord::OrdIsValueOrd, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error,
FixedTag, Header, Length, Result, Tag, Tagged,
FixedTag, Header, Length, Result, Tag, Tagged, Writer,
};
use const_oid::ObjectIdentifier;

Expand All @@ -19,7 +19,7 @@ impl EncodeValue for ObjectIdentifier {
}

fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.bytes(self.as_bytes())
encoder.write(self.as_bytes())
}
}

Expand Down
16 changes: 8 additions & 8 deletions der/src/asn1/real.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

use crate::{
str_slice::StrSlice, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, FixedTag, Header,
Length, Result, Tag,
Length, Result, Tag, Writer,
};

use super::integer::uint::strip_leading_zeroes;
Expand Down Expand Up @@ -136,18 +136,18 @@ impl EncodeValue for f64 {
return Ok(());
} else if self.is_nan() {
// Not a number
encoder.bytes(&[0b0100_0010])?;
encoder.write_byte(0b0100_0010)?;
} else if self.is_infinite() {
if self.is_sign_negative() {
// Negative infinity
encoder.bytes(&[0b0100_0001])?;
encoder.write_byte(0b0100_0001)?;
} else {
// Plus infinity
encoder.bytes(&[0b0100_0000])?;
encoder.write_byte(0b0100_0000)?;
}
} else {
// Minus zero
encoder.bytes(&[0b0100_0011])?;
encoder.write_byte(0b0100_0011)?;
}
} else {
// Always use binary encoding, set bit 8 to 1
Expand Down Expand Up @@ -178,16 +178,16 @@ impl EncodeValue for f64 {
}
}

encoder.bytes(&[first_byte])?;
encoder.write_byte(first_byte)?;

// Encode both bytes or just the last one, handled by encode_bytes directly
// Rust already encodes the data as two's complement, so no further processing is needed
encoder.bytes(ebytes)?;
encoder.write(ebytes)?;

// Now, encode the mantissa as unsigned binary number
let mantissa_bytes = mantissa.to_be_bytes();
let mbytes = strip_leading_zeroes(&mantissa_bytes);
encoder.bytes(mbytes)?;
encoder.write(mbytes)?;
}
Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions der/src/asn1/utc_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
datetime::{self, DateTime},
ord::OrdIsValueOrd,
ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, ErrorKind, FixedTag, Header,
Length, Result, Tag,
Length, Result, Tag, Writer,
};
use core::time::Duration;

Expand Down Expand Up @@ -128,7 +128,7 @@ impl EncodeValue for UtcTime {
datetime::encode_decimal(encoder, Self::TAG, self.0.hour())?;
datetime::encode_decimal(encoder, Self::TAG, self.0.minutes())?;
datetime::encode_decimal(encoder, Self::TAG, self.0.seconds())?;
encoder.byte(b'Z')
encoder.write_byte(b'Z')
}
}

Expand Down
4 changes: 2 additions & 2 deletions der/src/byte_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{
str_slice::StrSlice, DecodeValue, Decoder, DerOrd, EncodeValue, Encoder, Error, Header, Length,
Result,
Result, Writer,
};
use core::cmp::Ordering;

Expand Down Expand Up @@ -67,7 +67,7 @@ impl EncodeValue for ByteSlice<'_> {
}

fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.bytes(self.as_ref())
encoder.write(self.as_ref())
}
}

Expand Down
8 changes: 3 additions & 5 deletions der/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Copyright (c) 2016 The humantime Developers
// Released under the MIT OR Apache 2.0 licenses

use crate::{Encoder, Error, ErrorKind, Result, Tag};
use crate::{Encoder, Error, ErrorKind, Result, Tag, Writer};
use core::{fmt, str::FromStr, time::Duration};

#[cfg(feature = "std")]
Expand Down Expand Up @@ -372,17 +372,15 @@ pub(crate) fn decode_decimal(tag: Tag, hi: u8, lo: u8) -> Result<u8> {
}

/// Encode 2-digit decimal value
// TODO(tarcieri): checked arithmetic
#[allow(clippy::integer_arithmetic)]
pub(crate) fn encode_decimal(encoder: &mut Encoder<'_>, tag: Tag, value: u8) -> Result<()> {
let hi_val = value / 10;

if hi_val >= 10 {
return Err(tag.value_error());
}

encoder.byte(hi_val + b'0')?;
encoder.byte((value % 10) + b'0')
encoder.write_byte(b'0'.checked_add(hi_val).ok_or(ErrorKind::Overflow)?)?;
encoder.write_byte(b'0'.checked_add(value % 10).ok_or(ErrorKind::Overflow)?)
}

#[cfg(test)]
Expand Down
4 changes: 2 additions & 2 deletions der/src/document.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! ASN.1 DER-encoded documents stored on the heap.
use crate::{Decode, Decoder, Encode, Encoder, Error, FixedTag, Length, Result, Tag};
use crate::{Decode, Decoder, Encode, Encoder, Error, FixedTag, Length, Result, Tag, Writer};
use alloc::vec::Vec;
use core::fmt::{self, Debug};

Expand Down Expand Up @@ -166,7 +166,7 @@ impl Encode for Document {
}

fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.bytes(self.as_bytes())
encoder.write(self.as_bytes())
}
}

Expand Down
26 changes: 8 additions & 18 deletions der/src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use crate::{
asn1::*, Encode, EncodeRef, EncodeValue, Error, ErrorKind, Header, Length, Result, Tag,
TagMode, TagNumber, Tagged,
TagMode, TagNumber, Tagged, Writer,
};

/// DER encoder.
Expand Down Expand Up @@ -213,23 +213,6 @@ impl<'a> Encoder<'a> {
Ok(slice)
}

/// Encode a single byte into the backing buffer.
pub(crate) fn byte(&mut self, byte: u8) -> Result<()> {
match self.reserve(1u8)?.first_mut() {
Some(b) => {
*b = byte;
Ok(())
}
None => self.error(ErrorKind::Overlength),
}
}

/// Encode the provided byte slice into the backing buffer.
pub(crate) fn bytes(&mut self, slice: &[u8]) -> Result<()> {
self.reserve(slice.len())?.copy_from_slice(slice);
Ok(())
}

/// Get the size of the buffer in bytes.
fn buffer_len(&self) -> Result<Length> {
self.bytes
Expand All @@ -250,6 +233,13 @@ impl<'a> Encoder<'a> {
}
}

impl<'a> Writer for Encoder<'a> {
fn write(&mut self, slice: &[u8]) -> Result<()> {
self.reserve(slice.len())?.copy_from_slice(slice);
Ok(())
}
}

#[cfg(test)]
mod tests {
use hex_literal::hex;
Expand Down
14 changes: 7 additions & 7 deletions der/src/length.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Length calculations for encoded ASN.1 DER values
use crate::{Decode, Decoder, DerOrd, Encode, Encoder, Error, ErrorKind, Result};
use crate::{Decode, Decoder, DerOrd, Encode, Encoder, Error, ErrorKind, Result, Writer};
use core::{
cmp::Ordering,
fmt,
Expand Down Expand Up @@ -241,17 +241,17 @@ impl Encode for Length {
#[allow(clippy::cast_possible_truncation)]
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
if let Some(tag_byte) = self.initial_octet() {
encoder.byte(tag_byte)?;
encoder.write_byte(tag_byte)?;

// Strip leading zeroes
match self.0.to_be_bytes() {
[0, 0, 0, byte] => encoder.byte(byte),
[0, 0, bytes @ ..] => encoder.bytes(&bytes),
[0, bytes @ ..] => encoder.bytes(&bytes),
bytes => encoder.bytes(&bytes),
[0, 0, 0, byte] => encoder.write_byte(byte),
[0, 0, bytes @ ..] => encoder.write(&bytes),
[0, bytes @ ..] => encoder.write(&bytes),
bytes => encoder.write(&bytes),
}
} else {
encoder.byte(self.0 as u8)
encoder.write_byte(self.0 as u8)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions der/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ mod length;
mod ord;
mod str_slice;
mod tag;
mod writer;

#[cfg(feature = "alloc")]
mod document;
Expand All @@ -376,6 +377,7 @@ pub use crate::{
length::Length,
ord::{DerOrd, ValueOrd},
tag::{Class, FixedTag, Tag, TagMode, TagNumber, Tagged},
writer::Writer,
};

#[cfg(feature = "alloc")]
Expand Down
Loading

0 comments on commit ad4a4fc

Please sign in to comment.