Skip to content

x509-cert: wip: owned api #776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 38 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a5722bb
[WIP] spki: make `AlgorithmIdentifier` generic around `Params`
tarcieri Nov 25, 2022
2ce63e5
Make `SubjectPublicKeyInfo` generic around `Params`
tarcieri Nov 26, 2022
04e6e28
spki: make `SubjectPublicKeyInfo` own the public key
baloo Nov 29, 2022
a2332af
spki: make key param an attribute of spki
baloo Nov 30, 2022
10197af
der: adds a ByteVec, owned alternative to ByteSlice
baloo Nov 27, 2022
3d9f1f0
der: Any now uses the new ByteVec
baloo Nov 27, 2022
61dc5d6
der: ensure Any is exposed
baloo Nov 27, 2022
d214695
der: ensure ValueOrd is implemented for Any
baloo Nov 27, 2022
2f8c282
der: drop the _string helpers from `AnyRef`
baloo Nov 27, 2022
76667ba
x509-cert: make name an owned type
baloo Nov 27, 2022
f4683b5
der: bytes are immutable Vec isn't needed
baloo Nov 27, 2022
e00e5d5
der: Introduce `UInt` as an owned version of `UIntRef`
baloo Nov 28, 2022
a00d80d
x509-cert: SerialNumber is now a specialized object
baloo Nov 28, 2022
0398d5a
x509-cert: Make extensions owned
baloo Nov 28, 2022
1b4b49f
x509-cert: make OtherName an owned type
baloo Nov 28, 2022
8899675
der: adds a String type owning the data
baloo Nov 28, 2022
0401a14
der: adds a `Ia5String` owned equivalent to `IaStringRef`
baloo Nov 28, 2022
4ea51cc
x509-cert: use Ia5String and use owned types
baloo Nov 28, 2022
a3d7e7a
x509-cert: Use owned String as backend for Utf8String
baloo Nov 28, 2022
ce1f5f9
der: provide `TeletexString` as owned alternative to `TeletexStringRef`
baloo Nov 28, 2022
660a4bf
der: provide `PrintableString` as owned alternative to `PrintableStri…
baloo Nov 28, 2022
155c46b
x509-cert: make `DirectoryString` and `EdiPartyName` owned types
baloo Nov 28, 2022
16229aa
x509-cert: make `SubjectKeyIdentifier` an owned type
baloo Nov 28, 2022
a156bf9
x509-cert: make `GeneralName` an owned type
baloo Nov 28, 2022
8967049
x509-cert: make `DisplayText` an owned type
baloo Nov 28, 2022
dfe32c6
x509-cert: make `PolicyQualifierInfo` an owned type
baloo Nov 28, 2022
c06c01f
x509-cert: use `BitString` for various fields
baloo Nov 28, 2022
97051a5
x509-cert: Crl are now owned types
baloo Nov 28, 2022
19c2126
x509-cert: AKI is now an owned type
baloo Nov 28, 2022
4cdba9c
x509-cert: NoticeReference is now an owned type
baloo Nov 28, 2022
147ebac
x509-cert: CertReq is now using an owned signature
baloo Nov 28, 2022
d584ff0
x509-cert: TrustAnchorInfo is now an owned type
baloo Nov 28, 2022
0af0108
spki: Clean `BitStringLike` implementation
baloo Dec 2, 2022
05b6fe2
x509-cert: make key storage a parameter for `Certificate`
baloo Dec 2, 2022
2931d9b
x509-cert: fixup `Version::value_cmp` (clippy)
baloo Dec 2, 2022
a34730b
x509-cert: apply api change to tests
baloo Dec 2, 2022
ca3a003
der: impl From<ByteSlice> for Bytes
baloo Dec 13, 2022
0d71f2d
der: make Bytes accept a Box
baloo Dec 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions der/derive/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Attribute-related types used by the proc macro

use crate::{Asn1Type, Tag, TagMode, TagNumber};
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use proc_macro_error::{abort, abort_call_site};
use quote::quote;
use std::{fmt::Debug, str::FromStr};
use syn::{Attribute, Lit, LitStr, Meta, MetaList, MetaNameValue, NestedMeta, Path};
use syn::{Attribute, Ident, Lit, LitStr, Meta, MetaList, MetaNameValue, NestedMeta, Path};

/// Attribute name.
pub(crate) const ATTR_NAME: &str = "asn1";
Expand All @@ -21,12 +21,22 @@ pub(crate) struct TypeAttrs {
///
/// The default value is `EXPLICIT`.
pub tag_mode: TagMode,

/// The type parameters that needs to implement `der::Choice<'_>`
pub choice: Vec<Ident>,
/// The type parameters that needs to implement `der::Encode`
pub encode: Vec<Ident>,
/// The type parameters that needs to implement `der::asn1::BitStringLike<'_>`
pub bitstringlike: Vec<Ident>,
}

