Skip to content

Commit

Permalink
Refactor ToDer, and update serialization globally
Browse files Browse the repository at this point in the history
  • Loading branch information
chifflier committed Mar 7, 2025
1 parent e9dcda3 commit 945cfa3
Show file tree
Hide file tree
Showing 44 changed files with 1,013 additions and 1,436 deletions.
32 changes: 16 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 13 additions & 30 deletions derive/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,54 +498,37 @@ impl Container {
}
}

pub fn gen_to_der_len(&self) -> TokenStream {
pub fn gen_der_content_len(&self) -> TokenStream {
let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>();
let add_len_instructions = field_names.iter().fold(Vec::new(), |mut instrs, field| {
instrs.push(quote! {total_len += self.#field.to_der_len()?;});
instrs.push(quote! {total_len += self.#field.der_total_len();});
instrs
});
quote! {
fn to_der_len(&self) -> asn1_rs::Result<usize> {
let mut total_len = 0;
fn der_content_len(&self) -> asn1_rs::Length {
let mut total_len = asn1_rs::Length::Definite(0);
#(#add_len_instructions)*
// now add header length computation
if total_len < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + total_len)
} else {
// 1 (class+tag) + n (length) + len
let n = asn1_rs::Length::Definite(total_len).to_der_len()?;
Ok(1 + n + total_len)
}
total_len
}
}
}

pub fn gen_write_der_header(&self) -> TokenStream {
quote! {
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> asn1_rs::SerializeResult<usize> {
let mut empty = std::io::empty();
let num_bytes = self.write_der_content(&mut empty)?;
let header = asn1_rs::Header::new(
asn1_rs::Class::Universal,
true,
asn1_rs::Sequence::TAG,
asn1_rs::Length::Definite(num_bytes),
);
header.write_der_header(writer).map_err(Into::into)
pub fn gen_der_tag_info(&self) -> TokenStream {
quote!(
fn der_tag_info(&self) -> (asn1_rs::Class, bool, asn1_rs::Tag) {
(asn1_rs::Class::Universal, true, asn1_rs::Sequence::TAG)
}
}
)
}

pub fn gen_write_der_content(&self) -> TokenStream {
pub fn gen_der_write_content(&self) -> TokenStream {
let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>();
let write_instructions = field_names.iter().fold(Vec::new(), |mut instrs, field| {
instrs.push(quote! {num_bytes += self.#field.write_der_header(writer)?;});
instrs.push(quote! {num_bytes += self.#field.write_der_content(writer)?;});
instrs.push(quote! {num_bytes += self.#field.der_encode(writer)?;});
instrs
});
quote! {
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> asn1_rs::SerializeResult<usize> {
fn der_write_content<W: std::io::Write>(&self, writer: &mut W) -> asn1_rs::SerializeResult<usize> {
let mut num_bytes = 0;
#(#write_instructions)*
Ok(num_bytes)
Expand Down
14 changes: 8 additions & 6 deletions derive/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ pub fn derive_toder_sequence(s: synstructure::Structure) -> proc_macro2::TokenSt
_ => true,
});

let impl_to_der_len = container.gen_to_der_len();
let impl_write_der_header = container.gen_write_der_header();
let impl_write_der_content = container.gen_write_der_content();
let impl_der_content_len = container.gen_der_content_len();
let impl_der_tag_info = container.gen_der_tag_info();
let impl_der_write_content = container.gen_der_write_content();

// note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing
// the `where` statement if there are none.
Expand All @@ -204,9 +204,11 @@ pub fn derive_toder_sequence(s: synstructure::Structure) -> proc_macro2::TokenSt

#[cfg(feature = "std")]
gen impl asn1_rs::ToDer for @Self where #(#wh)+* {
#impl_to_der_len
#impl_write_der_header
#impl_write_der_content
type Encoder = asn1_rs::BerGenericEncoder;

#impl_der_content_len
#impl_der_tag_info
#impl_der_write_content
}
});
if debug_derive {
Expand Down
33 changes: 2 additions & 31 deletions src/asn1_types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,37 +575,6 @@ impl DynTagged for Any<'_> {
// }
// }

#[cfg(feature = "std")]
impl ToDer for Any<'_> {
fn to_der_len(&self) -> Result<usize> {
let hdr_len = self.header.to_der_len()?;
Ok(hdr_len + self.data.len())
}

fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// create fake header to have correct length
let header = Header::new(
self.header.class,
self.header.constructed,
self.header.tag,
Length::Definite(self.data.len()),
);
let sz = header.write_der_header(writer)?;
Ok(sz)
}

fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(self.data.as_bytes2()).map_err(Into::into)
}

/// Similar to using `to_der`, but uses header without computing length value
fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let sz = self.header.write_der_header(writer)?;
let sz = sz + writer.write(self.data.as_bytes2())?;
Ok(sz)
}
}

#[cfg(feature = "std")]
const _: () = {
use std::io::Write;
Expand All @@ -625,6 +594,8 @@ const _: () = {
(self.class(), self.constructed(), self.tag())
}
}

impl_toder_from_tober!(LFT 'a, Any<'a>);
};

#[cfg(test)]
Expand Down
38 changes: 3 additions & 35 deletions src/asn1_types/bitstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,40 +181,6 @@ impl Appendable for BitString {
}
}

#[cfg(feature = "std")]
impl ToDer for BitString {
fn to_der_len(&self) -> Result<usize> {
let sz = self.as_raw_slice().len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + 1 (unused bits) + len
Ok(3 + sz)
} else {
// 1 (class+tag) + n (length) + 1 (unused bits) + len
let n = Length::Definite(sz + 1).to_der_len()?;
Ok(2 + n + sz)
}
}

fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(1 + self.as_raw_slice().len()),
);
header.write_der_header(writer)
}

fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let bit_length = self.bitvec.len();
let data = self.as_raw_slice();
let unused_bits = data.len() * 8 - bit_length;
let sz = writer.write(&[unused_bits as u8])?;
let sz = sz + writer.write(data)?;
Ok(sz)
}
}

#[cfg(feature = "std")]
const _: () = {
use std::io::Write;
Expand All @@ -223,7 +189,7 @@ const _: () = {
type Encoder = Primitive<{ Tag::BitString.0 }>;

fn ber_content_len(&self) -> Length {
let len = 1 + (self.len() / 8);
let len = 1 + ((self.len() + 7) / 8);
Length::Definite(len)
}

Expand All @@ -242,6 +208,8 @@ const _: () = {
(Self::CLASS, false, Self::TAG)
}
}

impl_toder_from_tober!(TY BitString);
};

#[cfg(test)]
Expand Down
74 changes: 34 additions & 40 deletions src/asn1_types/boolean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,29 +89,6 @@ impl Tagged for Boolean {
const TAG: Tag = Tag::Boolean;
}

#[cfg(feature = "std")]
impl ToDer for Boolean {
fn to_der_len(&self) -> Result<usize> {
// 3 = 1 (tag) + 1 (length) + 1 (value)
Ok(3)
}

fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
}

fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let b = if self.value != 0 { 0xff } else { 0x00 };
writer.write(&[b]).map_err(Into::into)
}

/// Similar to using `to_der`, but uses header without computing length value
fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let sz = writer.write(&[Self::TAG.0 as u8, 0x01, self.value])?;
Ok(sz)
}
}

