From f6ee1a4c0295573826d685896988c39b85b751ab Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 09:27:13 -0500 Subject: [PATCH 1/3] der: allow `#[asn1(type = "INTEGER")]` on derive(Enumerated) Signed-off-by: Nathaniel McCallum --- der/derive/src/enumerated.rs | 34 +++++++++++++++++++++++++++------- der/derive/src/lib.rs | 2 +- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/der/derive/src/enumerated.rs b/der/derive/src/enumerated.rs index 17d8278da..7067a1bc0 100644 --- a/der/derive/src/enumerated.rs +++ b/der/derive/src/enumerated.rs @@ -6,7 +6,7 @@ use crate::ATTR_NAME; use proc_macro2::TokenStream; use proc_macro_error::abort; use quote::quote; -use syn::{DeriveInput, Expr, ExprLit, Ident, Lit, LitInt, Variant}; +use syn::{DeriveInput, Expr, ExprLit, Ident, Lit, LitInt, Meta, MetaList, NestedMeta, Variant}; /// Valid options for the `#[repr]` attribute on `Enumerated` types. const REPR_TYPES: &[&str] = &["u8", "u16", "u32"]; @@ -19,6 +19,9 @@ pub(crate) struct DeriveEnumerated { /// Value of the `repr` attribute. repr: Ident, + /// Whether or not to tag the enum as an integer + integer: bool, + /// Variants of this enum. variants: Vec, } @@ -36,13 +39,25 @@ impl DeriveEnumerated { // Reject `asn1` attributes, parse the `repr` attribute let mut repr: Option = None; + let mut integer = false; for attr in &input.attrs { if attr.path.is_ident(ATTR_NAME) { - abort!( - attr.path, - "`asn1` attribute is not allowed on `Enumerated` types" - ); + if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() { + for meta in nested { + if let NestedMeta::Meta(Meta::NameValue(nv)) = meta { + if nv.path.is_ident("type") { + if let Lit::Str(lit) = nv.lit { + match lit.value().as_str() { + "ENUMERATED" => integer = false, + "INTEGER" => integer = true, + s => abort!(lit, "`type = \"{}\"` is unsupported", s), + } + } + } + } + } + } } else if attr.path.is_ident("repr") { if repr.is_some() { abort!( @@ -81,6 +96,7 @@ impl DeriveEnumerated { ) }), variants, + integer, } } @@ -88,6 +104,10 @@ impl DeriveEnumerated { pub fn to_tokens(&self) -> TokenStream { let ident = &self.ident; let repr = &self.repr; + let tag = match self.integer { + false => quote! { ::der::Tag::Enumerated }, + true => quote! { ::der::Tag::Integer }, + }; let mut try_from_body = Vec::new(); for variant in &self.variants { @@ -115,7 +135,7 @@ impl DeriveEnumerated { } impl ::der::FixedTag for #ident { - const TAG: ::der::Tag = ::der::Tag::Enumerated; + const TAG: ::der::Tag = #tag; } impl TryFrom<#repr> for #ident { @@ -124,7 +144,7 @@ impl DeriveEnumerated { fn try_from(n: #repr) -> ::der::Result { match n { #(#try_from_body)* - _ => Err(der::Tag::Enumerated.value_error()) + _ => Err(#tag.value_error()) } } } diff --git a/der/derive/src/lib.rs b/der/derive/src/lib.rs index 320123002..d91871039 100644 --- a/der/derive/src/lib.rs +++ b/der/derive/src/lib.rs @@ -210,7 +210,7 @@ pub fn derive_choice(input: TokenStream) -> TokenStream { /// /// Note that the derive macro will write a `TryFrom<...>` impl for the /// provided `#[repr]`, which is used by the decoder. -#[proc_macro_derive(Enumerated)] +#[proc_macro_derive(Enumerated, attributes(asn1))] #[proc_macro_error] pub fn derive_enumerated(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); From ea00dfc13a2ef538644359ed77bbde96d93ecd62 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 09:27:51 -0500 Subject: [PATCH 2/3] Derive Integer for pkcs10::Version Signed-off-by: Nathaniel McCallum --- pkcs10/src/version.rs | 43 ++++--------------------------------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/pkcs10/src/version.rs b/pkcs10/src/version.rs index 48bfad7c4..079b9feba 100644 --- a/pkcs10/src/version.rs +++ b/pkcs10/src/version.rs @@ -1,49 +1,14 @@ //! Certification request information version identifier. -use der::{Decodable, Decoder, Encodable, Encoder, FixedTag, Tag}; +use der::Enumerated; /// Version identifier for certification request information. /// /// (RFC 2986 designates `0` as the only valid version) -#[derive(Clone, Debug, Copy, PartialEq, Eq)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)] +#[asn1(type = "INTEGER")] +#[repr(u8)] pub enum Version { /// Denotes PKCS#8 v1 V1 = 0, } - -impl Decodable<'_> for Version { - fn decode(decoder: &mut Decoder<'_>) -> der::Result { - Version::try_from(u8::decode(decoder)?).map_err(|_| Self::TAG.value_error()) - } -} - -impl Encodable for Version { - fn encoded_len(&self) -> der::Result { - der::Length::from(1u8).for_tlv() - } - - fn encode(&self, encoder: &mut Encoder<'_>) -> der::Result<()> { - u8::from(*self).encode(encoder) - } -} - -impl From for u8 { - fn from(version: Version) -> Self { - version as u8 - } -} - -impl TryFrom for Version { - type Error = der::Error; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Version::V1), - _ => Err(Self::TAG.value_error()), - } - } -} - -impl FixedTag for Version { - const TAG: Tag = Tag::Integer; -} From fdc0dd56792329059e1f852330db777932741df4 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Feb 2022 09:34:20 -0500 Subject: [PATCH 3/3] Add a Version enum to x509 Signed-off-by: Nathaniel McCallum --- x509/src/certificate.rs | 31 +++++++++++++++++++++++++++---- x509/tests/certificate.rs | 2 +- x509/tests/pkix_extensions.rs | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 98b8c711d..9c18c610a 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -1,14 +1,37 @@ //! Certificate [`Certificate`] and TBSCertificate [`TBSCertificate`] as defined in RFC 5280 use der::asn1::{BitString, ContextSpecific, ObjectIdentifier, UIntBytes}; -use der::{Sequence, TagMode, TagNumber}; +use der::{Enumerated, Sequence, TagMode, TagNumber}; use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo}; use x501::name::Name; use x501::time::Validity; -/// only support v3 certificates +/// Certificate `Version` as defined in [RFC 5280 Section 4.1]. +/// +/// ```text /// Version ::= INTEGER { v1(0), v2(1), v3(2) } -pub const X509_CERT_VERSION: u8 = 2; +/// ``` +/// +/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 +#[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)] +#[asn1(type = "INTEGER")] +#[repr(u8)] +pub enum Version { + /// Version 1 (default) + V1 = 0, + + /// Version 2 + V2 = 1, + + /// Version 3 + V3 = 2, +} + +impl Default for Version { + fn default() -> Self { + Self::V1 + } +} /// X.509 `TBSCertificate` as defined in [RFC 5280 Section 4.1.2.5] /// @@ -39,7 +62,7 @@ pub const X509_CERT_VERSION: u8 = 2; pub struct TBSCertificate<'a> { /// version [0] Version DEFAULT v1, //#[asn1(context_specific = "0", default = "Default::default")] - pub version: u8, + pub version: Version, /// serialNumber CertificateSerialNumber, pub serial_number: UIntBytes<'a>, /// signature AlgorithmIdentifier{SIGNATURE-ALGORITHM, {SignatureAlgorithms}}, diff --git a/x509/tests/certificate.rs b/x509/tests/certificate.rs index f36b2d9e4..60f0ba101 100644 --- a/x509/tests/certificate.rs +++ b/x509/tests/certificate.rs @@ -194,7 +194,7 @@ fn decode_cert() { let result = Certificate::from_der(der_encoded_cert); let cert: Certificate = result.unwrap(); - assert_eq!(cert.tbs_certificate.version, 2); + assert_eq!(cert.tbs_certificate.version, Version::V3); let target_serial: [u8; 16] = [ 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x49, 0xCF, 0x70, 0x66, 0x4D, 0x00, 0x00, 0x00, 0x02, diff --git a/x509/tests/pkix_extensions.rs b/x509/tests/pkix_extensions.rs index 5f15382fa..d97ae25fc 100644 --- a/x509/tests/pkix_extensions.rs +++ b/x509/tests/pkix_extensions.rs @@ -601,7 +601,7 @@ fn decode_cert() { let result = Certificate::from_der(der_encoded_cert); let cert: Certificate = result.unwrap(); - assert_eq!(cert.tbs_certificate.version, 2); + assert_eq!(cert.tbs_certificate.version, Version::V3); let target_serial: [u8; 1] = [2]; assert_eq!( cert.tbs_certificate.serial_number,