impl TypeAttrs {
/// Parse attributes from a struct field or enum variant.
pub fn parse(attrs: &[Attribute]) -> Self {
let mut tag_mode = None;
let mut choice = Vec::new();
let mut encode = Vec::new();
let mut bitstringlike = Vec::new();

let mut parsed_attrs = Vec::new();
AttrNameValue::from_attributes(attrs, &mut parsed_attrs);
Expand All @@ -39,6 +49,12 @@ impl TypeAttrs {
}

tag_mode = Some(mode);
} else if let Some(ident) = attr.parse_value::<String>("choice") {
choice.push(Ident::new(&ident, Span::call_site()));
} else if let Some(ident) = attr.parse_value::<String>("encode") {
encode.push(Ident::new(&ident, Span::call_site()));
} else if let Some(ident) = attr.parse_value::<String>("bitstringlike") {
bitstringlike.push(Ident::new(&ident, Span::call_site()));
} else {
abort!(
attr.name,
Expand All @@ -49,6 +65,9 @@ impl TypeAttrs {

Self {
tag_mode: tag_mode.unwrap_or_default(),
bitstringlike,
encode,
choice,
}
}
}
Expand Down
90 changes: 88 additions & 2 deletions der/derive/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use field::SequenceField;
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use quote::quote;
use std::cmp::Ordering;
use syn::{DeriveInput, Ident, Lifetime};

/// Derive the `Sequence` trait for a struct
Expand All @@ -20,6 +21,12 @@ pub(crate) struct DeriveSequence {

/// Fields of the struct.
fields: Vec<SequenceField>,

/// Type parameters of the struct.
type_parameters: Vec<Ident>,

/// Type attributes of the struct.
type_attrs: TypeAttrs,
}

impl DeriveSequence {
Expand All @@ -33,6 +40,12 @@ impl DeriveSequence {
),
};

let type_parameters = input
.generics
.type_params()
.map(|g| g.ident.clone())
.collect::<Vec<_>>();

// TODO(tarcieri): properly handle multiple lifetimes
let lifetime = input
.generics
Expand All @@ -52,6 +65,8 @@ impl DeriveSequence {
ident: input.ident,
lifetime,
fields,
type_parameters,
type_attrs,
}
}

Expand Down Expand Up @@ -82,8 +97,27 @@ impl DeriveSequence {
encode_body.push(field.to_encode_tokens());
}

let mut type_parameters = Vec::new();
let mut type_parameters_bounds = Vec::new();
for param in &self.type_parameters {
type_parameters.push(param.clone());

if let Some(bound) = param.to_bind_tokens(&lifetime, &self.type_attrs) {
type_parameters_bounds.push(bound);
}
}

let maybe_where_token = if !self.type_parameters.is_empty() {
quote! {where}
} else {
quote! {}
};

quote! {
impl<#lifetime> ::der::DecodeValue<#lifetime> for #ident<#lt_params> {
impl<#lifetime, #(#type_parameters),*> ::der::DecodeValue<#lifetime> for #ident<#lt_params #(#type_parameters),*>
#maybe_where_token
#(#type_parameters_bounds),*
{
fn decode_value<R: ::der::Reader<#lifetime>>(
reader: &mut R,
header: ::der::Header,
Expand All @@ -100,7 +134,10 @@ impl DeriveSequence {
}
}

impl<#lifetime> ::der::Sequence<#lifetime> for #ident<#lt_params> {
impl<#lifetime, #(#type_parameters),*> ::der::Sequence<#lifetime> for #ident<#lt_params #(#type_parameters),*>
#maybe_where_token
#(#type_parameters_bounds),*
{
fn fields<F, T>(&self, f: F) -> ::der::Result<T>
where
F: FnOnce(&[&dyn der::Encode]) -> ::der::Result<T>,
Expand All @@ -114,6 +151,55 @@ impl DeriveSequence {
}
}

/// Trait for extending `syn::Ident` with a helper to generate the TokenStream for binding the type
trait BindToken {
fn to_bind_tokens(&self, lifetime: &TokenStream, attrs: &TypeAttrs) -> Option<TokenStream>;
}

impl BindToken for Ident {
fn to_bind_tokens(&self, lifetime: &TokenStream, attrs: &TypeAttrs) -> Option<TokenStream> {
let ident = self;

let mut bounds = Vec::new();

if attrs.choice.iter().any(|i| ident.ident_eq(i)) {
bounds.push(quote! {
::der::Choice<#lifetime>
});
}
if attrs.encode.iter().any(|i| ident.ident_eq(i)) {
bounds.push(quote! {
::der::Encode
});
}
if attrs.bitstringlike.iter().any(|i| ident.ident_eq(i)) {
bounds.push(quote! {
::der::asn1::BitStringLike<#lifetime>
});
}

if !bounds.is_empty() {
Some(quote! {
#ident: #(#bounds)+*
})
} else {
None
}
}
}

/// A small helper for `Ident` to do comparison of `Ident` ignoring its callsite
trait IdentEq {
fn ident_eq(&self, ident: &Ident) -> bool;
}

impl IdentEq for Ident {
fn ident_eq(&self, ident: &Ident) -> bool {
// Implementation detail, ordering comparison only compares the name
self.cmp(ident) == Ordering::Equal
}
}

#[cfg(test)]
mod tests {
use super::DeriveSequence;
Expand Down
8 changes: 6 additions & 2 deletions der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ mod videotex_string;

pub use self::{
any::AnyRef,
bit_string::{BitStringIter, BitStringRef},
bit_string::{BitStringIter, BitStringLike, BitStringRef},
choice::Choice,
context_specific::{ContextSpecific, ContextSpecificRef},
generalized_time::GeneralizedTime,
Expand All @@ -47,7 +47,11 @@ pub use self::{

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub use self::{any::Any, bit_string::BitString, octet_string::OctetString, set_of::SetOfVec};
pub use self::{
any::Any, bit_string::BitString, ia5_string::Ia5String, integer::bigint::UInt,
octet_string::OctetString, printable_string::PrintableString, set_of::SetOfVec,
teletex_string::TeletexString,
};

#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
Expand Down
80 changes: 46 additions & 34 deletions der/src/asn1/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
use core::cmp::Ordering;

#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::Bytes;

#[cfg(feature = "oid")]
use crate::asn1::ObjectIdentifier;
Expand Down Expand Up @@ -94,11 +94,6 @@ impl<'a> AnyRef<'a> {
self.try_into()
}

/// Attempt to decode an ASN.1 `IA5String`.
pub fn ia5_string(self) -> Result<Ia5StringRef<'a>> {
self.try_into()
}

/// Attempt to decode an ASN.1 `OCTET STRING`.
pub fn octet_string(self) -> Result<OctetStringRef<'a>> {
self.try_into()
Expand All @@ -123,21 +118,6 @@ impl<'a> AnyRef<'a> {
}
}

/// Attempt to decode an ASN.1 `PrintableString`.
pub fn printable_string(self) -> Result<PrintableStringRef<'a>> {
self.try_into()
}

/// Attempt to decode an ASN.1 `TeletexString`.
pub fn teletex_string(self) -> Result<TeletexStringRef<'a>> {
self.try_into()
}

/// Attempt to decode an ASN.1 `VideotexString`.
pub fn videotex_string(self) -> Result<VideotexStringRef<'a>> {
self.try_into()
}