#[cfg(feature = "std")]
const _: () = {
use std::io::Write;
Expand All @@ -131,6 +108,23 @@ const _: () = {
(self.class(), self.constructed(), self.tag())
}
}

impl ToDer for Boolean {
type Encoder = Primitive<{ Tag::Boolean.0 }>;

fn der_content_len(&self) -> Length {
Length::Definite(1)
}

fn der_write_content<W: Write>(&self, target: &mut W) -> SerializeResult<usize> {
let value = if self.value != 0 { 0xff } else { 0x00 };
target.write(&[value]).map_err(Into::into)
}

fn der_tag_info(&self) -> (Class, bool, Tag) {
(self.class(), self.constructed(), self.tag())
}
}
};

//---- bool
Expand Down Expand Up @@ -176,23 +170,6 @@ impl Tagged for bool {
const TAG: Tag = Tag::Boolean;
}

#[cfg(feature = "std")]
impl ToDer for bool {
fn to_der_len(&self) -> Result<usize> {
// 3 = 1 (tag) + 1 (length) + 1 (value)
Ok(3)
}

fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
}

fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let b = if *self { 0xff } else { 0x00 };
writer.write(&[b]).map_err(Into::into)
}
}

#[cfg(feature = "std")]
const _: () = {
use std::io::Write;
Expand All @@ -213,6 +190,23 @@ const _: () = {
(Self::CLASS, false, Self::TAG)
}
}

impl ToDer for bool {
type Encoder = Primitive<{ Tag::Boolean.0 }>;

fn der_content_len(&self) -> Length {
Length::Definite(1)
}

fn der_write_content<W: Write>(&self, target: &mut W) -> SerializeResult<usize> {
let value = if *self { 0xff } else { 0x00 };
target.write(&[value]).map_err(Into::into)
}

fn der_tag_info(&self) -> (Class, bool, Tag) {
(self.class(), self.constructed(), self.tag())
}
}
};

#[cfg(test)]
Expand Down
Loading

0 comments on commit 945cfa3

Please sign in to comment.