/// Attempt to decode this value an ASN.1 `SEQUENCE`, creating a new
/// nested reader and calling the provided argument with it.
pub fn sequence<F, T>(self, f: F) -> Result<T>
Expand All @@ -154,11 +134,6 @@ impl<'a> AnyRef<'a> {
pub fn utc_time(self) -> Result<UtcTime> {
self.try_into()
}

/// Attempt to decode an ASN.1 `UTF8String`.
pub fn utf8_string(self) -> Result<Utf8StringRef<'a>> {
self.try_into()
}
}

impl<'a> Choice<'a> for AnyRef<'a> {
Expand Down Expand Up @@ -194,6 +169,13 @@ impl Tagged for AnyRef<'_> {
}
}

#[cfg(feature = "alloc")]
impl ValueOrd for Any {
fn value_cmp(&self, other: &Self) -> Result<Ordering> {
self.value.der_cmp(&other.value)
}
}

impl ValueOrd for AnyRef<'_> {
fn value_cmp(&self, other: &Self) -> Result<Ordering> {
self.value.der_cmp(&other.value)
Expand Down Expand Up @@ -226,19 +208,35 @@ pub struct Any {
tag: Tag,

/// Inner value encoded as bytes.
value: Vec<u8>,
value: Bytes,
}

#[cfg(feature = "alloc")]
impl Any {
/// Create a new [`Any`] from the provided [`Tag`] and DER bytes.
pub fn new(tag: Tag, bytes: impl Into<Vec<u8>>) -> Result<Self> {
let value = bytes.into();
pub fn new(tag: Tag, bytes: &[u8]) -> Result<Self> {
let value = Bytes::new(bytes)?;

// Ensure the tag and value are a valid `AnyRef`.
AnyRef::new(tag, &value)?;
AnyRef::new(tag, value.as_slice())?;
Ok(Self { tag, value })
}

/// Attempt to decode this [`Any`] type into the inner value.
pub fn decode_into<'a, T>(&'a self) -> Result<T>
where
T: DecodeValue<'a> + FixedTag,
{
self.tag.assert_eq(T::TAG)?;
let header = Header {
tag: self.tag,
length: self.value.len(),
};

let mut decoder = SliceReader::new(self.value.as_slice())?;
let result = T::decode_value(&mut decoder, header)?;
decoder.finish(result)
}
}

#[cfg(feature = "alloc")]
Expand All @@ -253,26 +251,26 @@ impl<'a> Decode<'a> for Any {
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
let header = Header::decode(reader)?;
let value = reader.read_vec(header.length)?;
Self::new(header.tag, value)
Self::new(header.tag, &value)
}
}

#[cfg(feature = "alloc")]
impl EncodeValue for Any {
fn value_len(&self) -> Result<Length> {
self.value.len().try_into()
Ok(self.value.len())
}

fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
writer.write(&self.value)
writer.write(self.value.as_slice())
}
}

#[cfg(feature = "alloc")]
impl<'a> From<&'a Any> for AnyRef<'a> {
fn from(any: &'a Any) -> AnyRef<'a> {
// Ensured to parse successfully in constructor
AnyRef::new(any.tag, &any.value).expect("invalid ANY")
AnyRef::new(any.tag, any.value.as_slice()).expect("invalid ANY")
}
}

Expand All @@ -282,3 +280,17 @@ impl Tagged for Any {
self.tag
}
}

#[cfg(feature = "alloc")]
impl<'a, T> From<T> for Any
where
T: Into<AnyRef<'a>>,
{
fn from(input: T) -> Any {
let anyref: AnyRef<'a> = input.into();
Self {
tag: anyref.tag(),
value: Bytes::from(anyref.value),
}
}
}
Loading