picky-asn1-x509-0.10.0/.cargo_vcs_info.json0000644000000001550000000000100135750ustar { "git": { "sha1": "57ef35d478658d95045da204935fa45ef419fb06" }, "path_in_vcs": "picky-asn1-x509" }picky-asn1-x509-0.10.0/CHANGELOG.md000064400000000000000000000130320072674642500142240ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased ## [0.10.0] 2023-06-01 (Unreleased) ### Fixed - Fixed `AlgorithmIdentifier` parsing: made `ECParameters` non-optional for EC keys - Fixed `ECParameters` - `public_key` now allowed to be optional - Fixed broken wasm compilation ### Added - `oid` is now added as re-export - New API methods: - `EcParameters::curve_oid` - `PrivateKeyInfo::new_ec_encryption` - `signature::EcdsaSignatureValue` - `AlgorithmIdentifier::is_one_of` - `AlgorithmIdentifier::new_x25519` - `AlgorithmIdentifier::new_ed448` - `AlgorithmIdentifier::new_x448` - `PrivateKeyInfo::new_ed_encryption` - `SubjectPublicKeyInfo::new_ed_key` - New constants: - `private_key_info::PRIVATE_KEY_INFO_VERSION_1` - `private_key_info::PRIVATE_KEY_INFO_VERSION_2` - Support of Ed25519/X25519/Ed448/X448 key structures ### Changed - (Breaking) `AlgorithmIdentifier::new_elliptic_curve` now accepts `EcParameters` instead of `impl Into>` - (Breaking) `AlgorithmIdentifierParameters::Ec` now have `EcParameters` instead of `Option` - (Breaking) `SubjectPublicKeyInfo::new_ec_key` now accepts curve's `ObjectIdentifier` and point as `BitString` - `PrivateKeyInfo` structure now also could represent newer `OneAsymmetricKey` structure (structures are backward-compatible). This allows to represent Ed keys with public key field set ## [0.9.0] 2022-11-07 ### Added - More OIDs such as PKINIT_AUTH_DATA and PKINIT_DH_KEY_DATA - Support for BMPString ## [0.8.0] 2022-08-01 ### Added - Implement `Zeroize` on `ECPrivateKey` and `RsaPrivateKey` (behind feature `zeroize`) ### Changed - Bump minimal rustc version to 1.60 ## [0.7.1] 2022-05-20 ### Added - OIDs used by NLA protocols ## [0.7.0] 2022-03-04 ### Added - Support for Authenticode timestamp deserialization/serialization - CTL implementation behind `ctl` feature - New `SpcSipInfo` struct - Add serialization/deserialization of Authenticode `TimestampRequest` - Add timestamp request OID - Add a few methods for creating an Attribute without usage low-level API: - `Attribute::new_content_type_pkcs7` - `Attribute::new_signing_time` - `Attribute::new_message_digest` - Add `EncapsulatedContentInfo::new_pkcs7_data` method ### Changed - (Breaking) `ShaVariant` enum is extended for MD5 and SH1 algorithms - (Breaking) Add `SpcStatementType` variant in `AttributeValues` enum - (Breaking) Add `SigningTime` variant in `AttributeValues` enum - `SpcAttributeAndOptionalValue` now supports both `SpcPeImageData` and `SpcSipInfo` values - Bump minimal rustc version to 1.56 ### Fixed - SignedData: - (Breaking) `RevocationInfoChoice` field is now optional as specified by the RFC - (Breaking) `CertificateSet` is now a `Vec` which can accept both a normal `Certificate` and an `other` kind of certificate as specified by the RFC ## [0.6.1] 2021-06-02 ### Added - More ECC OIDs ([#87](https://github.com/Devolutions/picky-rs/pull/87)) ## [0.6.0] 2021-05-27 ### Added - Support for V1 and V2 X509 certificates ([#83](https://github.com/Devolutions/picky-rs/pull/83)) - Support for `CrlNumber` extension ([#83](https://github.com/Devolutions/picky-rs/pull/83)) - PKCS7 implementation behind `pkcs7` feature ([#83](https://github.com/Devolutions/picky-rs/pull/83)) ### Changed - More supported attribute values: `ContentType`, `MessageDigest` and `SpcSpOpusInfo` ([#83](https://github.com/Devolutions/picky-rs/pull/83)) - Fix clippy upper case acronyms warning in a few places ([#85](https://github.com/Devolutions/picky-rs/pull/85)) ### Removed - Remove `ImplicitCurve` from `EcParameters` enum ([#85](https://github.com/Devolutions/picky-rs/pull/85)) ## [0.5.0] 2021-03-04 ### Added - Support for attributes in `CertificationRequestInfo` ([#78](https://github.com/Devolutions/picky-rs/pull/78)) ## [0.4.0] 2020-11-20 ### Added - OIDs from RFC8410 ([#72](https://github.com/Devolutions/picky-rs/pull/72)) - Support for Ed25519 `AlgorithmIdentifier` and `PublicKey` ([#72](https://github.com/Devolutions/picky-rs/pull/72)) ## [0.3.4] 2020-10-21 ### Changed - `AlgorithmIdentifier` parser has been made more lenient. For instance, `rsa-export-0.1.1` crate does not serialize the "NULL" parameter with rsa encryption OID. Such input is not rejected anymore. ## [0.3.3] 2020-10-13 ### Added - Documentation on `oids` module. ## [0.3.2] 2020-09-04 ### Added - `legacy` feature to support previously valid `RSAPrivateKey` with 6 components instead of 9 as specified by the RFC. Missing components are instead computed on the fly as required. ## [0.3.1] 2020-08-31 ### Changed - Update dependencies ## [0.3.0] 2020-08-21 ### Added - `DigestInfo` from RFC8017 ### Changed - `RSAPrivateKey` fields are now `pub` - `PrivateKeyInfo::new_rsa_encryption` takes 6 arguments instead of 8 ### Deprecated - `RSAPrivateKey` getters are deprecated in favor of direct access of public fields ## [0.2.0] 2020-08-20 ### Added - NIST signature related OIDs - `AlgorithmIdentifier::new_sha3_384_with_rsa_encryption` constructor - `AlgorithmIdentifier::new_sha3_512_with_rsa_encryption` constructor - Support for email attribute in certificate subject ### Changed - Rename "organisation" to "organization" - Change attribute structure in directory names to follow common practices ### Fixed - `RSAPrivateKey` is now RFC8017 compliant picky-asn1-x509-0.10.0/Cargo.toml0000644000000034710000000000100115770ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.60" name = "picky-asn1-x509" version = "0.10.0" authors = [ "Benoît CORTIER ", "Sergey Noskov ", "Kim Altintop ", "Joe Ellis ", "Hugues de Valon ", "Isode Ltd./Geobert Quach ", "Alexandr Yusuk ", ] description = "Provides ASN1 types defined by X.509 related RFCs" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/Devolutions/picky-rs" [dependencies.base64] version = "0.21" [dependencies.num-bigint-dig] version = "0.8.2" optional = true [dependencies.oid] version = "0.2.1" features = ["serde_support"] [dependencies.picky-asn1] version = "0.7" [dependencies.picky-asn1-der] version = "0.4" [dependencies.serde] version = "1.0.152" features = ["derive"] [dependencies.widestring] version = "0.5.1" features = ["alloc"] optional = true default-features = false [dependencies.zeroize] version = "^1.5" optional = true [dev-dependencies.hex] version = "0.4.3" [dev-dependencies.num-bigint-dig] version = "0.8.2" [dev-dependencies.pretty_assertions] version = "1.3.0" [features] ctl = ["pkcs7"] legacy = ["num-bigint-dig"] pkcs7 = ["widestring"] zeroize = [ "dep:zeroize", "picky-asn1/zeroize", ] picky-asn1-x509-0.10.0/Cargo.toml.orig000064400000000000000000000023630072674642500153070ustar 00000000000000[package] name = "picky-asn1-x509" version = "0.10.0" authors = [ "Benoît CORTIER ", "Sergey Noskov ", "Kim Altintop ", "Joe Ellis ", "Hugues de Valon ", "Isode Ltd./Geobert Quach ", "Alexandr Yusuk ", ] description = "Provides ASN1 types defined by X.509 related RFCs" edition = "2021" rust-version = "1.60" license = "MIT OR Apache-2.0" repository = "https://github.com/Devolutions/picky-rs" [dependencies] picky-asn1 = { version = "0.7", path = "../picky-asn1" } picky-asn1-der = { version = "0.4", path = "../picky-asn1-der" } serde = { version = "1.0.152", features = ["derive"] } oid = { version = "0.2.1", features = ["serde_support"] } base64 = "0.21" num-bigint-dig = { version = "0.8.2", optional = true } widestring = { version = "0.5.1", default-features = false, features = ["alloc"], optional = true } zeroize = { version = "^1.5", optional = true } [dev-dependencies] num-bigint-dig = "0.8.2" pretty_assertions = "1.3.0" hex = "0.4.3" [features] legacy = ["num-bigint-dig"] pkcs7 = ["widestring"] ctl = ["pkcs7"] zeroize = ["dep:zeroize", "picky-asn1/zeroize"] picky-asn1-x509-0.10.0/README.md000064400000000000000000000022610072674642500136740ustar 00000000000000[![Crates.io](https://img.shields.io/crates/v/picky-asn1-x509.svg)](https://crates.io/crates/picky-asn1-x509) [![docs.rs](https://docs.rs/picky-asn1-x509/badge.svg)](https://docs.rs/picky-asn1-x509) ![Crates.io](https://img.shields.io/crates/l/picky-asn1-x509) Compatible with rustc 1.60. Minimal rustc version bumps happen [only with minor number bumps in this project](https://github.com/Devolutions/picky-rs/issues/89#issuecomment-868303478). # picky-asn1-x509 Provide implementation for types defined in [X.509 RFC](https://tools.ietf.org/html/rfc5280) and related RFC ([PKCS#8](https://tools.ietf.org/html/rfc5208), [PKCS#10](https://tools.ietf.org/html/rfc2986)). This crate doesn't provide an easy to use API to create, read and validate X.509 certificates. This is a low-level library providing only raw types for serialization and deserialization purposes. These types are implementing `serde`'s `Serialize` and `Deserialize` and are to be used with [picky-asn1-der](https://crates.io/crates/picky-asn1-der). If you're looking for a higher level API, you may be interested by the [picky crate](https://crates.io/crates/picky) which uses this library internally and provides a nicer API. picky-asn1-x509-0.10.0/src/algorithm_identifier.rs000064400000000000000000001125330072674642500177460ustar 00000000000000use crate::oids; use oid::ObjectIdentifier; use picky_asn1::tag::{Tag, TagPeeker}; use picky_asn1::wrapper::ExplicitContextTag0; use picky_asn1::wrapper::ExplicitContextTag1; use picky_asn1::wrapper::ExplicitContextTag2; use picky_asn1::wrapper::{IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1}; use serde::{de, ser, Deserialize, Serialize}; use std::cmp::Ordering; use std::error::Error; use std::fmt; /// unsupported algorithm #[derive(Debug)] pub struct UnsupportedAlgorithmError { pub algorithm: String, } impl fmt::Display for UnsupportedAlgorithmError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "unsupported algorithm: {}", self.algorithm) } } impl Error for UnsupportedAlgorithmError {} #[derive(Debug, PartialEq, Eq, Clone)] pub struct AlgorithmIdentifier { algorithm: ObjectIdentifierAsn1, parameters: AlgorithmIdentifierParameters, } impl AlgorithmIdentifier { pub fn oid(&self) -> &ObjectIdentifier { &self.algorithm.0 } pub fn oid_asn1(&self) -> &ObjectIdentifierAsn1 { &self.algorithm } pub fn parameters(&self) -> &AlgorithmIdentifierParameters { &self.parameters } pub fn is_a(&self, algorithm: ObjectIdentifier) -> bool { algorithm.eq(&self.algorithm.0) } pub fn is_one_of(&self, algorithms: impl IntoIterator) -> bool { algorithms.into_iter().any(|oid| self.is_a(oid)) } pub fn new_md5_with_rsa_encryption() -> Self { Self { algorithm: oids::md5_with_rsa_encryption().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha1_with_rsa_encryption() -> Self { Self { algorithm: oids::sha1_with_rsa_encryption().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha1() -> Self { Self { algorithm: oids::sha1().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha224_with_rsa_encryption() -> Self { Self { algorithm: oids::sha224_with_rsa_encryption().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha256_with_rsa_encryption() -> Self { Self { algorithm: oids::sha256_with_rsa_encryption().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha384_with_rsa_encryption() -> Self { Self { algorithm: oids::sha384_with_rsa_encryption().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha512_with_rsa_encryption() -> Self { Self { algorithm: oids::sha512_with_rsa_encryption().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha3_384_with_rsa_encryption() -> Self { Self { algorithm: oids::id_rsassa_pkcs1_v1_5_with_sha3_384().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_sha3_512_with_rsa_encryption() -> Self { Self { algorithm: oids::id_rsassa_pkcs1_v1_5_with_sha3_512().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_rsassa_pss(parameters: RsassaPssParams) -> Self { Self { algorithm: oids::rsassa_pss().into(), parameters: AlgorithmIdentifierParameters::RsassaPss(parameters), } } pub fn new_rsa_encryption() -> Self { Self { algorithm: oids::rsa_encryption().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_rsa_encryption_with_sha(variant: ShaVariant) -> Result { let algorithm = match variant { ShaVariant::SHA2_224 => oids::sha224_with_rsa_encryption(), ShaVariant::SHA2_256 => oids::sha256_with_rsa_encryption(), ShaVariant::SHA2_384 => oids::sha384_with_rsa_encryption(), ShaVariant::SHA2_512 => oids::sha512_with_rsa_encryption(), ShaVariant::SHA3_384 => oids::id_rsassa_pkcs1_v1_5_with_sha3_384(), ShaVariant::SHA3_512 => oids::id_rsassa_pkcs1_v1_5_with_sha3_512(), _ => { return Err(UnsupportedAlgorithmError { algorithm: format!("{:?}", variant), }) } }; Ok(Self { algorithm: algorithm.into(), parameters: AlgorithmIdentifierParameters::Null, }) } pub fn new_dsa_with_sha1() -> Self { Self { algorithm: oids::dsa_with_sha1().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_ecdsa_with_sha512() -> Self { Self { algorithm: oids::ecdsa_with_sha512().into(), parameters: AlgorithmIdentifierParameters::Null, } } pub fn new_ecdsa_with_sha384() -> Self { Self { algorithm: oids::ecdsa_with_sha384().into(), parameters: AlgorithmIdentifierParameters::None, } } pub fn new_ecdsa_with_sha256() -> Self { Self { algorithm: oids::ecdsa_with_sha256().into(), parameters: AlgorithmIdentifierParameters::None, } } pub fn new_elliptic_curve(ec_params: EcParameters) -> Self { Self { algorithm: oids::ec_public_key().into(), parameters: AlgorithmIdentifierParameters::Ec(ec_params), } } /// Create new algorithm identifier without checking if the algorithm parameters are valid for /// the given algorithm. pub(crate) fn new_unchecked(algorithm: ObjectIdentifier, parameters: AlgorithmIdentifierParameters) -> Self { Self { algorithm: algorithm.into(), parameters, } } pub fn new_ed25519() -> Self { Self { algorithm: oids::ed25519().into(), parameters: AlgorithmIdentifierParameters::None, } } pub fn new_x25519() -> Self { Self { algorithm: oids::x25519().into(), parameters: AlgorithmIdentifierParameters::None, } } pub fn new_ed448() -> Self { Self { algorithm: oids::ed448().into(), parameters: AlgorithmIdentifierParameters::None, } } pub fn new_x448() -> Self { Self { algorithm: oids::x448().into(), parameters: AlgorithmIdentifierParameters::None, } } pub fn new_aes128(mode: AesMode, params: AesParameters) -> Self { Self { algorithm: mode.to_128bit_oid(), parameters: AlgorithmIdentifierParameters::Aes(params), } } pub fn new_aes192(mode: AesMode, params: AesParameters) -> Self { Self { algorithm: mode.to_192bit_oid(), parameters: AlgorithmIdentifierParameters::Aes(params), } } pub fn new_aes256(mode: AesMode, params: AesParameters) -> Self { Self { algorithm: mode.to_256bit_oid(), parameters: AlgorithmIdentifierParameters::Aes(params), } } pub fn new_sha(variant: ShaVariant) -> Self { Self { algorithm: variant.into(), parameters: AlgorithmIdentifierParameters::Null, } } } impl ser::Serialize for AlgorithmIdentifier { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(2))?; seq.serialize_element(&self.algorithm)?; match &self.parameters { AlgorithmIdentifierParameters::None => {} AlgorithmIdentifierParameters::Null => { seq.serialize_element(&())?; } AlgorithmIdentifierParameters::Ec(ec_params) => { seq.serialize_element(ec_params)?; } AlgorithmIdentifierParameters::Aes(aes_params) => { seq.serialize_element(aes_params)?; } AlgorithmIdentifierParameters::RsassaPss(rsa_params) => { seq.serialize_element(rsa_params)?; } } seq.end() } } impl<'de> de::Deserialize<'de> for AlgorithmIdentifier { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = AlgorithmIdentifier; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded algorithm identifier") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let oid: ObjectIdentifierAsn1 = seq_next_element!(seq, AlgorithmIdentifier, "algorithm oid"); let args = match Into::::into(&oid.0).as_str() { oids::RSA_ENCRYPTION | oids::SHA1_WITH_RSA_ENCRYPTION | oids::SHA224_WITH_RSA_ENCRYPTION | oids::SHA256_WITH_RSA_ENCRYPTION | oids::SHA384_WITH_RSA_ENCRYPTION | oids::SHA512_WITH_RSA_ENCRYPTION => { // Try to deserialize next element in sequence. // Error is ignored because some implementations just leave no parameter at all for // RSA encryption (ie: rsa-export-0.1.1 crate) but we still want to be able // to parse their output. let _ = seq.next_element::<()>(); AlgorithmIdentifierParameters::Null } oids::RSASSA_PSS => AlgorithmIdentifierParameters::RsassaPss(seq_next_element!( seq, RsassaPssParams, "RSASSA-PSS parameters" )), oids::ECDSA_WITH_SHA384 | oids::ECDSA_WITH_SHA256 | oids::ECDSA_WITH_SHA512 | oids::ED25519 | oids::ED448 | oids::X25519 | oids::X448 => AlgorithmIdentifierParameters::None, oids::DSA_WITH_SHA1 => { // A note from [RFC 3927](https://www.ietf.org/rfc/rfc3279.txt) // When the id-dsa-with-sha1 algorithm identifier appears as the // algorithm field in an AlgorithmIdentifier, the encoding SHALL omit // the parameters field. That is, the AlgorithmIdentifier SHALL be a // SEQUENCE of one component: the OBJECT IDENTIFIER id-dsa-with-sha1. AlgorithmIdentifierParameters::None } // A note from [RFC 5480](https://tools.ietf.org/html/rfc5480#section-2.1.1) // The parameter for id-ecPublicKey is as follows and MUST always be present oids::EC_PUBLIC_KEY => AlgorithmIdentifierParameters::Ec(seq_next_element!( seq, EcParameters, AlgorithmIdentifier, "elliptic curves parameters" )), // AES x if x.starts_with("2.16.840.1.101.3.4.1.") => AlgorithmIdentifierParameters::Aes( seq_next_element!(seq, AlgorithmIdentifier, "aes algorithm identifier"), ), // SHA x if x.starts_with("2.16.840.1.101.3.4.2.") || x == oids::SHA1 => { seq_next_element!(seq, AlgorithmIdentifier, "sha algorithm identifier"); AlgorithmIdentifierParameters::Null } _ => { return Err(serde_invalid_value!( AlgorithmIdentifier, "unsupported algorithm (unknown oid)", "a supported algorithm" )); } }; Ok(AlgorithmIdentifier { algorithm: oid, parameters: args, }) } } deserializer.deserialize_seq(Visitor) } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum AlgorithmIdentifierParameters { None, Null, Aes(AesParameters), Ec(EcParameters), RsassaPss(RsassaPssParams), } /// [RFC 4055 #3.1](https://www.rfc-editor.org/rfc/rfc4055#section-3.1) /// /// ```not_rust /// RSASSA-PSS-params ::= SEQUENCE { /// hashAlgorithm [0] HashAlgorithm DEFAULT /// sha1Identifier, /// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT /// mgf1SHA1Identifier, /// saltLength [2] INTEGER DEFAULT 20, /// trailerField [3] INTEGER DEFAULT 1 } /// ``` /// /// Implementations that perform signature generation MUST omit the trailerField /// field, indicating that the default trailer field value was used... thus the /// reason no trailer field is specified in this structure. #[derive(Clone, Debug, Eq, PartialEq)] pub struct RsassaPssParams { pub hash_algorithm: HashAlgorithm, pub mask_gen_algorithm: MaskGenAlgorithm, pub salt_length: usize, } impl RsassaPssParams { pub fn new(hash_algorithm: HashAlgorithm) -> Self { Self { hash_algorithm, mask_gen_algorithm: MaskGenAlgorithm::new(hash_algorithm), salt_length: hash_algorithm.len(), } } } impl ser::Serialize for RsassaPssParams { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(3))?; seq.serialize_element(&ExplicitContextTag0(&self.hash_algorithm))?; seq.serialize_element(&ExplicitContextTag1(&self.mask_gen_algorithm))?; seq.serialize_element(&ExplicitContextTag2(&IntegerAsn1::from_bytes_be_signed( self.salt_length.to_be_bytes().to_vec(), )))?; seq.end() } } fn usize_from_be_bytes(asn1: &IntegerAsn1) -> usize { let bytes = asn1.as_unsigned_bytes_be(); match bytes.len().cmp(&8) { Ordering::Greater => usize::MAX, Ordering::Less => { const USIZE_SIZE: usize = std::mem::size_of::(); let mut tmp = [0; USIZE_SIZE]; tmp[(USIZE_SIZE - bytes.len())..USIZE_SIZE].clone_from_slice(bytes); usize::from_be_bytes(tmp) } // unwrap is safe since we know this is exactly 8 bytes. Ordering::Equal => usize::from_be_bytes(bytes.try_into().unwrap()), } } impl<'de> de::Deserialize<'de> for RsassaPssParams { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = RsassaPssParams; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded RsassaPssParams") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let hash = seq_next_element!(seq, ExplicitContextTag0, HashAlgorithm, "cont [0]"); let mask_gen = seq_next_element!( seq, ExplicitContextTag1, MaskGenAlgorithm, "maskGenAlgorithm" ); let salt = seq_next_element!(seq, ExplicitContextTag2, IntegerAsn1, "saltLength"); Ok(RsassaPssParams { hash_algorithm: hash.0, mask_gen_algorithm: mask_gen.0, salt_length: usize_from_be_bytes(&salt.0), }) } } deserializer.deserialize_seq(Visitor) } } // https://www.rfc-editor.org/rfc/rfc4055#section-2.1 #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[allow(non_camel_case_types)] pub enum HashAlgorithm { // Nobody should be using SHA1 in 2023, it is completely broken... and the RFC for RsassaPssParams // adds needless complexity in regard to requiring the omission of parameters if SHA1 is used. //SHA1, SHA224, SHA256, SHA384, SHA512, } impl HashAlgorithm { pub fn len(&self) -> usize { use HashAlgorithm::*; match self { //SHA1 => 20, SHA224 => 28, SHA256 => 32, SHA384 => 48, SHA512 => 64, } } pub fn is_empty(&self) -> bool { false } } impl From<&HashAlgorithm> for ObjectIdentifierAsn1 { fn from(variant: &HashAlgorithm) -> Self { use HashAlgorithm::*; match variant { //SHA1 => oids::sha1().into(), SHA224 => oids::sha224().into(), SHA256 => oids::sha256().into(), SHA384 => oids::sha384().into(), SHA512 => oids::sha512().into(), } } } impl TryFrom for HashAlgorithm { type Error = UnsupportedAlgorithmError; fn try_from(oid: ObjectIdentifierAsn1) -> Result { match Into::::into(oid.0).as_str() { //oids::SHA1 => Ok(HashAlgorithm::SHA1), oids::SHA224 => Ok(HashAlgorithm::SHA224), oids::SHA256 => Ok(HashAlgorithm::SHA256), oids::SHA384 => Ok(HashAlgorithm::SHA384), oids::SHA512 => Ok(HashAlgorithm::SHA512), unsupported => Err(UnsupportedAlgorithmError { algorithm: unsupported.to_string(), }), } } } impl ser::Serialize for HashAlgorithm { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(2))?; seq.serialize_element(&ObjectIdentifierAsn1::from(self))?; seq.serialize_element(&())?; seq.end() } } impl<'de> de::Deserialize<'de> for HashAlgorithm { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = HashAlgorithm; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded HashAlgorithm") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let oid: ObjectIdentifierAsn1 = seq_next_element!(seq, ObjectIdentifierAsn1, "oid of hashAlgorithm"); let _: Option<()> = seq.next_element()?; oid.try_into().map_err(|_| { serde_invalid_value!( HashAlgorithm, "unsupported or unknown hash algorithm", "a supported hash algorithm" ) }) } } deserializer.deserialize_seq(Visitor) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct MaskGenAlgorithm { pub mask_gen_algorithm: ObjectIdentifierAsn1, pub hash_algorithm: HashAlgorithm, } impl MaskGenAlgorithm { pub fn new(hash_algorithm: HashAlgorithm) -> Self { Self { mask_gen_algorithm: ObjectIdentifierAsn1::from(oids::id_mgf1()), hash_algorithm, } } } impl ser::Serialize for MaskGenAlgorithm { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(2))?; seq.serialize_element(&self.mask_gen_algorithm)?; seq.serialize_element(&self.hash_algorithm)?; seq.end() } } impl<'de> de::Deserialize<'de> for MaskGenAlgorithm { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = MaskGenAlgorithm; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded MaskGenAlgorithm") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let mask_gen_algorithm: ObjectIdentifierAsn1 = seq_next_element!(seq, ObjectIdentifierAsn1, "oid of maskGenAlgorithm"); let hash_algorithm: HashAlgorithm = seq_next_element!(seq, HashAlgorithm, "hashAlgorithm"); Ok(MaskGenAlgorithm { mask_gen_algorithm, hash_algorithm, }) } } deserializer.deserialize_seq(Visitor) } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum EcParameters { NamedCurve(ObjectIdentifierAsn1), // -- implicitCurve and specifiedCurve MUST NOT be used in PKIX. // ImplicitCurve, // SpecifiedCurve(SpecifiedECDomain) } impl EcParameters { pub fn curve_oid(&self) -> &ObjectIdentifier { match self { EcParameters::NamedCurve(oid) => &oid.0, } } } impl From for EcParameters { fn from(oid: ObjectIdentifierAsn1) -> Self { Self::NamedCurve(oid) } } impl From for EcParameters { fn from(oid: ObjectIdentifier) -> Self { Self::NamedCurve(oid.into()) } } impl ser::Serialize for EcParameters { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { EcParameters::NamedCurve(oid) => oid.serialize(serializer), } } } impl<'de> de::Deserialize<'de> for EcParameters { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = EcParameters; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded DirectoryString") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, EcParameters, "choice tag"); let curve_oid = match tag_peeker.next_tag { Tag::OID => seq_next_element!(seq, ObjectIdentifierAsn1, "NamedCurve object identifier"), _ => { return Err(serde_invalid_value!( EcParameters, "unsupported or unknown elliptic curve parameter", "a supported elliptic curve parameter" )) } }; Ok(EcParameters::NamedCurve(curve_oid)) } } deserializer.deserialize_enum("DirectoryString", &["NamedCurve", "ImplicitCurve"], Visitor) } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum AesMode { Ecb, Cbc, Ofb, Cfb, Wrap, Gcm, Ccm, WrapPad, } #[derive(Debug, PartialEq, Eq, Clone)] pub enum AesParameters { Null, InitializationVector(OctetStringAsn1), AuthenticatedEncryptionParameters(AesAuthEncParams), } #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] pub struct AesAuthEncParams { nonce: OctetStringAsn1, icv_len: IntegerAsn1, } impl AesMode { fn to_128bit_oid(self) -> ObjectIdentifierAsn1 { match self { AesMode::Ecb => oids::aes128_ecb().into(), AesMode::Cbc => oids::aes128_cbc().into(), AesMode::Ofb => oids::aes128_ofb().into(), AesMode::Cfb => oids::aes128_cfb().into(), AesMode::Wrap => oids::aes128_wrap().into(), AesMode::Gcm => oids::aes128_gcm().into(), AesMode::Ccm => oids::aes128_ccm().into(), AesMode::WrapPad => oids::aes128_wrap_pad().into(), } } fn to_192bit_oid(self) -> ObjectIdentifierAsn1 { match self { AesMode::Ecb => oids::aes192_ecb().into(), AesMode::Cbc => oids::aes192_cbc().into(), AesMode::Ofb => oids::aes192_ofb().into(), AesMode::Cfb => oids::aes192_cfb().into(), AesMode::Wrap => oids::aes192_wrap().into(), AesMode::Gcm => oids::aes192_gcm().into(), AesMode::Ccm => oids::aes192_ccm().into(), AesMode::WrapPad => oids::aes192_wrap_pad().into(), } } fn to_256bit_oid(self) -> ObjectIdentifierAsn1 { match self { AesMode::Ecb => oids::aes256_ecb().into(), AesMode::Cbc => oids::aes256_cbc().into(), AesMode::Ofb => oids::aes256_ofb().into(), AesMode::Cfb => oids::aes256_cfb().into(), AesMode::Wrap => oids::aes256_wrap().into(), AesMode::Gcm => oids::aes256_gcm().into(), AesMode::Ccm => oids::aes256_ccm().into(), AesMode::WrapPad => oids::aes256_wrap_pad().into(), } } } impl ser::Serialize for AesParameters { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match self { AesParameters::Null => ().serialize(serializer), AesParameters::InitializationVector(iv) => iv.serialize(serializer), AesParameters::AuthenticatedEncryptionParameters(params) => params.serialize(serializer), } } } impl<'de> de::Deserialize<'de> for AesParameters { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = AesParameters; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded DirectoryString") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, AesParameters, "choice tag"); match tag_peeker.next_tag { Tag::OCTET_STRING => Ok(AesParameters::InitializationVector(seq_next_element!( seq, AesParameters, "Object Identifier" ))), Tag::NULL => { seq.next_element::<()>()?.expect("should not panic"); Ok(AesParameters::Null) } Tag::SEQUENCE => Ok(AesParameters::AuthenticatedEncryptionParameters(seq_next_element!( seq, AesAuthEncParams, "AES Authenticated Encryption parameters" ))), _ => Err(serde_invalid_value!( AesParameters, "unsupported or unknown AES parameter", "a supported AES parameter" )), } } } deserializer.deserialize_enum( "DirectoryString", &["Null", "InitializationVector", "AuthenticatedEncryptionParameters"], Visitor, ) } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[allow(non_camel_case_types)] // 'SHA2_512_224' is clearer than 'SHA2512224' or 'Sha2512224' imo pub enum ShaVariant { // TODO: rename enum (breaking) MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512, SHA2_512_224, SHA2_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256, } impl From for ObjectIdentifierAsn1 { fn from(variant: ShaVariant) -> Self { match variant { ShaVariant::MD5 => oids::md5().into(), ShaVariant::SHA1 => oids::sha1().into(), ShaVariant::SHA2_224 => oids::sha224().into(), ShaVariant::SHA2_256 => oids::sha256().into(), ShaVariant::SHA2_384 => oids::sha384().into(), ShaVariant::SHA2_512 => oids::sha512().into(), ShaVariant::SHA2_512_224 => oids::sha512_224().into(), ShaVariant::SHA2_512_256 => oids::sha512_256().into(), ShaVariant::SHA3_224 => oids::sha3_224().into(), ShaVariant::SHA3_256 => oids::sha3_256().into(), ShaVariant::SHA3_384 => oids::sha3_384().into(), ShaVariant::SHA3_512 => oids::sha3_512().into(), ShaVariant::SHAKE128 => oids::shake128().into(), ShaVariant::SHAKE256 => oids::shake256().into(), } } } impl TryFrom for ShaVariant { type Error = UnsupportedAlgorithmError; fn try_from(oid: ObjectIdentifierAsn1) -> Result { match Into::::into(oid.0).as_str() { oids::MD5 => Ok(ShaVariant::MD5), oids::SHA1 => Ok(ShaVariant::SHA1), oids::SHA224 => Ok(ShaVariant::SHA2_224), oids::SHA256 => Ok(ShaVariant::SHA2_256), oids::SHA384 => Ok(ShaVariant::SHA2_384), oids::SHA512 => Ok(ShaVariant::SHA2_512), oids::SHA512_224 => Ok(ShaVariant::SHA2_512_224), oids::SHA512_256 => Ok(ShaVariant::SHA2_512_256), oids::SHA3_224 => Ok(ShaVariant::SHA3_224), oids::SHA3_256 => Ok(ShaVariant::SHA3_256), oids::SHA3_384 => Ok(ShaVariant::SHA3_384), oids::SHA3_512 => Ok(ShaVariant::SHA3_512), oids::SHAKE128 => Ok(ShaVariant::SHAKE128), oids::SHAKE256 => Ok(ShaVariant::SHAKE256), unsupported => Err(UnsupportedAlgorithmError { algorithm: unsupported.to_string(), }), } } } /// [PKCS #1: RSA Cryptography Specifications Version /// 2.2](https://tools.ietf.org/html/rfc8017.html#section-9.2) /// /// # Section 9.2 /// /// The type DigestInfo has the syntax: /// /// ```not_rust /// DigestInfo ::= SEQUENCE { /// digestAlgorithm AlgorithmIdentifier, /// digest OCTET STRING /// } /// ``` #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct DigestInfo { pub oid: AlgorithmIdentifier, pub digest: OctetStringAsn1, } #[cfg(test)] mod tests { use super::*; use base64::{engine::general_purpose, Engine as _}; #[test] fn aes_null_params() { let expected = [48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 1, 1, 5, 0]; let aes_id = AlgorithmIdentifier::new_aes128(AesMode::Ecb, AesParameters::Null); check_serde!(aes_id: AlgorithmIdentifier in expected); } #[test] fn aes_iv_params() { let expected = [ 48, 25, 6, 9, 96, 134, 72, 1, 101, 3, 4, 1, 1, 4, 12, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ]; let aes_id = AlgorithmIdentifier::new_aes128(AesMode::Ecb, AesParameters::InitializationVector(vec![0xA5; 12].into())); check_serde!(aes_id: AlgorithmIdentifier in expected); } #[test] fn aes_ae_params() { let expected = [ 48, 30, 6, 9, 96, 134, 72, 1, 101, 3, 4, 1, 1, 48, 17, 4, 12, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 1, 12, ]; let aes_id = AlgorithmIdentifier::new_aes128( AesMode::Ecb, AesParameters::AuthenticatedEncryptionParameters(AesAuthEncParams { nonce: vec![0xff; 12].into(), icv_len: vec![12].into(), }), ); check_serde!(aes_id: AlgorithmIdentifier in expected); } #[test] fn sha256() { let expected = [48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0]; let sha = AlgorithmIdentifier::new_sha(ShaVariant::SHA2_256); check_serde!(sha: AlgorithmIdentifier in expected); } #[test] fn ec_params() { let expected = [ 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, ]; let ec_params = AlgorithmIdentifier::new_elliptic_curve(EcParameters::NamedCurve(oids::ecdsa_with_sha256().into())); check_serde!(ec_params: AlgorithmIdentifier in expected); } #[test] fn digest_info() { let digest = picky_asn1_der::to_vec(&DigestInfo { oid: AlgorithmIdentifier::new_sha(ShaVariant::SHA2_256), // Random 32 bytes generated for a SHA256 hash digest: vec![ 0xf4, 0x12, 0x6b, 0x55, 0xbf, 0xcf, 0x8c, 0xc4, 0xe9, 0xe0, 0xbe, 0x5a, 0x9c, 0x16, 0x88, 0x55, 0x0f, 0x26, 0x00, 0x8c, 0x2c, 0xa5, 0xf6, 0xaf, 0xbd, 0xe7, 0x9c, 0x42, 0x22, 0xe9, 0x25, 0xed, ] .into(), }) .unwrap(); let expected = vec![ 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, 0xf4, 0x12, 0x6b, 0x55, 0xbf, 0xcf, 0x8c, 0xc4, 0xe9, 0xe0, 0xbe, 0x5a, 0x9c, 0x16, 0x88, 0x55, 0x0f, 0x26, 0x00, 0x8c, 0x2c, 0xa5, 0xf6, 0xaf, 0xbd, 0xe7, 0x9c, 0x42, 0x22, 0xe9, 0x25, 0xed, ]; assert_eq!(digest, expected); } #[test] fn rsa_pss_params_sha256() { let expected = [ 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20, ]; let structure = RsassaPssParams::new(HashAlgorithm::SHA256); check_serde!(structure: RsassaPssParams in expected); } #[test] fn rsa_pss_params_sha384() { let expected = [ 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x30, ]; let structure = RsassaPssParams::new(HashAlgorithm::SHA384); check_serde!(structure: RsassaPssParams in expected); } #[test] fn rsa_pss_params_sha512() { let expected = [ 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x40, ]; let structure = RsassaPssParams::new(HashAlgorithm::SHA512); check_serde!(structure: RsassaPssParams in expected); } #[test] fn rsa_pss_encryption() { let expected = [ 0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0A, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20, ]; let structure = AlgorithmIdentifier::new_rsassa_pss(RsassaPssParams::new(HashAlgorithm::SHA256)); check_serde!(structure: AlgorithmIdentifier in expected); } #[test] fn rsa_encryption() { let expected = [ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, ]; let rsa_encryption = AlgorithmIdentifier::new_rsa_encryption(); check_serde!(rsa_encryption: AlgorithmIdentifier in expected); } #[test] fn rsa_encryption_with_missing_params() { let encoded = [ 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, ]; let deserialized: AlgorithmIdentifier = picky_asn1_der::from_bytes(&encoded).expect("failed AlgorithmIdentifier deserialization"); pretty_assertions::assert_eq!( deserialized, AlgorithmIdentifier::new_rsa_encryption(), concat!("deserialized ", stringify!($item), " doesn't match") ); } } picky-asn1-x509-0.10.0/src/attribute.rs000064400000000000000000000130500072674642500155530ustar 00000000000000#[cfg(feature = "pkcs7")] use crate::pkcs7::content_info::SpcSpOpusInfo; use crate::{oids, Extension, Extensions}; use picky_asn1::date::UTCTime; use picky_asn1::wrapper::{Asn1SequenceOf, Asn1SetOf, ObjectIdentifierAsn1, OctetStringAsn1, UTCTimeAsn1}; use serde::{de, ser}; pub type Attributes = Asn1SequenceOf; /// [RFC 2985 page 15 and 16](https://tools.ietf.org/html/rfc2985#page-15) /// /// Accepted attribute types are `challengePassword` (TODO), `extensionRequest`, /// `contentType`, `messageDigest` and `spcSpOpusInfo` /// /// `spcSpOpusInfo` is behind the `pkcs7` feature. /// /// `contentType`, `messageDigest`, `spcSpOpusInfo` and `SigningTime` are used for [microsoft authenticode](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) #[derive(Clone, Debug, PartialEq)] pub enum AttributeValues { /// `extensionRequest` Extensions(Asn1SetOf), // the set will always have 1 element in this variant // TODO: support for challenge password // ChallengePassword(Asn1SetOf)) ContentType(Asn1SetOf), SpcStatementType(Asn1SetOf>), MessageDigest(Asn1SetOf), SigningTime(Asn1SetOf), #[cfg(feature = "pkcs7")] SpcSpOpusInfo(Asn1SetOf), Custom(picky_asn1_der::Asn1RawDer), // fallback } #[derive(Clone, Debug, PartialEq)] pub struct Attribute { pub ty: ObjectIdentifierAsn1, pub value: AttributeValues, } impl Attribute { pub fn new_extension_request(extensions: Vec) -> Self { Self { ty: oids::extension_request().into(), value: AttributeValues::Extensions(Asn1SetOf(vec![Extensions(extensions)])), } } pub fn new_content_type_pkcs7() -> Self { Self { ty: oids::content_type().into(), value: AttributeValues::ContentType(vec![oids::pkcs7().into()].into()), } } pub fn new_signing_time(signing_time: UTCTime) -> Self { Self { ty: oids::signing_time().into(), value: AttributeValues::SigningTime(vec![signing_time.into()].into()), } } pub fn new_message_digest(digest: Vec) -> Self { Self { ty: oids::message_digest().into(), value: AttributeValues::MessageDigest(vec![digest.into()].into()), } } } impl ser::Serialize for Attribute { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(2))?; seq.serialize_element(&self.ty)?; match &self.value { AttributeValues::Extensions(extensions) => seq.serialize_element(extensions)?, AttributeValues::Custom(der) => seq.serialize_element(der)?, AttributeValues::ContentType(oid) => seq.serialize_element(oid)?, AttributeValues::MessageDigest(octet_string) => seq.serialize_element(octet_string)?, AttributeValues::SigningTime(signing_time) => seq.serialize_element(signing_time)?, #[cfg(feature = "pkcs7")] AttributeValues::SpcSpOpusInfo(spc_sp_opus_info) => seq.serialize_element(spc_sp_opus_info)?, AttributeValues::SpcStatementType(spc_statement_type) => seq.serialize_element(spc_statement_type)?, } seq.end() } } impl<'de> de::Deserialize<'de> for Attribute { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Attribute; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded attribute") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let ty: ObjectIdentifierAsn1 = seq_next_element!(seq, Attribute, "type oid"); let value = match Into::::into(&ty.0).as_str() { oids::EXTENSION_REQ => { AttributeValues::Extensions(seq_next_element!(seq, Attribute, "at extension request")) } oids::CONTENT_TYPE => { AttributeValues::ContentType(seq_next_element!(seq, Attribute, "message digest oid")) } oids::MESSAGE_DIGEST => { AttributeValues::MessageDigest(seq_next_element!(seq, Attribute, "an octet string")) } oids::SIGNING_TIME => AttributeValues::SigningTime(seq_next_element!(seq, Attribute, "UTCTime")), #[cfg(feature = "pkcs7")] oids::SPC_SP_OPUS_INFO_OBJID => { AttributeValues::SpcSpOpusInfo(seq_next_element!(seq, Attribute, "an SpcSpOpusInfo object")) } oids::SPC_STATEMENT_TYPE => { AttributeValues::SpcStatementType(seq_next_element!(seq, Attribute, "an SpcStatementType")) } _ => AttributeValues::Custom(seq_next_element!(seq, Attribute, "at custom value")), }; Ok(Attribute { ty, value }) } } deserializer.deserialize_seq(Visitor) } } picky-asn1-x509-0.10.0/src/attribute_type_and_value.rs000064400000000000000000000236200072674642500206360ustar 00000000000000use crate::{oids, DirectoryString}; use picky_asn1::wrapper::{IA5StringAsn1, ObjectIdentifierAsn1}; use serde::{de, ser}; use std::fmt; #[derive(Debug, PartialEq, Eq, Clone)] pub enum AttributeTypeAndValueParameters { CommonName(DirectoryString), Surname(DirectoryString), SerialNumber(DirectoryString), CountryName(DirectoryString), LocalityName(DirectoryString), StateOrProvinceName(DirectoryString), StreetName(DirectoryString), OrganizationName(DirectoryString), OrganizationalUnitName(DirectoryString), EmailAddress(IA5StringAsn1), GivenName(DirectoryString), Phone(DirectoryString), Custom(picky_asn1_der::Asn1RawDer), } #[derive(Debug, PartialEq, Eq, Clone)] pub struct AttributeTypeAndValue { pub ty: ObjectIdentifierAsn1, pub value: AttributeTypeAndValueParameters, } impl AttributeTypeAndValue { pub fn new_common_name>(name: S) -> Self { Self { ty: oids::at_common_name().into(), value: AttributeTypeAndValueParameters::CommonName(name.into()), } } pub fn new_surname>(name: S) -> Self { Self { ty: oids::at_surname().into(), value: AttributeTypeAndValueParameters::Surname(name.into()), } } pub fn new_serial_number>(name: S) -> Self { Self { ty: oids::at_serial_number().into(), value: AttributeTypeAndValueParameters::SerialNumber(name.into()), } } pub fn new_country_name>(name: S) -> Self { Self { ty: oids::at_country_name().into(), value: AttributeTypeAndValueParameters::CountryName(name.into()), } } pub fn new_locality_name>(name: S) -> Self { Self { ty: oids::at_locality_name().into(), value: AttributeTypeAndValueParameters::LocalityName(name.into()), } } pub fn new_state_or_province_name>(name: S) -> Self { Self { ty: oids::at_state_or_province_name().into(), value: AttributeTypeAndValueParameters::StateOrProvinceName(name.into()), } } pub fn new_street_name>(name: S) -> Self { Self { ty: oids::at_street_name().into(), value: AttributeTypeAndValueParameters::StreetName(name.into()), } } pub fn new_organization_name>(name: S) -> Self { Self { ty: oids::at_organization_name().into(), value: AttributeTypeAndValueParameters::OrganizationName(name.into()), } } pub fn new_organizational_unit_name>(name: S) -> Self { Self { ty: oids::at_organizational_unit_name().into(), value: AttributeTypeAndValueParameters::OrganizationalUnitName(name.into()), } } pub fn new_email_address>(name: S) -> Self { Self { ty: oids::email_address().into(), value: AttributeTypeAndValueParameters::EmailAddress(name.into()), } } pub fn new_given_name>(name: S) -> Self { Self { ty: oids::at_given_name().into(), value: AttributeTypeAndValueParameters::GivenName(name.into()), } } pub fn new_phone>(name: S) -> Self { Self { ty: oids::at_phone().into(), value: AttributeTypeAndValueParameters::Phone(name.into()), } } } impl ser::Serialize for AttributeTypeAndValue { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(2))?; seq.serialize_element(&self.ty)?; match &self.value { AttributeTypeAndValueParameters::CommonName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::Surname(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::SerialNumber(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::CountryName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::LocalityName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::StateOrProvinceName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::StreetName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::OrganizationName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::OrganizationalUnitName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::EmailAddress(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::GivenName(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::Phone(name) => { seq.serialize_element(name)?; } AttributeTypeAndValueParameters::Custom(der) => { seq.serialize_element(der)?; } } seq.end() } } impl<'de> de::Deserialize<'de> for AttributeTypeAndValue { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = AttributeTypeAndValue; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded AttributeTypeAndValue") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let ty: ObjectIdentifierAsn1 = seq_next_element!(seq, AttributeTypeAndValue, "type oid"); let value = match Into::::into(&ty.0).as_str() { oids::AT_COMMON_NAME => AttributeTypeAndValueParameters::CommonName(seq_next_element!( seq, AttributeTypeAndValue, "at common name" )), oids::AT_SURNAME => AttributeTypeAndValueParameters::Surname(seq_next_element!( seq, AttributeTypeAndValue, "at surname" )), oids::AT_SERIAL_NUMBER => AttributeTypeAndValueParameters::SerialNumber(seq_next_element!( seq, AttributeTypeAndValue, "at serial number" )), oids::AT_COUNTRY_NAME => AttributeTypeAndValueParameters::CountryName(seq_next_element!( seq, AttributeTypeAndValue, "at country name" )), oids::AT_LOCALITY_NAME => AttributeTypeAndValueParameters::LocalityName(seq_next_element!( seq, AttributeTypeAndValue, "at locality name" )), oids::AT_STATE_OR_PROVINCE_NAME => AttributeTypeAndValueParameters::StateOrProvinceName( seq_next_element!(seq, AttributeTypeAndValue, "at state or province name"), ), oids::AT_STREET_NAME => AttributeTypeAndValueParameters::StreetName(seq_next_element!( seq, AttributeTypeAndValue, "at street name" )), oids::AT_ORGANIZATION_NAME => AttributeTypeAndValueParameters::OrganizationName( seq_next_element!(seq, AttributeTypeAndValue, "at organization name"), ), oids::AT_ORGANIZATIONAL_UNIT_NAME => AttributeTypeAndValueParameters::OrganizationalUnitName( seq_next_element!(seq, AttributeTypeAndValue, "at organizational unit name"), ), oids::EMAIL_ADDRESS => AttributeTypeAndValueParameters::EmailAddress(seq_next_element!( seq, AttributeTypeAndValue, "at email address" )), oids::AT_GIVENNAME => AttributeTypeAndValueParameters::GivenName(seq_next_element!( seq, AttributeTypeAndValue, "at given name" )), oids::AT_PHONE => AttributeTypeAndValueParameters::Phone(seq_next_element!( seq, AttributeTypeAndValue, "at phone" )), _ => AttributeTypeAndValueParameters::Custom(seq_next_element!( seq, AttributeTypeAndValue, "at custom value" )), }; Ok(AttributeTypeAndValue { ty, value }) } } deserializer.deserialize_seq(Visitor) } } picky-asn1-x509-0.10.0/src/certificate.rs000064400000000000000000000310620072674642500160350ustar 00000000000000use crate::{ AlgorithmIdentifier, AuthorityKeyIdentifier, BasicConstraints, Extension, ExtensionView, Extensions, Name, SubjectPublicKeyInfo, Validity, Version, }; use picky_asn1::wrapper::{BitStringAsn1, ExplicitContextTag0, ExplicitContextTag3, IntegerAsn1}; use serde::{de, Deserialize, Serialize}; use std::fmt; /// [RFC 5280 #4.1](https://tools.ietf.org/html/rfc5280#section-4.1) /// /// ```not_rust /// Certificate ::= SEQUENCE { /// tbsCertificate TBSCertificate, /// signatureAlgorithm AlgorithmIdentifier, /// signatureValue BIT STRING } /// ``` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Certificate { pub tbs_certificate: TbsCertificate, pub signature_algorithm: AlgorithmIdentifier, pub signature_value: BitStringAsn1, } impl Certificate { fn h_find_extension(&self, key_identifier_oid: &oid::ObjectIdentifier) -> Option<&Extension> { (self.tbs_certificate.extensions.0) .0 .iter() .find(|ext| ext.extn_id() == key_identifier_oid) } pub fn subject_key_identifier(&self) -> Option<&[u8]> { let ext = self.h_find_extension(&crate::oids::subject_key_identifier())?; match ext.extn_value() { ExtensionView::SubjectKeyIdentifier(ski) => Some(&ski.0), _ => None, } } pub fn authority_key_identifier(&self) -> Option<&AuthorityKeyIdentifier> { let ext = self.h_find_extension(&crate::oids::authority_key_identifier())?; match ext.extn_value() { ExtensionView::AuthorityKeyIdentifier(aki) => Some(aki), _ => None, } } pub fn basic_constraints(&self) -> Option<&BasicConstraints> { let ext = self.h_find_extension(&crate::oids::basic_constraints())?; match ext.extn_value() { ExtensionView::BasicConstraints(bc) => Some(bc), _ => None, } } pub fn extensions(&self) -> &[Extension] { (self.tbs_certificate.extensions.0).0.as_slice() } } /// [RFC 5280 #4.1](https://tools.ietf.org/html/rfc5280#section-4.1) /// /// ```not_rust /// TBSCertificate ::= SEQUENCE { /// version [0] EXPLICIT Version DEFAULT v1, /// serialNumber CertificateSerialNumber, /// signature AlgorithmIdentifier, /// issuer Name, /// validity Validity, /// subject Name, /// subjectPublicKeyInfo SubjectPublicKeyInfo, /// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, /// -- If present, version MUST be v2 or v3 /// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, /// -- If present, version MUST be v2 or v3 /// extensions [3] EXPLICIT Extensions OPTIONAL /// -- If present, version MUST be v3 /// } /// ``` #[derive(Serialize, Clone, Debug, PartialEq)] pub struct TbsCertificate { #[serde(skip_serializing_if = "version_is_default")] pub version: ExplicitContextTag0, pub serial_number: IntegerAsn1, pub signature: AlgorithmIdentifier, pub issuer: Name, pub validity: Validity, pub subject: Name, pub subject_public_key_info: SubjectPublicKeyInfo, // issuer_unique_id // subject_unique_id #[serde(skip_serializing_if = "extensions_are_empty")] pub extensions: ExplicitContextTag3, } fn version_is_default(version: &Version) -> bool { version == &Version::default() } // Implement Deserialize manually to support missing version field (i.e.: fallback as V1) impl<'de> de::Deserialize<'de> for TbsCertificate { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = TbsCertificate; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct TBSCertificate") } fn visit_seq(self, mut seq: V) -> Result where V: de::SeqAccess<'de>, { let version: ExplicitContextTag0 = seq.next_element().unwrap_or_default().unwrap_or_default(); let serial_number = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?; let signature = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?; let issuer = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?; let validity = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?; let subject = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?; let subject_public_key_info = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(6, &self))?; let extensions: ExplicitContextTag3 = seq .next_element()? .unwrap_or_else(|| Some(Extensions(Vec::new()).into())) .unwrap_or_else(|| Extensions(Vec::new()).into()); if version.0 != Version::V3 && !(extensions.0).0.is_empty() { return Err(serde_invalid_value!( TbsCertificate, "Version is not V3, but Extensions are present", "no Extensions" )); } Ok(TbsCertificate { version, serial_number, signature, issuer, validity, subject, subject_public_key_info, extensions, }) } } deserializer.deserialize_seq(Visitor) } } fn extensions_are_empty(extensions: &Extensions) -> bool { extensions.0.is_empty() } #[cfg(test)] mod tests { use super::*; use crate::{DirectoryName, Extension, KeyIdentifier, KeyUsage}; use base64::{engine::general_purpose, Engine as _}; use num_bigint_dig::BigInt; use picky_asn1::bit_string::BitString; use picky_asn1::date::UTCTime; #[test] fn x509_v3_certificate() { let encoded = general_purpose::STANDARD .decode( "MIIEGjCCAgKgAwIBAgIEN8NXxDANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQ\ DDBdjb250b3NvLmxvY2FsIEF1dGhvcml0eTAeFw0xOTEwMTcxNzQxMjhaFw0yMjEwM\ TYxNzQxMjhaMB0xGzAZBgNVBAMMEnRlc3QuY29udG9zby5sb2NhbDCCASIwDQYJKoZ\ IhvcNAQEBBQADggEPADCCAQoCggEBAMptALdk7xKj9JmFSycxlaTV47oLv5Aabir17\ f1WseAcZ492Mx0wqcJMmT8rVAusyfqvrhodHu4GELGBySo4KChLEuoEOGTNw/wEMtM\ 6j1E9K7kig1iiuH9nf9oow7OUdix4+w7TWQWpwl1NekKdTtvLLtEGSjmG187CUqR6f\ NHYag+iVMV5Umc5VQadvAgva8qxOsPpDkN/E2df5gST7H5g3igaZtxUa3x7VreN3qJ\ P0+hYQiyM7KsgmdFAkKpHC6/k36H7SXtpzh0NbH5OJHifYsAP34WL+a6lAd0VM7UiI\ RMcLWA8HfmKL3p4bC+LFv5I0dvUUy1BTz1wHpRvVz8CAwEAAaNdMFswCQYDVR0TBAI\ wADAOBgNVHQ8BAf8EBAMCAaAwHQYDVR0OBBYEFCMimIgHf5c00sI9jZzeWoMLsR60M\ B8GA1UdIwQYMBaAFBbHC24DEnsUFLz/zmqB5cMCHo9OMA0GCSqGSIb3DQEBCwUAA4I\ CAQA1ehZTTBbes2DgGXwQugoV9PdOGMFEVT4dzrrluo/4exSfqLrNuY2NXVuNBKW4n\ DA5aD71Q/KUZ8Y8cV9qa8OBJQvQ0dd0qeHmeEYdDsj5YD4ECycKx9U1ZX5fi6tpSIX\ 6DsietpCnrw4aTgbEOvMeQcuYCTP30Vpt+mYEKBlR/E2Vcl2zUD+67gqppSaC1RceL\ /8Cy6ZXlPqwmS2zqK9UhYVRKlEww8xSh/9CR9MmIDc4pHtCpMawcn6Dmo+A+LcKi5v\ /NIwvSJTei+h1gvRhvEOPcf4VZJMHXquNrxkMsKpuu7g/AYH7wl2MBaNaxyNlXY5e5\ OjxslrbRCfDab11YaJEONcBnapl/+Ajr70uVFN09tDXyk0EHYf75NiRztgVKclna26\ zP5qRb0JSYNQJW2kIIBX6DhU7kt6RcauF2hJ+jLWOF2vsAS8PdEr7vnR1EGOrrcQ3V\ UgMscNsDqf50YMi2Inu1Kt2t+QSvYs61ON39aVpqR67nskdUWzFCVgWQVezM1ZagoO\ yNp7WjRYl8hJ0YVZ7TRtP8nJOkZ6s046YHVWxMuGdqZfd/AUFb9xzzXjGRuuZ1JmSf\ +VBOFEe2MaPMyMQBeIs3Othz6Fcy6Am5F6c3It31WYJwiCa/NdbMIvGy1xvAN5kzR/\ Y6hkoQljoSr1rVuszJ9dtvuTccA==", ) .expect("invalid base64"); // Issuer let issuer: Name = DirectoryName::new_common_name("contoso.local Authority"); check_serde!(issuer: Name in encoded[34..70]); // Validity let validity = Validity { not_before: UTCTime::new(2019, 10, 17, 17, 41, 28).unwrap().into(), not_after: UTCTime::new(2022, 10, 16, 17, 41, 28).unwrap().into(), }; check_serde!(validity: Validity in encoded[70..102]); // Subject let subject: Name = DirectoryName::new_common_name("test.contoso.local"); check_serde!(subject: Name in encoded[102..133]); // SubjectPublicKeyInfo let subject_public_key_info = SubjectPublicKeyInfo::new_rsa_key( IntegerAsn1::from(encoded[165..422].to_vec()), BigInt::from(65537).to_signed_bytes_be().into(), ); check_serde!(subject_public_key_info: SubjectPublicKeyInfo in encoded[133..427]); // Extensions let mut key_usage = KeyUsage::new(7); key_usage.set_digital_signature(true); key_usage.set_key_encipherment(true); let extensions = Extensions(vec![ Extension::new_basic_constraints(None, None).into_non_critical(), Extension::new_key_usage(key_usage), Extension::new_subject_key_identifier(&encoded[469..489]), Extension::new_authority_key_identifier(KeyIdentifier::from(encoded[502..522].to_vec()), None, None), ]); check_serde!(extensions: Extensions in encoded[429..522]); // SignatureAlgorithm let signature_algorithm = AlgorithmIdentifier::new_sha256_with_rsa_encryption(); check_serde!(signature_algorithm: AlgorithmIdentifier in encoded[522..537]); // TbsCertificate let tbs_certificate = TbsCertificate { version: ExplicitContextTag0(Version::V3), serial_number: BigInt::from(935548868).to_signed_bytes_be().into(), signature: signature_algorithm.clone(), issuer, validity, subject, subject_public_key_info, extensions: extensions.into(), }; check_serde!(tbs_certificate: TbsCertificate in encoded[4..522]); // Full certificate let certificate = Certificate { tbs_certificate, signature_algorithm, signature_value: BitString::with_bytes(&encoded[542..1054]).into(), }; check_serde!(certificate: Certificate in encoded); } #[test] fn key_id() { let encoded = general_purpose::STANDARD .decode( "MIIDPzCCAiegAwIBAgIBATANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\ MA8GA1UECgwIUG9sYXJTU0wxGTAXBgNVBAMMEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\ MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G\ A1UECgwIUG9sYXJTU0wxGjAYBgNVBAMMEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN\ BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/\ uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD\ d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf\ CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr\ lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w\ bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB\ o00wSzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQfdNY/KcF0dEU7BRIsPai9Q1kCpjAf\ BgNVHSMEGDAWgBS0WuSls97SUva51aaVD+s+vMf9/zANBgkqhkiG9w0BAQUFAAOC\ AQEAm9GKWy4Z6eS483GoR5omwx32meCStm/vFuW+nozRwqwTG5d2Etx4TPnz73s8\ fMtM1QB0QbfBDDHxfGymEsKwICmCkJszKE7c03j3mkddrrvN2eIYiL6358S3yHMj\ iLVCraRUoEm01k7iytjxrcKb//hxFvHoxD1tdMqbuvjMlTS86kJSrkUMDw68UzfL\ jvo3oVjiexfasjsICXFNoncjthKtS7v4zrsgXNPz92h58NgXnDtQU+Eb9tVA9kUs\ Ln/az3v5DdgrNoAO60zK1zYAmekLil7pgba/jBLPeAQ2fZVgFxttKv33nUnUBzKA\ Od8i323fM5dQS1qQpBjBc/5fPw==", ) .expect("invalid base64"); let cert: Certificate = picky_asn1_der::from_bytes(&encoded).expect("intermediate cert"); pretty_assertions::assert_eq!( hex::encode(cert.subject_key_identifier().unwrap()), "1f74d63f29c17474453b05122c3da8bd435902a6" ); pretty_assertions::assert_eq!( hex::encode(cert.authority_key_identifier().unwrap().key_identifier().unwrap()), "b45ae4a5b3ded252f6b9d5a6950feb3ebcc7fdff" ); } } picky-asn1-x509-0.10.0/src/certification_request.rs000064400000000000000000000202620072674642500201460ustar 00000000000000use crate::attribute::{Attribute, Attributes}; use crate::{AlgorithmIdentifier, Name, SubjectPublicKeyInfo}; use picky_asn1::tag::Tag; use picky_asn1::wrapper::{Asn1SequenceOf, BitStringAsn1}; use serde::{de, ser, Deserialize, Serialize}; /// [RFC 2986 #4](https://tools.ietf.org/html/rfc2986#section-4) /// /// ```not_rust /// CertificationRequestInfo ::= SEQUENCE { /// version INTEGER { v1(0) } (v1,...), /// subject Name, /// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, /// attributes [0] Attributes{{ CRIAttributes }} /// } /// ``` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct CertificationRequestInfo { pub version: u8, pub subject: Name, pub subject_public_key_info: SubjectPublicKeyInfo, pub attributes: OpenSslAttributes, } /// OpenSSL compatible CRI `attributes` type /// /// For some reason, openssl is producing an "implicit context tag" but with coding `0xA0` /// /// In [appendix A of the RFC](https://datatracker.ietf.org/doc/html/rfc2986#appendix-A) /// ```not_rust /// DEFINITIONS IMPLICIT TAGS ::= /// ``` /// states that context tags are implicit unless stated otherwise. /// This type implements a workaround to imitate this. #[derive(Clone, Debug, PartialEq)] pub struct OpenSslAttributes(pub Attributes); impl std::ops::Deref for OpenSslAttributes { type Target = Attributes; fn deref(&self) -> &Self::Target { &self.0 } } impl std::ops::DerefMut for OpenSslAttributes { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl ser::Serialize for OpenSslAttributes { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { let mut raw_der = picky_asn1_der::to_vec(&self.0).unwrap(); raw_der[0] = Tag::context_specific_constructed(0).inner(); picky_asn1_der::Asn1RawDer(raw_der).serialize(serializer) } } impl<'de> de::Deserialize<'de> for OpenSslAttributes { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { let mut raw_der = picky_asn1_der::Asn1RawDer::deserialize(deserializer)?.0; raw_der[0] = Tag::SEQUENCE.inner(); let attrs = picky_asn1_der::from_bytes(&raw_der).unwrap_or_default(); Ok(Self(attrs)) } } impl CertificationRequestInfo { pub fn new(subject: Name, subject_public_key_info: SubjectPublicKeyInfo) -> Self { // It shall be 0 for this version of the standard. Self { version: 0, subject, subject_public_key_info, attributes: OpenSslAttributes(Asn1SequenceOf(Vec::new())), } } pub fn with_attribute(mut self, attribute: Attribute) -> Self { self.attributes.0.push(attribute); self } pub fn add_attribute(&mut self, attribute: Attribute) { self.attributes.0.push(attribute); } } /// [RFC 2986 #4](https://tools.ietf.org/html/rfc2986#section-4) /// /// ```not_rust /// CertificationRequest ::= SEQUENCE { /// certificationRequestInfo CertificationRequestInfo, /// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, /// signature BIT STRING /// } /// ``` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct CertificationRequest { pub certification_request_info: CertificationRequestInfo, pub signature_algorithm: AlgorithmIdentifier, pub signature: BitStringAsn1, } #[cfg(test)] mod tests { use super::*; use crate::name::*; use crate::{DirectoryName, Extension, GeneralName}; use base64::{engine::general_purpose, Engine as _}; use picky_asn1::bit_string::BitString; use picky_asn1::restricted_string::{IA5String, PrintableString, Utf8String}; use picky_asn1::wrapper::IntegerAsn1; use std::str::FromStr; #[test] fn basic_csr() { let encoded = general_purpose::STANDARD .decode( "MIICYjCCAUoCAQAwHTEbMBkGA1UEAxMSdGVzdC5jb250b3NvLmxvY2FsMIIBIjAN\ BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAym0At2TvEqP0mYVLJzGVpNXjugu/\ kBpuKvXt/Vax4Bxnj3YzHTCpwkyZPytUC6zJ+q+uGh0e7gYQsYHJKjgoKEsS6gQ4\ ZM3D/AQy0zqPUT0ruSKDWKK4f2d/2ijDs5R2LHj7DtNZBanCXU16Qp1O28su0QZK\ OYbXzsJSpHp80dhqD6JUxXlSZzlVBp28CC9ryrE6w+kOQ38TZ1/mBJPsfmDeKBpm\ 3FRrfHtWt43eok/T6FhCLIzsqyCZ0UCQqkcLr+TfoftJe2nOHQ1sfk4keJ9iwA/f\ hYv5rqUB3RUztSIhExwtYDwd+YovenhsL4sW/kjR29RTLUFPPXAelG9XPwIDAQAB\ oAAwDQYJKoZIhvcNAQELBQADggEBAKrCf4sFDBFZQ6CPYdaxe3InMp7KFaueMIB8\ /YK73rJ+JGB6fQfltCCkToTE1y0Q3UqTlqHmaqdoh0KMWue6jCFvBat4/TUqUG7W\ tRLDP67eMulolcIzLqwTjR38DVJvnwrd2pey43q3UHBjlStxT/gI4ysQHn4qrzHB\ 6OK9O6ypqTtwXxnm3TJF9dctLwvbh7NZSaamSlxI0/ajKZOP9k1KZEOPtaiiMPe2\ yr+QvwY2ov66MRG5PPRZELQWBaPZOuFwmCsFOLXJMpvhoAgklBCFZmiQMgApGIC1\ FIDgjm2ZhQQIRMnTsAV6f7BclRTaUkc0sPl17YB9GfNfOm1oL7o=", ) .expect("invalid base64"); let certification_request_info = CertificationRequestInfo::new( DirectoryName::new_common_name(PrintableString::from_str("test.contoso.local").unwrap()), SubjectPublicKeyInfo::new_rsa_key( IntegerAsn1::from(encoded[74..331].to_vec()), IntegerAsn1::from(encoded[333..336].to_vec()), ), ); check_serde!(certification_request_info: CertificationRequestInfo in encoded[4..338]); let csr = CertificationRequest { certification_request_info, signature_algorithm: AlgorithmIdentifier::new_sha256_with_rsa_encryption(), signature: BitString::with_bytes(&encoded[358..614]).into(), }; check_serde!(csr: CertificationRequest in encoded); } #[test] fn csr_with_extensions_attribute() { let encoded = general_purpose::STANDARD .decode( "MIICjDCCAXQCAQAwIDELMAkGA1UEBhMCWFgxETAPBgNVBAMMCHNvbWV0ZXN0MIIB\ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNELh212N4optYS7pqbtvjyv\ +t4fQjX/pwB88BUCEjBgh+DJ49EBPQg9oObADTcBi3EeXu4M5y6f/dzIhovayJ/y\ 9j7Cj0Bw+VY+eRXywkVG/DqaiKG2mIQW+fho7/jhazhpeIxCzObPTwiQK7i96Vjq\ 9S+o4QQejE2SYLOhQ4/cgUaT7JBm4yab7cvhFjKYjVmoP6ioIcHb9Cmv25Lttuvk\ n64bDiPKz6BkutRpbMipQjSA8xKEgjgFG/nxBynA8PXnZIunhTNyhXrqRoAe6SXn\ ZLZLmwOkeU5WTewVVTXlmqaZTPwtb/9EjjoRnO3+Ulb5zT5wPULc79xuY16kzwID\ AQABoCcwJQYJKoZIhvcNAQkOMRgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJ\ KoZIhvcNAQELBQADggEBAIm9lOhZG3XY4CNJ5b18Qu/OfFi+T0tgxt4bTqINQ1Iz\ SQFrsnheBrzmasfFliz10N96cOmNka1UpWqK7N5/TfkJHX3zKYRpc2jEkrFun48B\ 3+bOJJPH48zmTGxBgU7iiorpaVt3CpgXNswhU3fpcT5gLy8Ys7DXC39Nn1lW0Lko\ cd6xK4oIJyoeiXyVBdn68gtPY6xjFxta67nyj39sSGhATxrDgxtLHEH2+HStywr0\ 4/osg9vP/OH5iFYOiEimK6ErYNg8rM1A/OTe5p8emA6y3o5dHG8lKYwevyUXMSLv\ 38CNeh0MS2KmyHz2085HlIIAXIu2xAUyWLsQik+eV6M=", ) .expect("invalid base64"); let extensions = vec![Extension::new_subject_alt_name(vec![GeneralName::DnsName( IA5String::from_string("localhost".into()).unwrap().into(), )]) .into_non_critical()]; let mut dn = DirectoryName::new(); dn.add_attr(NameAttr::CountryName, PrintableString::from_str("XX").unwrap()); dn.add_attr(NameAttr::CommonName, Utf8String::from_str("sometest").unwrap()); let certification_request_info = CertificationRequestInfo::new( dn, SubjectPublicKeyInfo::new_rsa_key( IntegerAsn1::from(encoded[77..334].to_vec()), IntegerAsn1::from(encoded[336..339].to_vec()), ), ) .with_attribute(Attribute::new_extension_request(extensions)); check_serde!(certification_request_info: CertificationRequestInfo in encoded[4..380]); let csr = CertificationRequest { certification_request_info, signature_algorithm: AlgorithmIdentifier::new_sha256_with_rsa_encryption(), signature: BitString::with_bytes(&encoded[400..656]).into(), }; check_serde!(csr: CertificationRequest in encoded); } } picky-asn1-x509-0.10.0/src/directory_string.rs000064400000000000000000000143150072674642500171470ustar 00000000000000use picky_asn1::restricted_string::{PrintableString, Utf8String}; use picky_asn1::tag::{Tag, TagPeeker}; use picky_asn1::wrapper::{BMPStringAsn1, PrintableStringAsn1}; use serde::{de, ser}; use std::borrow::Cow; use std::fmt; /// [RFC 5280 #4.1.2.4](https://tools.ietf.org/html/rfc5280#section-4.1.2.4) /// /// TeletexString, UniversalString and BmpString are not supported. /// /// ```not_rust /// DirectoryString ::= CHOICE { /// teletexString TeletexString (SIZE (1..MAX)), /// printableString PrintableString (SIZE (1..MAX)), /// universalString UniversalString (SIZE (1..MAX)), /// utf8String UTF8String (SIZE (1..MAX)), /// bmpString BMPString (SIZE (1..MAX)) } /// ``` #[derive(Debug, PartialEq, Eq, Clone)] pub enum DirectoryString { //TeletexString, PrintableString(PrintableStringAsn1), //UniversalString, Utf8String(String), BmpString(BMPStringAsn1), } impl fmt::Display for DirectoryString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_utf8_lossy()) } } impl DirectoryString { pub fn to_utf8_lossy(&self) -> Cow { match &self { DirectoryString::PrintableString(string) => String::from_utf8_lossy(string.as_bytes()), DirectoryString::Utf8String(string) => Cow::Borrowed(string.as_str()), DirectoryString::BmpString(string) => Cow::Owned(utf16_to_utf8_lossy(string.as_bytes())), } } pub fn as_bytes(&self) -> &[u8] { match &self { DirectoryString::PrintableString(string) => string.as_bytes(), DirectoryString::Utf8String(string) => string.as_bytes(), DirectoryString::BmpString(string) => string.as_bytes(), } } } impl From<&str> for DirectoryString { fn from(string: &str) -> Self { Self::Utf8String(string.to_owned()) } } impl From for DirectoryString { fn from(string: String) -> Self { Self::Utf8String(string) } } impl From for DirectoryString { fn from(string: PrintableString) -> Self { Self::PrintableString(string.into()) } } impl From for DirectoryString { fn from(string: Utf8String) -> Self { Self::Utf8String(String::from_utf8(string.into_bytes()).expect("Utf8String has the right charset")) } } impl From for DirectoryString { fn from(string: PrintableStringAsn1) -> Self { Self::PrintableString(string) } } impl From for DirectoryString { fn from(string: BMPStringAsn1) -> Self { Self::BmpString(string) } } impl From for String { fn from(ds: DirectoryString) -> Self { match ds { DirectoryString::PrintableString(string) => String::from_utf8_lossy(string.as_bytes()).into(), DirectoryString::Utf8String(string) => string, DirectoryString::BmpString(string) => utf16_to_utf8_lossy(string.as_bytes()), } } } impl ser::Serialize for DirectoryString { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { DirectoryString::PrintableString(string) => string.serialize(serializer), DirectoryString::Utf8String(string) => string.serialize(serializer), DirectoryString::BmpString(string) => string.serialize(serializer), } } } impl<'de> de::Deserialize<'de> for DirectoryString { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = DirectoryString; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded DirectoryString") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, DirectoryString, "choice tag"); match tag_peeker.next_tag { Tag::UTF8_STRING => Ok(DirectoryString::Utf8String(seq_next_element!( seq, DirectoryString, "Utf8String" ))), Tag::PRINTABLE_STRING => Ok(DirectoryString::PrintableString(seq_next_element!( seq, DirectoryString, "PrintableString" ))), Tag::BMP_STRING => Ok(DirectoryString::BmpString(seq_next_element!( seq, DirectoryString, "BmpString" ))), Tag::TELETEX_STRING => Err(serde_invalid_value!( DirectoryString, "TeletexString not supported", "a supported string type" )), Tag::VIDEOTEX_STRING => Err(serde_invalid_value!( DirectoryString, "VideotexString not supported", "a supported string type" )), Tag::IA5_STRING => Err(serde_invalid_value!( DirectoryString, "IA5String not supported", "a supported string type" )), _ => Err(serde_invalid_value!( DirectoryString, "unknown string type", "a known supported string type" )), } } } deserializer.deserialize_enum("DirectoryString", &["PrintableString", "Utf8String"], Visitor) } } fn utf16_to_utf8_lossy(data: &[u8]) -> String { debug_assert_eq!(data.len() % 2, 0); String::from_utf16_lossy( &data .chunks(2) .map(|c| u16::from_be_bytes(c.try_into().unwrap())) .collect::>(), ) } picky-asn1-x509-0.10.0/src/extension.rs000064400000000000000000000551160072674642500155750ustar 00000000000000use crate::{oids, GeneralName, GeneralNames}; use core::slice::{Iter, IterMut}; use picky_asn1::bit_string::BitString; use picky_asn1::wrapper::{ Asn1SequenceOf, BitStringAsn1, ExplicitContextTag1, ImplicitContextTag0, ImplicitContextTag2, IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1, OctetStringAsn1Container, Optional, }; use serde::{de, ser, Deserialize, Serialize}; use std::fmt; /// [RFC 5280 #4.1.2.9](https://tools.ietf.org/html/rfc5280#section-4.1.2.9) #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Extensions(pub Vec); /// [RFC 5280 #4.1.2.9](https://tools.ietf.org/html/rfc5280#section-4.1.2.9) #[derive(Debug, PartialEq, Clone)] pub struct Extension { extn_id: ObjectIdentifierAsn1, critical: Optional, extn_value: ExtensionValue, } impl Extension { pub fn extn_id(&self) -> &ObjectIdentifierAsn1 { &self.extn_id } pub fn critical(&self) -> bool { self.critical.0 } pub fn extn_value(&self) -> ExtensionView<'_> { ExtensionView::from(&self.extn_value) } pub fn into_critical(mut self) -> Self { self.critical = true.into(); self } pub fn into_non_critical(mut self) -> Self { self.critical = false.into(); self } pub fn set_critical(&mut self, critical: bool) { self.critical = critical.into(); } /// When present, conforming CAs SHOULD mark this extension as critical /// /// Default is critical. pub fn new_key_usage(key_usage: KeyUsage) -> Self { Self { extn_id: oids::key_usage().into(), critical: true.into(), extn_value: ExtensionValue::KeyUsage(key_usage.into()), } } /// Conforming CAs MUST mark this extension as non-critical /// /// Default is non-critical. pub fn new_subject_key_identifier>>(ski: V) -> Self { Self { extn_id: oids::subject_key_identifier().into(), critical: false.into(), extn_value: ExtensionValue::SubjectKeyIdentifier(OctetStringAsn1(ski.into()).into()), } } /// Conforming CAs MUST mark this extension as non-critical /// /// Default is critical. pub fn new_authority_key_identifier( key_identifier: KI, authority_cert_issuer: I, authority_cert_serial_number: SN, ) -> Self where KI: Into>, I: Into>, SN: Into>, { Self { extn_id: oids::authority_key_identifier().into(), critical: false.into(), extn_value: ExtensionValue::AuthorityKeyIdentifier( AuthorityKeyIdentifier { key_identifier: key_identifier.into().map(ImplicitContextTag0), authority_cert_issuer: authority_cert_issuer.into().map(ExplicitContextTag1), authority_cert_serial_number: authority_cert_serial_number.into().map(ImplicitContextTag2), } .into(), ), } } /// Marking this extension as critical is always acceptable. /// Check details here: /// You may change this value using `into_non_critical` or `set_critical` methods. /// /// Default is critical. pub fn new_basic_constraints>, PLC: Into>>( ca: CA, path_len_constraints: PLC, ) -> Self { Self { extn_id: oids::basic_constraints().into(), critical: true.into(), extn_value: ExtensionValue::BasicConstraints( BasicConstraints { ca: Optional(ca.into()), path_len_constraint: Optional(path_len_constraints.into()), } .into(), ), } } /// This extension MAY, at the option of the certificate issuer, be either critical or non-critical. /// Conforming CAs SHOULD NOT mark this extension as critical if the anyExtendedKeyUsage /// KeyPurposeId is present. /// /// Default is non-critical if anyExtendedKeyUsage is present, critical otherwise. pub fn new_extended_key_usage(extended_key_usage: EKU) -> Self where EKU: Into, { let eku = extended_key_usage.into(); Self { extn_id: oids::extended_key_usage().into(), critical: Optional(!eku.contains(oids::kp_any_extended_key_usage())), extn_value: ExtensionValue::ExtendedKeyUsage(eku.into()), } } /// If the subject field contains an empty sequence, then the issuing CA MUST include a /// subjectAltName extension that is marked as critical. When including /// the subjectAltName extension in a certificate that has a non-empty /// subject distinguished name, conforming CAs SHOULD mark the /// subjectAltName extension as non-critical. /// /// Default is critical. pub fn new_subject_alt_name>(name: N) -> Self { let name = name.into(); Self { extn_id: oids::subject_alternative_name().into(), critical: true.into(), extn_value: ExtensionValue::SubjectAltName(name.into()), } } /// Where present, conforming CAs SHOULD mark this extension as non-critical. /// /// Default is non-critical. pub fn new_issuer_alt_name>(name: N) -> Self { let name = name.into(); Self { extn_id: oids::issuer_alternative_name().into(), critical: false.into(), extn_value: ExtensionValue::IssuerAltName(name.into()), } } pub fn new_crl_number>>(number: N) -> Self { Self { extn_id: oids::crl_number().into(), critical: false.into(), extn_value: ExtensionValue::CrlNumber(number.into()), } } } impl ser::Serialize for Extension { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(3))?; seq.serialize_element(&self.extn_id)?; if self.critical != bool::default() { seq.serialize_element(&self.critical)?; } seq.serialize_element(&self.extn_value)?; seq.end() } } impl<'de> de::Deserialize<'de> for Extension { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Extension; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded algorithm identifier") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let id: ObjectIdentifierAsn1 = seq_next_element!(seq, Extension, "id"); let critical: Optional = seq_next_element!(seq, Extension, "critical"); let value = match Into::::into(&id.0).as_str() { oids::AUTHORITY_KEY_IDENTIFIER => ExtensionValue::AuthorityKeyIdentifier(seq_next_element!( seq, Extension, "AuthorityKeyIdentifier" )), oids::SUBJECT_KEY_IDENTIFIER => { ExtensionValue::SubjectKeyIdentifier(seq_next_element!(seq, Extension, "SubjectKeyIdentifier")) } oids::KEY_USAGE => ExtensionValue::KeyUsage(seq_next_element!(seq, Extension, "KeyUsage")), oids::SUBJECT_ALTERNATIVE_NAME => { ExtensionValue::SubjectAltName(seq_next_element!(seq, Extension, "SubjectAltName")) } oids::ISSUER_ALTERNATIVE_NAME => { ExtensionValue::IssuerAltName(seq_next_element!(seq, Extension, "IssuerAltName")) } oids::BASIC_CONSTRAINTS => { ExtensionValue::BasicConstraints(seq_next_element!(seq, Extension, "BasicConstraints")) } oids::EXTENDED_KEY_USAGE => { ExtensionValue::ExtendedKeyUsage(seq_next_element!(seq, Extension, "ExtendedKeyUsage")) } oids::CRL_NUMBER => ExtensionValue::CrlNumber(seq_next_element!(seq, Extension, "CrlNumber")), _ => ExtensionValue::Generic(seq_next_element!(seq, Extension, "Generic")), }; Ok(Extension { extn_id: id, critical, extn_value: value, }) } } deserializer.deserialize_seq(Visitor) } } /// A view on an Extension's value designed to be easier to match on #[derive(Debug, PartialEq, Eq, Clone)] pub enum ExtensionView<'a> { AuthorityKeyIdentifier(&'a AuthorityKeyIdentifier), SubjectKeyIdentifier(&'a SubjectKeyIdentifier), KeyUsage(&'a KeyUsage), SubjectAltName(super::name::GeneralNames), IssuerAltName(super::name::GeneralNames), BasicConstraints(&'a BasicConstraints), ExtendedKeyUsage(&'a ExtendedKeyUsage), Generic(&'a OctetStringAsn1), CrlNumber(&'a OctetStringAsn1Container), } impl<'a> From<&'a ExtensionValue> for ExtensionView<'a> { fn from(value: &'a ExtensionValue) -> Self { match value { ExtensionValue::AuthorityKeyIdentifier(OctetStringAsn1Container(val)) => Self::AuthorityKeyIdentifier(val), ExtensionValue::SubjectKeyIdentifier(OctetStringAsn1Container(val)) => Self::SubjectKeyIdentifier(val), ExtensionValue::KeyUsage(OctetStringAsn1Container(val)) => Self::KeyUsage(val), ExtensionValue::SubjectAltName(OctetStringAsn1Container(val)) => Self::SubjectAltName(val.clone()), ExtensionValue::IssuerAltName(OctetStringAsn1Container(val)) => Self::IssuerAltName(val.clone()), ExtensionValue::BasicConstraints(OctetStringAsn1Container(val)) => Self::BasicConstraints(val), ExtensionValue::ExtendedKeyUsage(OctetStringAsn1Container(val)) => Self::ExtendedKeyUsage(val), ExtensionValue::Generic(val) => Self::Generic(val), ExtensionValue::CrlNumber(val) => Self::CrlNumber(val), } } } #[derive(Debug, PartialEq, Clone)] enum ExtensionValue { AuthorityKeyIdentifier(OctetStringAsn1Container), SubjectKeyIdentifier(OctetStringAsn1Container), KeyUsage(OctetStringAsn1Container), //CertificatePolicies(OctetStringAsn1Container>), //PolicyMappings(OctetStringAsn1Container>), SubjectAltName(OctetStringAsn1Container), IssuerAltName(OctetStringAsn1Container), //SubjectDirectoryAttributes(OctetStringAsn1Container>), BasicConstraints(OctetStringAsn1Container), //NameConstraints(…), //PolicyConstraints(…), ExtendedKeyUsage(OctetStringAsn1Container), //CRLDistributionPoints(…), //InhibitAnyPolicy(…), //FreshestCRL(…), Generic(OctetStringAsn1), CrlNumber(OctetStringAsn1Container), } impl ser::Serialize for ExtensionValue { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match self { ExtensionValue::AuthorityKeyIdentifier(aki) => aki.serialize(serializer), ExtensionValue::SubjectKeyIdentifier(ski) => ski.serialize(serializer), ExtensionValue::KeyUsage(key_usage) => key_usage.serialize(serializer), ExtensionValue::SubjectAltName(san) => san.serialize(serializer), ExtensionValue::IssuerAltName(ian) => ian.serialize(serializer), ExtensionValue::BasicConstraints(basic_constraints) => basic_constraints.serialize(serializer), ExtensionValue::ExtendedKeyUsage(eku) => eku.serialize(serializer), ExtensionValue::Generic(octet_string) => octet_string.serialize(serializer), ExtensionValue::CrlNumber(integer) => integer.serialize(serializer), } } } /// [RFC 5280 #4.2.1.1](https://tools.ietf.org/html/rfc5280#section-4.2.1.1) /// /// ```not_rust /// AuthorityKeyIdentifier ::= SEQUENCE { /// keyIdentifier [0] KeyIdentifier OPTIONAL, /// authorityCertIssuer [1] GeneralNames OPTIONAL, /// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } /// ``` #[derive(Serialize, Debug, PartialEq, Eq, Clone)] pub struct AuthorityKeyIdentifier { key_identifier: Option>, authority_cert_issuer: Option>, authority_cert_serial_number: Option>, } impl AuthorityKeyIdentifier { pub fn key_identifier(&self) -> Option<&[u8]> { self.key_identifier.as_ref().map(|ki| (ki.0).0.as_slice()) } pub fn authority_cert_issuer(&self) -> Option { self.authority_cert_issuer.as_ref().map(|aci| aci.clone().0) } pub fn authority_cert_serial_number(&self) -> Option<&IntegerAsn1> { self.authority_cert_serial_number.as_ref().map(|acsn| &acsn.0) } } pub type KeyIdentifier = OctetStringAsn1; impl<'de> de::Deserialize<'de> for AuthorityKeyIdentifier { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = AuthorityKeyIdentifier; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded algorithm identifier") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { Ok(AuthorityKeyIdentifier { key_identifier: seq.next_element().unwrap_or(Some(None)).unwrap_or(None), authority_cert_issuer: seq.next_element().unwrap_or(Some(None)).unwrap_or(None), authority_cert_serial_number: seq.next_element().unwrap_or(Some(None)).unwrap_or(None), }) } } deserializer.deserialize_seq(Visitor) } } /// [RFC 5280 #4.2.1.2](https://tools.ietf.org/html/rfc5280#section-4.2.1.2) pub type SubjectKeyIdentifier = OctetStringAsn1; /// [RFC 5280 #4.2.1.3](https://tools.ietf.org/html/rfc5280#section-4.2.1.3) #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct KeyUsage(BitStringAsn1); impl Default for KeyUsage { fn default() -> Self { Self::new(9) } } macro_rules! bit_string_get_set { ($getter:ident , $setter:ident , $idx:literal) => { pub fn $getter(&self) -> bool { self.0.is_set($idx) } pub fn $setter(&mut self, val: bool) { if self.0.get_num_bits() <= $idx { self.0.set_num_bits($idx + 1) } self.0.set($idx, val); } }; ( $( $getter:ident , $setter:ident , $idx:literal ; )+ ) => { $( bit_string_get_set! { $getter, $setter, $idx } )+ }; } impl KeyUsage { pub fn new(num_bits: usize) -> Self { Self(BitString::with_len(num_bits).into()) } pub fn as_bytes(&self) -> &[u8] { self.0.payload_view() } bit_string_get_set! { digital_signature, set_digital_signature, 0; content_commitment, set_content_commitment, 1; key_encipherment, set_key_encipherment, 2; data_encipherment, set_data_encipherment, 3; key_agreement, set_key_agreement, 4; key_cert_sign, set_key_cert_sign, 5; crl_sign, set_crl_sign, 6; encipher_only, set_encipher_only, 7; decipher_only, set_decipher_only, 8; } } /// [RFC 5280 #4.2.1.6](https://tools.ietf.org/html/rfc5280#section-4.2.1.6) type SubjectAltName = GeneralNames; /// [RFC 5280 #4.2.1.7](https://tools.ietf.org/html/rfc5280#section-4.2.1.7) type IssuerAltName = GeneralNames; /// [RFC 5280 #4.2.1.9](https://tools.ietf.org/html/rfc5280#section-4.2.1.9) /// /// ```not_rust /// BasicConstraints ::= SEQUENCE { /// cA BOOLEAN DEFAULT FALSE, /// pathLenConstraint INTEGER (0..MAX) OPTIONAL } /// ``` #[derive(Serialize, Debug, PartialEq, Eq, Clone)] pub struct BasicConstraints { ca: Optional>, // default is false path_len_constraint: Optional>, } impl BasicConstraints { pub fn ca(&self) -> Option { self.ca.0 } pub fn pathlen(&self) -> Option { self.path_len_constraint.0 } } impl<'de> de::Deserialize<'de> for BasicConstraints { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = BasicConstraints; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded basic constraints extension") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { Ok(BasicConstraints { ca: Optional(seq.next_element().unwrap_or(Some(None)).unwrap_or(None)), path_len_constraint: Optional(seq.next_element().unwrap_or(Some(None)).unwrap_or(None)), }) } } deserializer.deserialize_seq(Visitor) } } /// [RFC 5280 #4.2.1.12](https://tools.ietf.org/html/rfc5280#section-4.2.1.12) #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct ExtendedKeyUsage(Asn1SequenceOf); impl> From> for ExtendedKeyUsage { fn from(purpose_oids: Vec) -> Self { ExtendedKeyUsage::new(purpose_oids) } } impl ExtendedKeyUsage { pub fn new>(purpose_oids: Vec) -> Self { Self( purpose_oids .into_iter() .map(|oid| oid.into()) .collect::>() .into(), ) } pub fn iter(&self) -> Iter { (self.0).0.iter() } pub fn iter_mut(&mut self) -> IterMut { (self.0).0.iter_mut() } pub fn contains>(&self, item: C) -> bool { (self.0).0.iter().any(|id| item.eq(&id.0)) } } #[cfg(test)] mod tests { use super::*; use crate::GeneralName; use base64::{engine::general_purpose, Engine as _}; use picky_asn1::restricted_string::IA5String; #[test] fn key_usage() { let encoded: [u8; 4] = [0x03, 0x02, 0x01, 0xA0]; let mut key_usage = KeyUsage::new(7); key_usage.set_digital_signature(true); key_usage.set_key_encipherment(true); assert_eq!(key_usage.as_bytes(), &[0xA0]); check_serde!(key_usage: KeyUsage in encoded); } #[test] fn eku_ku_bc_san_extensions() { let cert_der = general_purpose::STANDARD .decode( "MIIDIjCCAgoCAQAwIDELMAkGA1UEBhMCRlIxETAPBgNVBAMMCERyYXBlYXUhMIIB\ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5GqDEM7AfctJizsFEqtAvXd5\ Fl1GtyXDAnx68MUTuSL22t8aBZoCCi3/9AlS75uUqKggHnRuY2MRYPQaUzpE1F1a\ aZJNr6tXQy39FtdXrDq2zfwZdDmLW6sPmhvJrBO4yWjuG3wh1paPHy+rBHOjYt+9\ Pbl/FmDDjIzF8B2LZDuLdnS94Fs/JhogJL/XF4b6RLW60gEnYFjL+ebYdV/f3JYi\ ccQxY4imvbB2URlIO3t+aG9WMmhHZbbOi/HBdFG1fB7Hsa9Ek2FXshULzEDCJcMz\ n8HD96XbVBmlaz9nYIcZ83eCOhra67FfFy4pIE1M9saxYJg/OJrMHG12r89yUQID\ AQABoIG8MIG5BgkqhkiG9w0BCQ4xgaswgagwCQYDVR0TBAIwADALBgNVHQ8EBAMC\ BeAwJwYDVR0lBCAwHgYIKwYBBQUHAwIGCCsGAQUFBwMBBggrBgEFBQcDAzBlBgNV\ HREEXjBcghFkZXZlbC5leGFtcGxlLmNvbYIQaXB2Ni5leGFtcGxlLmNvbYIQaXB2\ NC5leGFtcGxlLmNvbYIQdGVzdC5leGFtcGxlLmNvbYIRcGFydHkuZXhhbXBsZS5j\ b20wDQYJKoZIhvcNAQELBQADggEBANaSDnpQUGcGypAaafJKAGME2Od8F4pvKjKF\ lREoWC7JFGIGE/pUrnvrE7qIFmCM3mnFWXEHvResFsdPmEWar+1jMdFinxBg0+J+\ Op0fxOwfHpxs++8hPsQgnDdL9pIjYFwmIAm64jnyq6wsYIl5CpkvBjGVRVddXkTb\ VDWhWaGncSdDur6++dp2OAGYTAv4XIHc0nhtcBoxeL4VhjcuksOdGg3JF02gW6Rc\ B1gipqD0jun8kPgWcQY22zhmP2HuPp0y58t9cu9FsnUcAFa//5pQA1LuaSFp65D4\ 92uaByS3lH18xzrkygzn1BeHRpo0fk4I9Rk8uy2QygCk43Pv6SU=", ) .expect("cert der"); let encoded = &cert_der[359..359 + 3 + 168]; let mut key_usage = KeyUsage::new(3); key_usage.set_digital_signature(true); key_usage.set_content_commitment(true); key_usage.set_key_encipherment(true); let extensions = Extensions(vec![ Extension::new_basic_constraints(None, None).into_non_critical(), Extension::new_key_usage(key_usage).into_non_critical(), Extension::new_extended_key_usage(vec![ oids::kp_client_auth(), oids::kp_server_auth(), oids::kp_code_signing(), ]) .into_non_critical(), Extension::new_subject_alt_name(vec![ GeneralName::DnsName(IA5String::from_string("devel.example.com".into()).unwrap().into()), GeneralName::DnsName(IA5String::from_string("ipv6.example.com".into()).unwrap().into()), GeneralName::DnsName(IA5String::from_string("ipv4.example.com".into()).unwrap().into()), GeneralName::DnsName(IA5String::from_string("test.example.com".into()).unwrap().into()), GeneralName::DnsName(IA5String::from_string("party.example.com".into()).unwrap().into()), ]) .into_non_critical(), ]); check_serde!(extensions: Extensions in encoded); } } picky-asn1-x509-0.10.0/src/lib.rs000064400000000000000000000014750072674642500143260ustar 00000000000000#[macro_use] mod macros; pub mod algorithm_identifier; pub mod attribute; pub mod attribute_type_and_value; pub mod certificate; pub mod certification_request; pub mod directory_string; pub mod extension; pub mod name; pub mod oids; #[cfg(feature = "pkcs7")] pub mod pkcs7; pub mod private_key_info; pub mod signature; pub mod subject_public_key_info; pub mod validity; pub mod version; pub use algorithm_identifier::*; pub use attribute::*; pub use attribute_type_and_value::*; pub use certificate::*; pub use certification_request::*; pub use directory_string::*; pub use extension::*; pub use name::*; #[cfg(feature = "pkcs7")] pub use pkcs7::*; pub use private_key_info::*; pub use subject_public_key_info::*; pub use validity::*; pub use version::*; // Re-export `oid` crate as we use it in crate public API pub use oid; picky-asn1-x509-0.10.0/src/macros.rs000064400000000000000000000046160072674642500150440ustar 00000000000000macro_rules! serde_invalid_value { ($typ:ident, $unexp:literal, $exp:literal) => {{ const _: Option<$typ> = None; de::Error::invalid_value( serde::de::Unexpected::Other(concat!("[", stringify!($typ), "] ", $unexp)), &$exp, ) }}; } macro_rules! seq_next_element { ($seq:ident, $typ:ident, $missing_elem:literal) => {{ const _: Option<$typ> = None; $seq.next_element()?.ok_or_else(|| { de::Error::invalid_value( serde::de::Unexpected::Other(concat!("[", stringify!($typ), "] ", $missing_elem, " is missing")), &concat!("valid ", $missing_elem), ) })? }}; ($seq:ident, $typ_hint:path, $typ:ident, $missing_elem:literal) => {{ const _: Option<$typ> = None; $seq.next_element::<$typ_hint>()?.ok_or_else(|| { de::Error::invalid_value( serde::de::Unexpected::Other(concat!("[", stringify!($typ), "] ", $missing_elem, " is missing")), &concat!("valid ", $missing_elem), ) })? }}; } #[cfg(test)] #[macro_use] mod tests { macro_rules! check_serde { ($item:ident: $type:ident in $encoded:ident[$range:expr]) => { let encoded = &$encoded[$range]; check_serde!($item: $type in encoded); }; ($item:ident: $type:ident in $encoded:ident) => { let encoded = &$encoded[..]; let encoded_base64 = general_purpose::STANDARD.encode(encoded); println!(concat!(stringify!($item), " check...")); let serialized = picky_asn1_der::to_vec(&$item).expect(concat!( "failed ", stringify!($item), " serialization" )); let serialized_base64 = general_purpose::STANDARD.encode(&serialized); pretty_assertions::assert_eq!( serialized_base64, encoded_base64, concat!("serialized ", stringify!($item), " doesn't match") ); let deserialized: $type = picky_asn1_der::from_bytes(encoded).expect(concat!( "failed ", stringify!($item), " deserialization" )); pretty_assertions::assert_eq!( deserialized, $item, concat!("deserialized ", stringify!($item), " doesn't match") ); }; } } picky-asn1-x509-0.10.0/src/name.rs000064400000000000000000000456400072674642500145020ustar 00000000000000use crate::{AttributeTypeAndValue, AttributeTypeAndValueParameters, DirectoryString}; use picky_asn1::tag::{Encoding, Tag, TagClass, TagPeeker}; use picky_asn1::wrapper::*; use serde::{de, ser, Deserialize, Serialize}; use std::fmt; #[derive(Clone, Debug, PartialEq, Eq)] pub enum NameAttr { CommonName, Surname, SerialNumber, CountryName, LocalityName, StateOrProvinceName, StreetName, OrganizationName, OrganizationalUnitName, GivenName, Phone, } /// [RFC 5280 #4.1.2.4](https://tools.ietf.org/html/rfc5280#section-4.1.2.4) /// /// ```not_rust /// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName /// ``` pub type RdnSequence = Asn1SequenceOf; /// [RFC 5280 #4.1.2.4](https://tools.ietf.org/html/rfc5280#section-4.1.2.4) /// /// ```not_rust /// RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue /// ``` pub type RelativeDistinguishedName = Asn1SetOf; /// [RFC 5280 #4.2.1.6](https://tools.ietf.org/html/rfc5280#section-4.2.1.6) /// /// ```not_rust /// DirectoryName ::= Name /// ``` pub type DirectoryName = Name; /// [RFC 5280 #4.1.2.4](https://tools.ietf.org/html/rfc5280#section-4.1.2.4) /// /// ```not_rust /// Name ::= CHOICE { -- only one possibility for now -- /// rdnSequence RDNSequence } /// ``` #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Name(pub RdnSequence); impl Default for Name { fn default() -> Self { Self::new() } } impl Name { pub fn new() -> Self { Self(Asn1SequenceOf(Vec::new())) } pub fn new_common_name>(name: S) -> Self { let mut dn = Self::default(); dn.add_attr(NameAttr::CommonName, name); dn } /// Find the first common name contained in this `Name` pub fn find_common_name(&self) -> Option<&DirectoryString> { for relative_distinguished_name in &((self.0).0) { for attr_ty_val in &relative_distinguished_name.0 { if let AttributeTypeAndValueParameters::CommonName(dir_string) = &attr_ty_val.value { return Some(dir_string); } } } None } pub fn add_attr>(&mut self, attr: NameAttr, value: S) { let ty_val = match attr { NameAttr::CommonName => AttributeTypeAndValue::new_common_name(value), NameAttr::Surname => AttributeTypeAndValue::new_surname(value), NameAttr::SerialNumber => AttributeTypeAndValue::new_serial_number(value), NameAttr::CountryName => AttributeTypeAndValue::new_country_name(value), NameAttr::LocalityName => AttributeTypeAndValue::new_locality_name(value), NameAttr::StateOrProvinceName => AttributeTypeAndValue::new_state_or_province_name(value), NameAttr::StreetName => AttributeTypeAndValue::new_street_name(value), NameAttr::OrganizationName => AttributeTypeAndValue::new_organization_name(value), NameAttr::OrganizationalUnitName => AttributeTypeAndValue::new_organizational_unit_name(value), NameAttr::GivenName => AttributeTypeAndValue::new_given_name(value), NameAttr::Phone => AttributeTypeAndValue::new_phone(value), }; let set_val = Asn1SetOf(vec![ty_val]); ((self.0).0).push(set_val); } /// Add an emailAddress attribute. /// NOTE: this attribute does not conform with the RFC 5280, email should be placed in SAN instead pub fn add_email>(&mut self, value: S) { let set_val = Asn1SetOf(vec![AttributeTypeAndValue::new_email_address(value)]); ((self.0).0).push(set_val); } } impl fmt::Display for Name { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { NamePrettyFormatter(self).fmt(f) } } pub struct NamePrettyFormatter<'a>(pub &'a Name); impl fmt::Display for NamePrettyFormatter<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut first = true; for name in &((self.0).0).0 { for attr in &name.0 { if first { first = false; } else { write!(f, ",")?; } match &attr.value { AttributeTypeAndValueParameters::CommonName(name) => { write!(f, "CN={}", name)?; } AttributeTypeAndValueParameters::Surname(name) => { write!(f, "SURNAME={}", name)?; } AttributeTypeAndValueParameters::SerialNumber(name) => { write!(f, "SN={}", name)?; } AttributeTypeAndValueParameters::CountryName(name) => { write!(f, "C={}", name)?; } AttributeTypeAndValueParameters::LocalityName(name) => { write!(f, "L={}", name)?; } AttributeTypeAndValueParameters::StateOrProvinceName(name) => { write!(f, "ST={}", name)?; } AttributeTypeAndValueParameters::StreetName(name) => { write!(f, "STREET NAME={}", name)?; } AttributeTypeAndValueParameters::OrganizationName(name) => { write!(f, "O={}", name)?; } AttributeTypeAndValueParameters::OrganizationalUnitName(name) => { write!(f, "OU={}", name)?; } AttributeTypeAndValueParameters::EmailAddress(name) => { write!(f, "EMAIL={}", String::from_utf8_lossy(name.as_bytes()))?; } AttributeTypeAndValueParameters::GivenName(name) => { write!(f, "GIVENNAME={}", name)?; } AttributeTypeAndValueParameters::Phone(name) => { write!(f, "PHONE={}", name)?; } AttributeTypeAndValueParameters::Custom(der) => { write!(f, "{}={:?}", Into::::into(&attr.ty.0), der)?; } } } } Ok(()) } } /// [RFC 5280 #4.2.1.6](https://tools.ietf.org/html/rfc5280#section-4.2.1.6) /// /// ```not_rust /// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName /// ``` pub type GeneralNames = Asn1SequenceOf; /// [RFC 5280 #4.2.1.6](https://tools.ietf.org/html/rfc5280#section-4.2.1.6) /// /// ```not_rust /// GeneralName ::= CHOICE { /// otherName [0] OtherName, /// rfc822Name [1] IA5String, /// dNSName [2] IA5String, /// x400Address [3] ORAddress, /// directoryName [4] Name, /// ediPartyName [5] EDIPartyName, /// uniformResourceIdentifier [6] IA5String, /// iPAddress [7] OCTET STRING, /// registeredID [8] OBJECT IDENTIFIER } /// ``` #[derive(Debug, PartialEq, Eq, Clone)] pub enum GeneralName { OtherName(OtherName), Rfc822Name(IA5StringAsn1), DnsName(IA5StringAsn1), //X400Address(ORAddress), DirectoryName(Name), EdiPartyName(EdiPartyName), Uri(IA5StringAsn1), IpAddress(OctetStringAsn1), RegisteredId(ObjectIdentifierAsn1), } impl GeneralName { pub fn new_edi_party_name(party_name: PN, name_assigner: Option) -> Self where PN: Into, NA: Into, { Self::EdiPartyName(EdiPartyName { name_assigner: Optional(name_assigner.map(Into::into).map(ImplicitContextTag0)), party_name: ImplicitContextTag1(party_name.into()), }) } } impl From for GeneralName { fn from(name: Name) -> Self { Self::DirectoryName(name) } } impl ser::Serialize for GeneralName { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { GeneralName::OtherName(name) => { let mut raw_der = picky_asn1_der::to_vec(name).map_err(ser::Error::custom)?; raw_der[0] = Tag::context_specific_constructed(0).inner(); picky_asn1_der::Asn1RawDer(raw_der).serialize(serializer) } GeneralName::Rfc822Name(name) => ImplicitContextTag1(name).serialize(serializer), GeneralName::DnsName(name) => ImplicitContextTag2(name).serialize(serializer), GeneralName::DirectoryName(name) => ImplicitContextTag4(name).serialize(serializer), GeneralName::EdiPartyName(name) => ImplicitContextTag5(name).serialize(serializer), GeneralName::Uri(name) => ImplicitContextTag6(name).serialize(serializer), GeneralName::IpAddress(name) => ImplicitContextTag7(name).serialize(serializer), GeneralName::RegisteredId(name) => ImplicitContextTag8(name).serialize(serializer), } } } impl<'de> de::Deserialize<'de> for GeneralName { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = GeneralName; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded GeneralName") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, DirectoryString, "choice tag"); match tag_peeker.next_tag.components() { (TagClass::ContextSpecific, Encoding::Primitive, 0) => Err(serde_invalid_value!( GeneralName, "Primitive encoding for OtherName not supported", "a supported choice" )), (TagClass::ContextSpecific, Encoding::Constructed, 0) => Ok(GeneralName::OtherName( seq_next_element!(seq, OtherName, GeneralName, "OtherName"), )), (TagClass::ContextSpecific, Encoding::Primitive, 1) => Ok(GeneralName::Rfc822Name( seq_next_element!(seq, ImplicitContextTag1, GeneralName, "RFC822Name").0, )), (TagClass::ContextSpecific, Encoding::Constructed, 1) => Ok(GeneralName::Rfc822Name( seq_next_element!(seq, ExplicitContextTag1, GeneralName, "RFC822Name").0, )), (TagClass::ContextSpecific, Encoding::Primitive, 2) => Ok(GeneralName::DnsName( seq_next_element!(seq, ImplicitContextTag2, GeneralName, "DNSName").0, )), (TagClass::ContextSpecific, Encoding::Constructed, 2) => Ok(GeneralName::DnsName( seq_next_element!(seq, ExplicitContextTag2, GeneralName, "DNSName").0, )), (TagClass::ContextSpecific, _, 3) => Err(serde_invalid_value!( GeneralName, "X400Address not supported", "a supported choice" )), (TagClass::ContextSpecific, Encoding::Primitive, 4) => Ok(GeneralName::DirectoryName( seq_next_element!(seq, ImplicitContextTag4, GeneralName, "DirectoryName").0, )), (TagClass::ContextSpecific, Encoding::Constructed, 4) => Ok(GeneralName::DirectoryName( seq_next_element!(seq, ExplicitContextTag4, GeneralName, "DirectoryName").0, )), (TagClass::ContextSpecific, Encoding::Primitive, 5) => Ok(GeneralName::EdiPartyName( seq_next_element!(seq, ImplicitContextTag5, GeneralName, "EDIPartyName").0, )), (TagClass::ContextSpecific, Encoding::Constructed, 5) => Ok(GeneralName::EdiPartyName( seq_next_element!(seq, ExplicitContextTag5, GeneralName, "EDIPartyName").0, )), (TagClass::ContextSpecific, Encoding::Primitive, 6) => Ok(GeneralName::Uri( seq_next_element!(seq, ImplicitContextTag6, GeneralName, "URI").0, )), (TagClass::ContextSpecific, Encoding::Constructed, 6) => Ok(GeneralName::Uri( seq_next_element!(seq, ExplicitContextTag6, GeneralName, "URI").0, )), (TagClass::ContextSpecific, Encoding::Primitive, 7) => Ok(GeneralName::IpAddress( seq_next_element!(seq, ImplicitContextTag7, GeneralName, "IpAddress").0, )), (TagClass::ContextSpecific, Encoding::Constructed, 7) => Ok(GeneralName::IpAddress( seq_next_element!(seq, ExplicitContextTag7, GeneralName, "IpAddress").0, )), (TagClass::ContextSpecific, Encoding::Primitive, 8) => Ok(GeneralName::RegisteredId( seq_next_element!( seq, ImplicitContextTag8, GeneralName, "RegisteredId" ) .0, )), (TagClass::ContextSpecific, Encoding::Constructed, 8) => Ok(GeneralName::RegisteredId( seq_next_element!( seq, ExplicitContextTag8, GeneralName, "RegisteredId" ) .0, )), _ => Err(serde_invalid_value!( GeneralName, "unknown choice value", "a supported GeneralName choice" )), } } } deserializer.deserialize_enum( "GeneralName", &[ "RFC822Name", "DNSName", "DirectoryName", "EDIPartyName", "URI", "IpAddress", "RegisteredId", ], Visitor, ) } } // OtherName ::= SEQUENCE { // type-id OBJECT IDENTIFIER, // value [0] EXPLICIT ANY DEFINED BY type-id} #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub struct OtherName { pub type_id: ObjectIdentifierAsn1, pub value: ExplicitContextTag0, } /// [RFC 5280 #4.2.1.6](https://tools.ietf.org/html/rfc5280#section-4.2.1.6) /// /// ```not_rust /// EDIPartyName ::= SEQUENCE { /// nameAssigner [0] DirectoryString OPTIONAL, /// partyName [1] DirectoryString } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct EdiPartyName { pub name_assigner: Optional>>, pub party_name: ImplicitContextTag1, } #[cfg(test)] mod tests { use super::*; use base64::{engine::general_purpose, Engine as _}; use oid::ObjectIdentifier; use picky_asn1::restricted_string::IA5String; use picky_asn1_der::Asn1RawDer; use std::str::FromStr; #[test] fn common_name() { #[rustfmt::skip] let encoded = [ 0x30, 0x1D, // sequence 0x31, 0x1B, // set 0x30, 0x19, // sequence 0x06, 0x03, // tag of oid 0x55, 0x04, 0x03, // oid of common name 0x0c, 0x12, // tag of utf-8 string 0x74, 0x65, 0x73, 0x74, 0x2E, 0x63, 0x6F, 0x6E, 0x74, 0x6F, 0x73, 0x6F, 0x2E, 0x6C, 0x6F, 0x63, 0x61, 0x6C, // utf8 string ]; let expected = Name::new_common_name("test.contoso.local"); check_serde!(expected: Name in encoded); } #[test] fn multiple_attributes() { #[rustfmt::skip] let encoded = [ 0x30, 0x52, // sequence, 0x52(82) bytes 0x31, 0x1B, // set 1 (common name), 0x1b(27) bytes 0x30, 0x19, // sequence, 0x19(25) bytes 0x06, 0x03, // oid tag 0x55, 0x04, 0x03, // oid of common name attribute 0x0c, 0x12, // tag of utf-8 string b't', b'e', b's', b't', b'.', b'c', b'o', b'n', b't', b'o', b's', b'o', b'.', b'l', b'o', b'c', b'a', b'l', 0x31, 0x10, // set 2 (locality) 0x30, 0x0E, // sequence 0x06, 0x03, //oid tag 0x55, 0x04, 0x07, // oid of locality attribute 0x0c, 0x07, // tag of utf-8 string b'U', b'n', b'k', b'n', b'o', b'w', b'n', // utf8 string data 0x31, 0x21, // set 3 (emailAddress) 0x30, 0x1F, // sequence 0x06, 0x09, // oid tag 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, // oid of emailAddress 0x16, 0x12, // tag of IA5String b's', b'o', b'm', b'e', b'@', b'c', b'o', b'n', b't', b'o', b's', b'o', b'.', b'l', b'o', b'c', b'a', b'l', // utf-8 string data ]; let mut expected = Name::new_common_name("test.contoso.local"); expected.add_attr(NameAttr::LocalityName, "Unknown"); let email = IA5StringAsn1(IA5String::from_str("some@contoso.local").unwrap()); expected.add_email(email); check_serde!(expected: Name in encoded); } #[test] fn general_name_dns() { #[rustfmt::skip] let encoded = [ 0x82, 0x11, 0x64, 0x65, 0x76, 0x65, 0x6C, 0x2E, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, ]; let expected = GeneralName::DnsName(IA5String::from_string("devel.example.com".into()).unwrap().into()); check_serde!(expected: GeneralName in encoded); } #[test] fn general_name_other_name() { let encoded = [ 160, 24, 6, 8, 43, 6, 1, 5, 5, 7, 8, 3, 160, 12, 48, 10, 12, 8, 65, 69, 45, 57, 52, 51, 52, 57, ]; let expected = GeneralName::OtherName(OtherName { type_id: ObjectIdentifierAsn1(ObjectIdentifier::try_from("1.3.6.1.5.5.7.8.3").unwrap()), value: ExplicitContextTag0::from(Asn1RawDer(vec![48, 10, 12, 8, 65, 69, 45, 57, 52, 51, 52, 57])), }); check_serde!(expected: GeneralName in encoded); } } picky-asn1-x509-0.10.0/src/oids.rs000064400000000000000000000271650072674642500145220ustar 00000000000000//! OIDs commonly used with X.509 certificates /// Unsafely marks a branch as unreachable. /// This won't panic if reached, however children will be sacrificed and dark magic performed. /// /// # Unsafety /// /// This is incredibly unsafe. /// You can already see Hades waving his hand at you from here. /// You shall not pass this bridge leading to insanity. Never. /// No one can tell you what would happen if you did. /// Only one thing is for sure: it leads to a land of desolation called UB. /// I mean, I'm literally creating infinity out of emptiness. /// If you don't care about your mental sanity, you can read the /// [nomicon on unchecked uninitialized memory](https://doc.rust-lang.org/nomicon/unchecked-uninit.html). unsafe fn unreachable() -> ! { std::hint::unreachable_unchecked() } macro_rules! define_oid { ($uppercase:ident => $lowercase:ident => $str_value:literal) => { pub const $uppercase: &'static str = $str_value; pub fn $lowercase() -> ::oid::ObjectIdentifier { use ::std::sync::Once; static mut OID: Option<::oid::ObjectIdentifier> = None; static INIT: Once = Once::new(); unsafe { INIT.call_once(|| { OID = Some($uppercase.try_into().unwrap()) }); if let Some(oid) = &OID { oid.clone() } else { unreachable() } } } }; ( $( $uppercase:ident => $lowercase:ident => $str_value:literal, )+ ) => { $( define_oid! { $uppercase => $lowercase => $str_value } )+ }; } define_oid! { // x9-57 DSA_WITH_SHA1 => dsa_with_sha1 => "1.2.840.10040.4.3", // x9-42 DIFFIE_HELLMAN => diffie_hellman => "1.2.840.10046.2.1", // ANSI-X962 EC_PUBLIC_KEY => ec_public_key => "1.2.840.10045.2.1", ECDSA_WITH_SHA256 => ecdsa_with_sha256 => "1.2.840.10045.4.3.2", ECDSA_WITH_SHA384 => ecdsa_with_sha384 => "1.2.840.10045.4.3.3", ECDSA_WITH_SHA512 => ecdsa_with_sha512 => "1.2.840.10045.4.3.4", SECP192R1 => secp192r1 => "1.2.840.10045.3.1.1", SECP256R1 => secp256r1 => "1.2.840.10045.3.1.7", // RSADSI RSA_ENCRYPTION => rsa_encryption => "1.2.840.113549.1.1.1", MD5_WITH_RSA_ENCRYPTHION => md5_with_rsa_encryption => "1.2.840.113549.1.1.4", SHA1_WITH_RSA_ENCRYPTION => sha1_with_rsa_encryption => "1.2.840.113549.1.1.5", SHA256_WITH_RSA_ENCRYPTION => sha256_with_rsa_encryption => "1.2.840.113549.1.1.11", SHA384_WITH_RSA_ENCRYPTION => sha384_with_rsa_encryption => "1.2.840.113549.1.1.12", SHA512_WITH_RSA_ENCRYPTION => sha512_with_rsa_encryption => "1.2.840.113549.1.1.13", SHA224_WITH_RSA_ENCRYPTION => sha224_with_rsa_encryption => "1.2.840.113549.1.1.14", RSASSA_PSS => rsassa_pss => "1.2.840.113549.1.1.10", EMAIL_ADDRESS => email_address => "1.2.840.113549.1.9.1", // deprecated EXTENSION_REQ => extension_request => "1.2.840.113549.1.9.14", // pkcs7 PKCS7 => pkcs7 => "1.2.840.113549.1.7.1", SIGNED_DATA => signed_data => "1.2.840.113549.1.7.2", CONTENT_TYPE => content_type => "1.2.840.113549.1.9.3", MESSAGE_DIGEST => message_digest => "1.2.840.113549.1.9.4", // NIST DSA_WITH_SHA224 => dsa_with_sha224 => "2.16.840.1.101.3.4.3.1", DSA_WITH_SHA256 => dsa_with_sha256 => "2.16.840.1.101.3.4.3.2", DSA_WITH_SHA384 => dsa_with_sha384 => "2.16.840.1.101.3.4.3.3", DSA_WITH_SHA512 => dsa_with_sha512 => "2.16.840.1.101.3.4.3.4", ID_ECDSA_WITH_SHA3_256 => id_ecdsa_with_sha3_256 => "2.16.840.1.101.3.4.3.10", ID_RSASSA_PKCS1_V1_5_WITH_SHA3_224 => id_rsassa_pkcs1_v1_5_with_sha3_224 => "2.16.840.1.101.3.4.3.13", ID_RSASSA_PKCS1_V1_5_WITH_SHA3_256 => id_rsassa_pkcs1_v1_5_with_sha3_256 => "2.16.840.1.101.3.4.3.14", ID_RSASSA_PKCS1_V1_5_WITH_SHA3_384 => id_rsassa_pkcs1_v1_5_with_sha3_384 => "2.16.840.1.101.3.4.3.15", ID_RSASSA_PKCS1_V1_5_WITH_SHA3_512 => id_rsassa_pkcs1_v1_5_with_sha3_512 => "2.16.840.1.101.3.4.3.16", // Certicom Object Identifiers SECP384R1 => secp384r1 => "1.3.132.0.34", SECT163K1 => sect163k1 => "1.3.132.0.1", SECT163R2 => sect163r2 => "1.3.132.0.15", SECP224R1 => secp224r1 => "1.3.132.0.33", SECT233K1 => sect233k1 => "1.3.132.0.26", SECT233R1 => sect233r1 => "1.3.132.0.27", SECT283K1 => sect283k1 => "1.3.132.0.16", SECT283R1 => sect283r1 => "1.3.132.0.17", SECT409K1 => sect409k1 => "1.3.132.0.36", SECT409R1 => sect409r1 => "1.3.132.0.37", SECP521R1 => secp521r1 => "1.3.132.0.35", SECT571K1 => sect571k1 => "1.3.132.0.38", SECT571R1 => sect571r1 => "1.3.132.0.39", // RFC 8410 X25519 => x25519 => "1.3.101.110", X448 => x448 => "1.3.101.111", ED25519 => ed25519 => "1.3.101.112", ED448 => ed448 => "1.3.101.113", // Extended key purpose OIDS KP_SERVER_AUTH => kp_server_auth => "1.3.6.1.5.5.7.3.1", KP_CLIENT_AUTH => kp_client_auth => "1.3.6.1.5.5.7.3.2", KP_CODE_SIGNING => kp_code_signing => "1.3.6.1.5.5.7.3.3", KP_EMAIL_PROTECTION => kp_email_protection => "1.3.6.1.5.5.7.3.4", KP_IPSEC_END_SYSTEM => kp_ipsec_end_system => "1.3.6.1.5.5.7.3.5", KP_IPSPEC_TUNNEL => kp_ipsec_tunnel => "1.3.6.1.5.5.7.3.6", KP_IPSEC_USER => kp_ipsec_user => "1.3.6.1.5.5.7.3.7", KP_TIME_STAMPING => kp_time_stamping => "1.3.6.1.5.5.7.3.8", KP_OCSP_SIGNING => kp_ocsp_signing => "1.3.6.1.5.5.7.3.9", KP_ANY_EXTENDED_KEY_USAGE => kp_any_extended_key_usage => "2.5.29.37.0", KP_LIFETIME_SIGNING => kp_lifetime_signing => "1.3.6.1.4.1.311.10.3.13", // attribute types AT_COMMON_NAME => at_common_name => "2.5.4.3", AT_SURNAME => at_surname => "2.5.4.4", AT_SERIAL_NUMBER => at_serial_number => "2.5.4.5", AT_COUNTRY_NAME => at_country_name => "2.5.4.6", AT_LOCALITY_NAME => at_locality_name => "2.5.4.7", AT_STATE_OR_PROVINCE_NAME => at_state_or_province_name => "2.5.4.8", AT_STREET_NAME => at_street_name => "2.5.4.9", AT_ORGANIZATION_NAME => at_organization_name => "2.5.4.10", AT_ORGANIZATIONAL_UNIT_NAME => at_organizational_unit_name => "2.5.4.11", AT_GIVENNAME => at_given_name => "2.5.4.42", AT_PHONE => at_phone => "2.5.4.20", // certificate extensions SUBJECT_KEY_IDENTIFIER => subject_key_identifier => "2.5.29.14", KEY_USAGE => key_usage => "2.5.29.15", SUBJECT_ALTERNATIVE_NAME => subject_alternative_name => "2.5.29.17", ISSUER_ALTERNATIVE_NAME => issuer_alternative_name => "2.5.29.18", BASIC_CONSTRAINTS => basic_constraints => "2.5.29.19", CRL_NUMBER => crl_number => "2.5.29.20", AUTHORITY_KEY_IDENTIFIER => authority_key_identifier => "2.5.29.35", EXTENDED_KEY_USAGE => extended_key_usage => "2.5.29.37", // aes // aes-128 AES128_ECB => aes128_ecb => "2.16.840.1.101.3.4.1.1", AES128_CBC => aes128_cbc => "2.16.840.1.101.3.4.1.2", AES128_OFB => aes128_ofb => "2.16.840.1.101.3.4.1.3", AES128_CFB => aes128_cfb => "2.16.840.1.101.3.4.1.4", AES128_WRAP => aes128_wrap => "2.16.840.1.101.3.4.1.5", AES128_GCM => aes128_gcm => "2.16.840.1.101.3.4.1.6", AES128_CCM => aes128_ccm => "2.16.840.1.101.3.4.1.7", AES128_WRAP_PAD => aes128_wrap_pad => "2.16.840.1.101.3.4.1.8", // aes-192 AES192_ECB => aes192_ecb => "2.16.840.1.101.3.4.1.21", AES192_CBC => aes192_cbc => "2.16.840.1.101.3.4.1.22", AES192_OFB => aes192_ofb => "2.16.840.1.101.3.4.1.23", AES192_CFB => aes192_cfb => "2.16.840.1.101.3.4.1.24", AES192_WRAP => aes192_wrap => "2.16.840.1.101.3.4.1.25", AES192_GCM => aes192_gcm => "2.16.840.1.101.3.4.1.26", AES192_CCM => aes192_ccm => "2.16.840.1.101.3.4.1.27", AES192_WRAP_PAD => aes192_wrap_pad => "2.16.840.1.101.3.4.1.28", // aes-256 AES256_ECB => aes256_ecb => "2.16.840.1.101.3.4.1.41", AES256_CBC => aes256_cbc => "2.16.840.1.101.3.4.1.42", AES256_OFB => aes256_ofb => "2.16.840.1.101.3.4.1.43", AES256_CFB => aes256_cfb => "2.16.840.1.101.3.4.1.44", AES256_WRAP => aes256_wrap => "2.16.840.1.101.3.4.1.45", AES256_GCM => aes256_gcm => "2.16.840.1.101.3.4.1.46", AES256_CCM => aes256_ccm => "2.16.840.1.101.3.4.1.47", AES256_WRAP_PAD => aes256_wrap_pad => "2.16.840.1.101.3.4.1.48", // hash algorithm MD5 => md5 => "1.2.840.113549.2.5", SHA1 => sha1 => "1.3.14.3.2.26", SHA256 => sha256 => "2.16.840.1.101.3.4.2.1", SHA384 => sha384 => "2.16.840.1.101.3.4.2.2", SHA512 => sha512 => "2.16.840.1.101.3.4.2.3", SHA224 => sha224 => "2.16.840.1.101.3.4.2.4", SHA512_224 => sha512_224 => "2.16.840.1.101.3.4.2.5", SHA512_256 => sha512_256 => "2.16.840.1.101.3.4.2.6", SHA3_224 => sha3_224 => "2.16.840.1.101.3.4.2.7", SHA3_256 => sha3_256 => "2.16.840.1.101.3.4.2.8", SHA3_384 => sha3_384 => "2.16.840.1.101.3.4.2.9", SHA3_512 => sha3_512 => "2.16.840.1.101.3.4.2.10", SHAKE128 => shake128 => "2.16.840.1.101.3.4.2.11", SHAKE256 => shake256 => "2.16.840.1.101.3.4.2.12", // authenticode SIGNING_TIME => signing_time => "1.2.840.113549.1.9.5", COUNTER_SIGN => counter_sign => "1.2.840.113549.1.9.6", SPC_INDIRECT_DATA_OBJID => spc_indirect_data_objid => "1.3.6.1.4.1.311.2.1.4", SPC_STATEMENT_TYPE => spc_statement_type => "1.3.6.1.4.1.311.2.1.11", SPC_SP_OPUS_INFO_OBJID => spc_sp_opus_info_objid => "1.3.6.1.4.1.311.2.1.12", SPC_PE_IMAGE_DATAOBJ => spc_pe_image_dataobj => "1.3.6.1.4.1.311.2.1.15", SPC_SIPINFO_OBJID => spc_sip_info_objid => "1.3.6.1.4.1.311.2.1.30", TIMESTAMP_REQUEST => timestamp_request => "1.3.6.1.4.1.311.3.2.1", MS_COUNTER_SIGN => ms_counter_signature => "1.3.6.1.4.1.311.3.3.1", // CTL CERT_TRUST_LIST => cert_trust_list => "1.3.6.1.4.1.311.10.1", ROOT_LIST_SIGNER => root_list_signer => "1.3.6.1.4.1.311.10.3.9", CERT_ENHKEY_USAGE_PROP_ID => cert_enhkey_usage_prop_id => "1.3.6.1.4.1.311.10.11.9", CERT_FRIENDLY_NAME_PROP_ID => cert_friendly_name_prop_id => "1.3.6.1.4.1.311.10.11.11", CERT_KEY_IDENTIFIER_PROP_ID => cert_key_identifier_prop_id => "1.3.6.1.4.1.311.10.11.20", CERT_SUBJECT_NAME_MD5_HASH_PROP_ID => cert_subject_name_md5_hash_prop_id => "1.3.6.1.4.1.311.10.11.29", CERT_ROOT_PROGRAM_CERT_POLICIES_PROP_ID => cert_root_program_cert_policies_prop_id => "1.3.6.1.4.1.311.10.11.83", CERT_AUTH_ROOT_SHA256_HASH_PROP_ID => cert_auto_root_sha256_hash_prop_id => "1.3.6.1.4.1.311.10.11.98", CERT_DISALLOWED_FILETIME_PROP_ID => cert_disallowed_filetime_prop_id => "1.3.6.1.4.1.311.10.11.104", CERT_ROOT_PROGRAM_CHAIN_POLICIES_PROP_ID => cert_root_program_chain_policies_prop_id => "1.3.6.1.4.1.311.10.11.105", DISALLOWED_ENHKEY_USAGE => disallowed_enhkey_usage => "1.3.6.1.4.1.311.10.11.122", UNKNOWN_RESERVED_PROP_ID_126 => unknown_reserved_prop_id_126 => "1.3.6.1.4.1.311.10.11.126", UNKNOWN_RESERVED_PROP_ID_127 => unknown_reserved_prop_id_127 => "1.3.6.1.4.1.311.10.11.127", AUTO_UPDATE_END_REVOCATION => auto_update_end_revocation => "1.3.6.1.4.1.311.60.3.2", // RSA Mask Generator Function 1 ID_MGF1 => id_mgf1 => "1.2.840.113549.1.1.8", // NLA protocols KRB5 => krb5 => "1.2.840.113554.1.2.2", MS_KRB5 => ms_krb5 => "1.2.840.48018.1.2.2", KRB5_USER_TO_USER => krb5_user_to_user => "1.2.840.113554.1.2.2.3", NTLM_SSP => ntlm_ssp => "1.3.6.1.4.1.311.2.2.10", NEGOEX => negoex => "1.3.6.1.4.1.311.2.2.30", SPNEGO => spnego => "1.3.6.1.5.5.2", // [SPNEGO](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/211417c4-11ef-46c0-a8fb-f178a51c2088) // The OID assigned for PKU2U is (1.3.6.1.5.2.7) GSS_PKU2U => gss_pku2u => "1.3.6.1.5.2.7", // http://oid-info.com/get/1.3.6.1.5.2.3.1 // id-pkinit-authData PKINIT_AUTH_DATA => pkinit_auth_data => "1.3.6.1.5.2.3.1", // http://oid-info.com/get/1.3.6.1.5.2.3.2 // id-pkinit-DHKeyData PKINIT_DH_KEY_DATA => kpinit_dh_key_data => "1.3.6.1.5.2.3.2", } picky-asn1-x509-0.10.0/src/pkcs7/cmsversion.rs000064400000000000000000000037030072674642500167730ustar 00000000000000use serde::{de, ser, Deserialize, Serialize}; use std::fmt; /// [RFC 5682 #10.2.5](https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.5) /// ``` not_rust /// CmsVersion ::= INTEGER /// { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) } /// ``` #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[repr(u8)] pub enum CmsVersion { V0 = 0x00, V1 = 0x01, V2 = 0x02, V3 = 0x03, V4 = 0x04, V5 = 0x05, } impl CmsVersion { pub fn from_u8(v: u8) -> Option { match v { 0x00 => Some(Self::V0), 0x01 => Some(Self::V1), 0x02 => Some(Self::V2), 0x03 => Some(Self::V3), 0x04 => Some(Self::V4), 0x05 => Some(Self::V5), _ => None, } } } impl Serialize for CmsVersion { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { serializer.serialize_u8(*self as u8) } } impl<'de> Deserialize<'de> for CmsVersion { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = CmsVersion; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a valid cms version number") } fn visit_u8(self, v: u8) -> Result where E: de::Error, { let cms_version = CmsVersion::from_u8(v).ok_or_else(|| { E::invalid_value( de::Unexpected::Other("invalid cms version number"), &"a valid integer representing a supported cms version number (0, 1, 2, 3, 4 or 5)", ) })?; Ok(cms_version) } } deserializer.deserialize_u8(Visitor) } } picky-asn1-x509-0.10.0/src/pkcs7/content_info.rs000064400000000000000000000510770072674642500172770ustar 00000000000000use oid::ObjectIdentifier; use picky_asn1::bit_string::BitString; use picky_asn1::restricted_string::{BMPString, CharSetError}; use picky_asn1::tag::{TagClass, TagPeeker}; use picky_asn1::wrapper::{ BMPStringAsn1, BitStringAsn1, ExplicitContextTag0, ExplicitContextTag1, ExplicitContextTag2, IA5StringAsn1, ImplicitContextTag0, ImplicitContextTag1, IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1, Optional, }; use serde::{de, ser, Deserialize, Serialize}; use widestring::U16String; #[cfg(feature = "ctl")] use super::ctl::Ctl; use crate::{oids, DigestInfo}; /// ``` not_rust /// [RFC 5652 #5.2](https://datatracker.ietf.org/doc/html/rfc5652#section-5.2) /// EncapsulatedContentInfo ::= SEQUENCE { /// eContentType ContentType, /// eContent [0] EXPLICIT OCTET STRING OPTIONAL } /// /// ContentType ::= OBJECT IDENTIFIER /// ``` #[derive(Debug, PartialEq, Eq, Clone)] pub enum ContentValue { SpcIndirectDataContent(SpcIndirectDataContent), OctetString(OctetStringAsn1), Data(OctetStringAsn1), #[cfg(feature = "ctl")] CertificateTrustList(Ctl), } impl Serialize for ContentValue { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { ContentValue::SpcIndirectDataContent(spc_indirect_data_content) => { spc_indirect_data_content.serialize(serializer) } ContentValue::OctetString(octet_string) => octet_string.serialize(serializer), ContentValue::Data(octet_string) => octet_string.serialize(serializer), #[cfg(feature = "ctl")] ContentValue::CertificateTrustList(ctl) => ctl.serialize(serializer), } } } #[derive(Serialize, Debug, PartialEq, Eq, Clone)] pub struct EncapsulatedContentInfo { pub content_type: ObjectIdentifierAsn1, #[serde(skip_serializing_if = "Option::is_none")] pub content: Option>, } impl EncapsulatedContentInfo { pub fn new_pkcs7_data(data: Option>) -> Self { Self { content_type: oids::pkcs7().into(), content: data.map(|data| ContentValue::Data(data.into()).into()), } } pub fn new(oid: ObjectIdentifier, data: Option>) -> Self { Self { content_type: ObjectIdentifierAsn1::from(oid), content: data.map(|data| ContentValue::Data(data.into()).into()), } } } impl<'de> de::Deserialize<'de> for EncapsulatedContentInfo { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = EncapsulatedContentInfo; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded ContentInfo") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let oid: ObjectIdentifierAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; let value = match Into::::into(&oid.0).as_str() { oids::SPC_INDIRECT_DATA_OBJID => Some( ContentValue::SpcIndirectDataContent( seq_next_element!( seq, ExplicitContextTag0, EncapsulatedContentInfo, "SpcIndirectDataContent" ) .0, ) .into(), ), oids::PKCS7 => seq .next_element::>()? .map(|value| ExplicitContextTag0(ContentValue::Data(value.0))), #[cfg(feature = "ctl")] oids::CERT_TRUST_LIST => Some( ContentValue::CertificateTrustList( seq_next_element!( seq, ExplicitContextTag0, EncapsulatedContentInfo, "CertificateTrustList" ) .0, ) .into(), ), _ => Some(ExplicitContextTag0::from(ContentValue::OctetString( seq_next_element!( seq, ExplicitContextTag0, EncapsulatedContentInfo, "OctetStringAsn1" ) .0, ))), }; Ok(EncapsulatedContentInfo { content_type: oid, content: value, }) } } deserializer.deserialize_seq(Visitor) } } // https://github.com/sassoftware/relic/blob/master/lib/authenticode/structs.go#L46 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct SpcSipInfo { pub version: IntegerAsn1, pub uuid: OctetStringAsn1, reserved1: IntegerAsn1, reserved2: IntegerAsn1, reserved3: IntegerAsn1, reserved4: IntegerAsn1, reserved5: IntegerAsn1, } /// [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcIndirectDataContent ::= SEQUENCE { /// data SpcAttributeTypeAndOptionalValue, /// messageDigest DigestInfo /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct SpcIndirectDataContent { pub data: SpcAttributeAndOptionalValue, pub message_digest: DigestInfo, } // [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcAttributeTypeAndOptionalValue ::= SEQUENCE { /// type ObjectID, /// value [0] EXPLICIT ANY OPTIONAL /// } /// ``` #[derive(Debug, PartialEq, Eq, Clone)] pub enum SpcAttributeAndOptionalValueValue { SpcPeImageData(SpcPeImageData), SpcSipInfo(SpcSipInfo), } impl Serialize for SpcAttributeAndOptionalValueValue { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { SpcAttributeAndOptionalValueValue::SpcPeImageData(spc_pe_image_data) => { spc_pe_image_data.serialize(serializer) } SpcAttributeAndOptionalValueValue::SpcSipInfo(spc_sip_info) => spc_sip_info.serialize(serializer), } } } #[derive(Serialize, Debug, PartialEq, Eq, Clone)] pub struct SpcAttributeAndOptionalValue { pub ty: ObjectIdentifierAsn1, pub value: SpcAttributeAndOptionalValueValue, } impl<'de> de::Deserialize<'de> for SpcAttributeAndOptionalValue { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SpcAttributeAndOptionalValue; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SpcAttributeAndOptionalValue") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let oid: ObjectIdentifierAsn1 = seq_next_element!(seq, SpcAttributeAndOptionalValue, "type oid"); let value = match Into::::into(&oid.0).as_str() { oids::SPC_PE_IMAGE_DATAOBJ => SpcAttributeAndOptionalValueValue::SpcPeImageData(seq_next_element!( seq, SpcPeImageData, SpcAttributeAndOptionalValue, "a SpcPeImageData object" )), oids::SPC_SIPINFO_OBJID => SpcAttributeAndOptionalValueValue::SpcSipInfo(seq_next_element!( seq, SpcSipInfo, "a SpcSipInfo object" )), _ => { return Err(serde_invalid_value!( SpcAttributeAndOptionalValue, "unknown oid type", "a SPC_PE_IMAGE_DATAOBJ oid" )); } }; Ok(SpcAttributeAndOptionalValue { ty: oid, value }) } } deserializer.deserialize_seq(Visitor) } } /// [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcPeImageData ::= SEQUENCE { /// flags SpcPeImageFlags DEFAULT { includeResources }, /// file SpcLink /// } /// ``` #[derive(Serialize, Debug, PartialEq, Eq, Clone)] pub struct SpcPeImageData { pub flags: SpcPeImageFlags, pub file: ExplicitContextTag0, // According to Authenticode_PE.docx, there is no ExplicitContextTag0, but otherwise created Authenticode signature won't be valid } impl<'de> de::Deserialize<'de> for SpcPeImageData { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SpcPeImageData; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SpcPeImageData") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { Ok(SpcPeImageData { flags: seq.next_element()?.unwrap_or_default(), file: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?, }) } } deserializer.deserialize_seq(Visitor) } } /// [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcPeImageFlags ::= BIT STRING { /// includeResources (0), /// includeDebugInfo (1), /// includeImportAddressTable (2) /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct SpcPeImageFlags(pub BitStringAsn1); impl Default for SpcPeImageFlags { fn default() -> Self { let mut flags = BitString::with_len(3); flags.set(0, true); // includeResources flags.set(1, false); // includeDebugInfo flags.set(2, false); // includeImportAddressTable Self(flags.into()) } } /// [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcLink ::= CHOICE { /// url [0] IMPLICIT IA5STRING, /// moniker [1] IMPLICIT SpcSerializedObject, /// file [2] EXPLICIT SpcString /// } /// ``` #[derive(Debug, PartialEq, Eq, Clone)] pub enum SpcLink { Url(Url), Moniker(Moniker), File(File), } impl Serialize for SpcLink { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { SpcLink::Url(url) => url.0.serialize(serializer), SpcLink::Moniker(moniker) => moniker.0.serialize(serializer), SpcLink::File(file) => file.0.serialize(serializer), } } } impl<'de> Deserialize<'de> for SpcLink { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SpcLink; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SpcLink") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, SpcLink, "choice tag"); let spc_link = match tag_peeker.next_tag.class_and_number() { (TagClass::ContextSpecific, 0) => SpcLink::Url(Url(seq_next_element!( seq, Optional>, SpcLink, "Url" ))), (TagClass::ContextSpecific, 1) => SpcLink::Moniker(Moniker(seq_next_element!( seq, Optional>, SpcLink, "Moniker" ))), (TagClass::ContextSpecific, 2) => SpcLink::File(File(seq_next_element!( seq, ExplicitContextTag2, SpcLink, "File" ))), _ => { return Err(serde_invalid_value!( SpcString, "unknown choice value", "a supported SpcString choice" )) } }; Ok(spc_link) } } deserializer.deserialize_enum("SpcLink", &["Url", "Moniker", "File"], Visitor) } } impl Default for SpcLink { fn default() -> Self { SpcLink::File(File::default()) } } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Url(pub Optional>); #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Moniker(pub Optional>); #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct File(pub ExplicitContextTag2); impl Default for File { fn default() -> Self { let buffer = unicode_string_into_u8_vec(U16String::from_str("<<>>")); File(SpcString::Unicode(Optional(ImplicitContextTag0(BMPString::new(buffer).unwrap().into()))).into()) } } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct SpcUuid(pub OctetStringAsn1); impl Default for SpcUuid { fn default() -> Self { Self(OctetStringAsn1(vec![ 0xa6, 0xb5, 0x86, 0xd5, 0xb4, 0xa1, 0x24, 0x66, 0xae, 0x05, 0xa2, 0x17, 0xda, 0x8e, 0x60, 0xd6, ])) } } /// [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcSerializedObject ::= SEQUENCE { /// classId SpcUuid, /// serializedData OCTETSTRING /// } /// /// SpcUuid ::= OCTETSTRING /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] pub struct SpcSerializedObject { pub class_id: SpcUuid, pub serialized_data: OctetStringAsn1, } /// [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcString ::= CHOICE { /// unicode [0] IMPLICIT BMPSTRING, /// ascii [1] IMPLICIT IA5STRING /// } /// ``` #[derive(Debug, PartialEq, Eq, Clone)] pub enum SpcString { Unicode(Optional>), Ancii(Optional>), } impl Serialize for SpcString { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { SpcString::Unicode(unicode) => unicode.serialize(serializer), SpcString::Ancii(ancii) => ancii.serialize(serializer), } } } impl<'de> Deserialize<'de> for SpcString { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SpcString; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SpcString") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, SpcString, "choice tag"); let spc_string = match tag_peeker.next_tag.class_and_number() { (TagClass::ContextSpecific, 0) => SpcString::Unicode(seq_next_element!( seq, Optional>, SpcString, "BMPStringAsn1" )), (TagClass::ContextSpecific, 1) => SpcString::Ancii(seq_next_element!( seq, Optional>, SpcString, "IA5StringAsn1" )), _ => { return Err(serde_invalid_value!( SpcString, "unknown choice value", "a supported SpcString choice" )); } }; Ok(spc_string) } } deserializer.deserialize_enum("SpcString", &["Unicode, Ancii"], Visitor) } } impl TryFrom for SpcString { type Error = CharSetError; fn try_from(string: String) -> Result { let buffer = unicode_string_into_u8_vec(U16String::from_str(&string)); Ok(SpcString::Unicode(Optional(ImplicitContextTag0( BMPString::new(buffer)?.into(), )))) } } /// [Authenticode_PE.docx](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx) /// ``` not_rust /// SpcSpOpusInfo ::= SEQUENCE { /// programName [0] EXPLICIT SpcString OPTIONAL, /// moreInfo [1] EXPLICIT SpcLink OPTIONAL, /// } /// ``` #[derive(Serialize, Debug, PartialEq, Eq, Clone)] pub struct SpcSpOpusInfo { #[serde(skip_serializing_if = "Option::is_none")] pub program_name: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub more_info: Option>, } impl<'de> Deserialize<'de> for SpcSpOpusInfo { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SpcSpOpusInfo; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SpcSpOpusInfo") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { Ok(SpcSpOpusInfo { program_name: seq.next_element().unwrap_or(Some(None)).unwrap_or(None), more_info: seq.next_element().unwrap_or(Some(None)).unwrap_or(None), }) } } deserializer.deserialize_seq(Visitor) } } fn unicode_string_into_u8_vec(unicode_string: U16String) -> Vec { let mut buffer = Vec::with_capacity(unicode_string.len() * 2); for elem in unicode_string.into_vec().into_iter() { let bytes = elem.to_be_bytes(); buffer.push(bytes[0]); buffer.push(bytes[1]); } buffer } picky-asn1-x509-0.10.0/src/pkcs7/crls.rs000064400000000000000000000235430072674642500155520ustar 00000000000000use super::signer_info::CertificateSerialNumber; use crate::{AlgorithmIdentifier, Extensions, Name, Time, Version}; use picky_asn1::tag::{Tag, TagClass, TagPeeker}; use picky_asn1::wrapper::{ Asn1SequenceOf, BitStringAsn1, ExplicitContextTag0, ImplicitContextTag1, ObjectIdentifierAsn1, }; use serde::{de, ser, Deserialize, Serialize}; /// [RFC 5652 #10.2.1](https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.1) /// /// ```not_rust /// RevocationInfoChoices ::= SET OF RevocationInfoChoice /// ``` #[derive(Debug, PartialEq, Clone, Default)] pub struct RevocationInfoChoices(pub Vec); // This is a workaround for constructed encoding as implicit impl ser::Serialize for RevocationInfoChoices { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { let mut raw_der = picky_asn1_der::to_vec(&self.0).unwrap_or_else(|_| vec![0]); raw_der[0] = Tag::context_specific_constructed(1).inner(); picky_asn1_der::Asn1RawDer(raw_der).serialize(serializer) } } impl<'de> de::Deserialize<'de> for RevocationInfoChoices { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { let mut raw_der = picky_asn1_der::Asn1RawDer::deserialize(deserializer)?.0; raw_der[0] = Tag::SEQUENCE.inner(); let vec = picky_asn1_der::from_bytes(&raw_der).unwrap_or_default(); Ok(RevocationInfoChoices(vec)) } } /// [RFC 5652 #10.2.1](https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.1) /// /// ```not_rust /// RevocationInfoChoice ::= CHOICE { /// crl CertificateList, /// other [1] IMPLICIT OtherRevocationInfoFormat } /// /// ``` #[allow(clippy::large_enum_variant)] #[derive(Debug, PartialEq, Clone)] pub enum RevocationInfoChoice { Crl(CertificateList), Other(ImplicitContextTag1), } impl ser::Serialize for RevocationInfoChoice { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { RevocationInfoChoice::Crl(certificate_list) => certificate_list.serialize(serializer), RevocationInfoChoice::Other(other_revocation_info_format) => { other_revocation_info_format.serialize(serializer) } } } } impl<'de> Deserialize<'de> for RevocationInfoChoice { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = RevocationInfoChoice; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded RevocationInfoChoice") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, RevocationInfoChoice, "choice tag"); let revocation_info_choice = if tag_peeker.next_tag.class() == TagClass::ContextSpecific && tag_peeker.next_tag.number() == 1 { RevocationInfoChoice::Other(seq_next_element!( seq, RevocationInfoChoice, "OtherRevocationInfoFormat " )) } else { RevocationInfoChoice::Crl(seq_next_element!(seq, RevocationInfoChoice, "CertificateList")) }; Ok(revocation_info_choice) } } deserializer.deserialize_enum("RevocationInfoChoice", &["Crl", "Other"], Visitor) } } /// CRLs are specified in X.509 /// ``` not_rust /// [RFC 5280 #5.1](https://datatracker.ietf.org/doc/html/rfc5280#section-5.1) /// CertificateList ::= SEQUENCE { /// tbsCertList TBSCertList, /// signatureAlgorithm AlgorithmIdentifier, /// signatureValue BIT STRING } /// /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct CertificateList { pub tbs_cert_list: TbsCertList, pub signature_algorithm: AlgorithmIdentifier, pub signature_value: BitStringAsn1, } /// ``` not_rust /// [RFC 5280 #5.1](https://datatracker.ietf.org/doc/html/rfc5280#section-5.1) /// TBSCertList ::= SEQUENCE { /// version Version OPTIONAL, /// -- if present, MUST be v2 /// signature AlgorithmIdentifier, /// issuer Name, /// thisUpdate Time, /// nextUpdate Time OPTIONAL, /// revokedCertificates SEQUENCE OF SEQUENCE { /// userCertificate CertificateSerialNumber, /// revocationDate Time, /// crlEntryExtensions Extensions OPTIONAL /// -- if present, version MUST be v2 /// } OPTIONAL, /// crlExtensions [0] EXPLICIT Extensions OPTIONAL /// -- if present, version MUST be v2 /// } /// ``` #[derive(Serialize, Debug, PartialEq, Clone)] pub struct TbsCertList { pub version: Option, pub signature: AlgorithmIdentifier, pub issuer: Name, pub this_update: Time, pub next_update: Option(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let version = seq.next_element().unwrap_or(Some(None)).unwrap_or(None); if version.is_some() && !version.eq(&Some(Version::V2)) { return Err(serde_invalid_value!( TbsCertList, "Version of TbsCertList doesn't equal to v2", "Version of TbsCertList equals to v2" )); } let signature = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?; let issuer = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?; let this_update = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?; let next_update = seq.next_element().unwrap_or(Some(None)).unwrap_or(None); let tag_peeker: TagPeeker = seq_next_element!(seq, TbsCertList, "a tag"); let revoked_certificates = if tag_peeker.next_tag == Tag::SEQUENCE { seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))? } else { None }; let crl_extension = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(6, &self))?; Ok(TbsCertList { version, signature, issuer, this_update, next_update, revoked_certificates, crl_extension, }) } } deserializer.deserialize_seq(Visitor) } } #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] pub struct RevokedCertificates(pub Asn1SequenceOf); #[derive(Serialize, Debug, PartialEq, Clone)] pub struct RevokedCertificate { pub user_certificate: CertificateSerialNumber, pub revocation_data: Time, pub crl_entry_extensions: Option, } impl<'de> de::Deserialize<'de> for RevokedCertificate { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = RevokedCertificate; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-decoded TbsCertList") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { Ok(RevokedCertificate { user_certificate: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?, revocation_data: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?, crl_entry_extensions: seq.next_element().unwrap_or(Some(None)).unwrap_or(None), }) } } deserializer.deserialize_seq(Visitor) } } /// [RFC 5652 #10.2.1](https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.1) /// ``` not_rust /// OtherRevocationInfoFormat ::= SEQUENCE { /// otherRevInfoFormat OBJECT IDENTIFIER, /// otherRevInfo ANY DEFINED BY otherRevInfoFormat } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct OtherRevocationInfoFormat { other_rev_info_format: ObjectIdentifierAsn1, other_rev_info: (), } picky-asn1-x509-0.10.0/src/pkcs7/ctl.rs000064400000000000000000000447030072674642500153720ustar 00000000000000use crate::{oids, AlgorithmIdentifier}; use picky_asn1::wrapper::{ Asn1SequenceOf, Asn1SetOf, BitStringAsn1, IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1, OctetStringAsn1Container, UTCTimeAsn1, }; use serde::{de, ser, Deserialize, Deserializer, Serialize}; /// ``` not_rust /// CTL ::= SEQUENCE { /// signers SEQUENCE of OBJECT IDENTIFIER, /// sequenceNumber INTEGER, /// effectiveDate UTCTime, /// digestAlgorithm AlgorithmIdentifier, /// ctlEntries: SEQUENCE OF CTLEntry /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Ctl { pub signers: Asn1SequenceOf, pub sequence_number: IntegerAsn1, pub effective_date: UTCTimeAsn1, pub digest_algorithm: AlgorithmIdentifier, pub crl_entries: Asn1SequenceOf, } /// ``` not_rust /// CTLEntry ::= SEQUENCE { /// certFingerprint OCTET STRING, /// attributes SET OF CTLEntryAttribute /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct CTLEntry { pub cert_fingerprint: OctetStringAsn1, pub attributes: Asn1SetOf, } /// ``` not_rust /// CTLEntryAttribute ::= SEQUENCE { /// oid OBJECT IDENTIFIER /// } /// ``` #[derive(Serialize, Debug, PartialEq, Eq, Clone)] pub struct CTLEntryAttribute { pub oid: ObjectIdentifierAsn1, pub value: CTLEntryAttributeValues, } impl<'de> de::Deserialize<'de> for CTLEntryAttribute { fn deserialize(deserializer: D) -> Result>::Error> where D: Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = CTLEntryAttribute; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded CTLEntryAttribute") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let oid: ObjectIdentifierAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; let value = match Into::::into(&oid.0).as_str() { oids::CERT_AUTH_ROOT_SHA256_HASH_PROP_ID => CTLEntryAttributeValues::CertAuthRootSha256HashPropId( seq_next_element!(seq, Asn1SetOf, CTLEntryAttribute, "OctetStringAsn1"), ), oids::CERT_DISALLOWED_FILETIME_PROP_ID => CTLEntryAttributeValues::CertDisallowedFileTimePropId( seq_next_element!(seq, Asn1SetOf, CTLEntryAttribute, "OctetStringAsn1"), ), oids::DISALLOWED_ENHKEY_USAGE => CTLEntryAttributeValues::DisallowedEnhkeyUsage(seq_next_element!( seq, Asn1SetOf>>, CTLEntryAttribute, "OctetStringAsn1Container>" )), oids::CERT_ENHKEY_USAGE_PROP_ID => { CTLEntryAttributeValues::CertEnhkeyUsagePropId(seq_next_element!( seq, Asn1SetOf>>, CTLEntryAttribute, "OctetStringAsn1Container>" )) } oids::CERT_FRIENDLY_NAME_PROP_ID => CTLEntryAttributeValues::CertFriendlyName(seq_next_element!( seq, Asn1SetOf, CTLEntryAttribute, "OctetStringAsn1" )), oids::CERT_KEY_IDENTIFIER_PROP_ID => CTLEntryAttributeValues::CertKeyIdentifierPropId( seq_next_element!(seq, Asn1SetOf, CTLEntryAttribute, "OctetStringAsn1"), ), oids::CERT_ROOT_PROGRAM_CHAIN_POLICIES_PROP_ID => { CTLEntryAttributeValues::CertRootProgramChainPolicy(seq_next_element!( seq, Asn1SetOf>, CTLEntryAttribute, "OctetStringAsn1Container" )) } oids::CERT_ROOT_PROGRAM_CERT_POLICIES_PROP_ID => { CTLEntryAttributeValues::CertRootProgramCertPoliciesPropId(seq_next_element!( seq, Asn1SetOf>>, CTLEntryAttribute, "OctetStringAsn1Container" )) } oids::CERT_SUBJECT_NAME_MD5_HASH_PROP_ID => CTLEntryAttributeValues::CertSubjectNameMd5HashPropId( seq_next_element!(seq, Asn1SetOf, CTLEntryAttribute, "OctetStringAsn1"), ), oids::UNKNOWN_RESERVED_PROP_ID_126 => CTLEntryAttributeValues::UnknownReservedPropId126( seq_next_element!(seq, Asn1SetOf, CTLEntryAttribute, "OctetStringAsn1"), ), oids::UNKNOWN_RESERVED_PROP_ID_127 => { CTLEntryAttributeValues::UnknownReservedPropId127(seq_next_element!( seq, Asn1SetOf>>, CTLEntryAttribute, "OctetStringAsn1Container>" )) } _ => { return Err(serde_invalid_value!( CTLEntryAttribute, "unknown oid typ", "any of [CERT_AUTH_ROOT_SHA256_HASH_PROP_ID, CERT_DISALLOWED_FILETIME_PROP_ID, DISALLOWED_ENHKEY_USAGE, CERT_ENHKEY_USAGE_PROP_ID,\ CERT_FRIENDLY_NAME_PROP_ID, CERT_KEY_IDENTIFIER_PROP_ID, CERT_ROOT_PROGRAM_CHAIN_POLICIES_PROP_ID, CERT_ROOT_PROGRAM_CERT_POLICIES_PROP_ID, \ CERT_SUBJECT_NAME_MD5_HASH_PROP_ID, UNKNOWN_RESERVED_PROP_ID_126, UNKNOWN_RESERVED_PROP_ID_127]" )); } }; Ok(CTLEntryAttribute { oid, value }) } } deserializer.deserialize_seq(Visitor) } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum CTLEntryAttributeValues { CertAuthRootSha256HashPropId(Asn1SetOf), CertDisallowedFileTimePropId(Asn1SetOf), // A 64-bit little-endian Windows FILETIME that indicates when the certificate was revoked. It can be empty, which indicates since epoch DisallowedEnhkeyUsage(Asn1SetOf>>), CertEnhkeyUsagePropId(Asn1SetOf>>), // Contains an array of object identifiers (OIDs) for Certificate Trust List (CTL) extensions CertFriendlyName(Asn1SetOf), // The certificate friendly name CertKeyIdentifierPropId(Asn1SetOf), // The name of the private key file CertRootProgramChainPolicy(Asn1SetOf>), CertRootProgramCertPoliciesPropId(Asn1SetOf>>), CertSubjectNameMd5HashPropId(Asn1SetOf), // Contains the md5 hash of the subject name UnknownReservedPropId126(Asn1SetOf), // Indicates the NotBefore time of a particular certificate, as a Windows FILETIME UnknownReservedPropId127(Asn1SetOf>>), // Appears to be the set of EKUs for which the NotBefore-ing applies } impl Serialize for CTLEntryAttributeValues { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { CTLEntryAttributeValues::CertAuthRootSha256HashPropId(octet_string) => octet_string.serialize(serializer), CTLEntryAttributeValues::CertDisallowedFileTimePropId(octet_string) => octet_string.serialize(serializer), CTLEntryAttributeValues::DisallowedEnhkeyUsage(octet_string_container) => { octet_string_container.serialize(serializer) } CTLEntryAttributeValues::CertEnhkeyUsagePropId(octet_string_container) => { octet_string_container.serialize(serializer) } CTLEntryAttributeValues::CertFriendlyName(octet_string) => octet_string.serialize(serializer), CTLEntryAttributeValues::CertKeyIdentifierPropId(octet_string) => octet_string.serialize(serializer), CTLEntryAttributeValues::CertRootProgramChainPolicy(root_program_chain_policy) => { root_program_chain_policy.serialize(serializer) } CTLEntryAttributeValues::CertRootProgramCertPoliciesPropId(octet_string_container) => { octet_string_container.serialize(serializer) } CTLEntryAttributeValues::CertSubjectNameMd5HashPropId(octet_string) => octet_string.serialize(serializer), CTLEntryAttributeValues::UnknownReservedPropId126(octet_string) => octet_string.serialize(serializer), CTLEntryAttributeValues::UnknownReservedPropId127(octet_string_container) => { octet_string_container.serialize(serializer) } } } } /// ``` not_rust /// RootProgramChainPolicy ::= SEQUENCE { /// oid OBJECT IDENTIFIER /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct RootProgramChainPolicy { pub oid: ObjectIdentifierAsn1, } /// ``` not_rust /// CertPolicy ::= SEQUENCE { /// oid OBJECT IDENTIFIER, /// qualifier SEQUENCE OF PolicyQualifier /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct CertPolicy { pub oid: ObjectIdentifierAsn1, pub qualifier: Asn1SequenceOf, } /// ``` not_rust /// PolicyQualifier ::= SEQUENCE { /// oid OBJECT IDENTIFIER, /// bits BIT STRING /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct PolicyQualifier { pub oid: ObjectIdentifierAsn1, pub bits: BitStringAsn1, } #[cfg(test)] mod tests { use super::*; use crate::{AlgorithmIdentifier, ShaVariant}; use base64::{engine::general_purpose, Engine as _}; use picky_asn1::date::UTCTime; #[test] fn decode_certificate_trust_list_entry() { let ctl_entry_hex = general_purpose::STANDARD .decode( "MIIBKgQUzdTurmAArH9Aw4AsFx4wFIAwwHIxggEQMBgGCisGAQQBgjcKC34xCgQI\ AADZtUTB0gEwHgYKKwYBBAGCNwoLaTEQBA4wDAYKKwYBBAGCNzwDAjAgBgorBgEE\ AYI3CgsdMRIEEPDEAvBATqmtvyWgPd8spvowJAYKKwYBBAGCNwoLFDEWBBQOrIJg\ QFYnl+UlE/wq4QpTlVnkpDAwBgorBgEEAYI3CgtiMSIEIIhd5kw0Dj6nBljwHhFF\ +Vf82ieqvuoaufqp/bAQLUB3MFoGCisGAQQBgjcKCwsxTARKTQBpAGMAcgBvAHMA\ bwBmAHQAIABSAG8AbwB0ACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAQQB1AHQA\ aABvAHIAaQB0AHkAAAA=", ) .unwrap(); let cert_fingerprint = OctetStringAsn1::from(ctl_entry_hex[6..26].to_vec()); let unknown_reserved_prop_id_126_entry = CTLEntryAttribute { oid: oids::unknown_reserved_prop_id_126().into(), value: CTLEntryAttributeValues::UnknownReservedPropId126( vec![ctl_entry_hex[48..56].to_vec().into()].into(), ), }; check_serde!(unknown_reserved_prop_id_126_entry: CTLEntryAttribute in ctl_entry_hex[30..56]); let cert_root_program_chain_policies_prop_id_entry = CTLEntryAttribute { oid: oids::cert_root_program_chain_policies_prop_id().into(), value: CTLEntryAttributeValues::CertRootProgramChainPolicy( vec![RootProgramChainPolicy { oid: oids::auto_update_end_revocation().into(), } .into()] .into(), ), }; check_serde!(cert_root_program_chain_policies_prop_id_entry: CTLEntryAttribute in ctl_entry_hex[56..88]); let cert_subject_name_md5_hash_prop_id_entry = CTLEntryAttribute { oid: oids::cert_subject_name_md5_hash_prop_id().into(), value: CTLEntryAttributeValues::CertSubjectNameMd5HashPropId( vec![ctl_entry_hex[106..122].to_vec().into()].into(), ), }; check_serde!(cert_subject_name_md5_hash_prop_id_entry: CTLEntryAttribute in ctl_entry_hex[88..122]); let cert_key_identifier_prop_id_entry = CTLEntryAttribute { oid: oids::cert_key_identifier_prop_id().into(), value: CTLEntryAttributeValues::CertKeyIdentifierPropId( vec![ctl_entry_hex[140..160].to_vec().into()].into(), ), }; check_serde!(cert_key_identifier_prop_id_entry: CTLEntryAttribute in ctl_entry_hex[122..160]); let cert_auto_root_sha256_hash_prop_id_entry = CTLEntryAttribute { oid: oids::cert_auto_root_sha256_hash_prop_id().into(), value: CTLEntryAttributeValues::CertAuthRootSha256HashPropId( vec![ctl_entry_hex[178..210].to_vec().into()].into(), ), }; check_serde!(cert_auto_root_sha256_hash_prop_id_entry: CTLEntryAttribute in ctl_entry_hex[160..210]); let cert_friendly_name_prop_id_entry = CTLEntryAttribute { oid: oids::cert_friendly_name_prop_id().into(), value: CTLEntryAttributeValues::CertFriendlyName(vec![ctl_entry_hex[228..302].to_vec().into()].into()), }; check_serde!(cert_friendly_name_prop_id_entry: CTLEntryAttribute in ctl_entry_hex[210..302]); let attributes = Asn1SetOf::from(vec![ unknown_reserved_prop_id_126_entry, cert_root_program_chain_policies_prop_id_entry, cert_subject_name_md5_hash_prop_id_entry, cert_key_identifier_prop_id_entry, cert_auto_root_sha256_hash_prop_id_entry, cert_friendly_name_prop_id_entry, ]); let ctl_entry = CTLEntry { cert_fingerprint, attributes, }; check_serde!(ctl_entry: CTLEntry in ctl_entry_hex); } #[test] fn decode_certificate_trust_list() { let ctl_hex = general_purpose::STANDARD .decode( "MIIBZzAMBgorBgEEAYI3CgMJAgkUAddM40UqdcsXDTIxMDUxOTE5MTUwM1owCQYF\ Kw4DAhoFADCCATAwggEsBBQY98H8wwkCA/1bqi+GGnVJdsjdJTGCARIwGAYKKwYB\ BAGCNwoLaDEKBAgAADYETd/TATAYBgorBgEEAYI3Cgt+MQoECAAAEMUektIBMBwG\ CisGAQQBgjcKCwkxDgQMMAoGCCsGAQUFBwMIMCAGCisGAQQBgjcKCx0xEgQQT/fb\ VtD9iKGf47qLwA4GYjAkBgorBgEEAYI3CgsUMRYEFD7fKQzB9cxzLOs9JOF+Utq9\ J+LwMDAGCisGAQQBgjcKC2IxIgQgW3iZh/PEBVuHAJQbM3g6Xxbgz/k36jIBH+BH\ efdjUwgwRAYKKwYBBAGCNwoLCzE2BDRWAGUAcgBpAFMAaQBnAG4AIABUAGkAbQBl\ ACAAUwB0AGEAbQBwAGkAbgBnACAAQwBBAAAA", ) .unwrap(); let cert_enhkey_usage_prop_id_entry = CTLEntryAttribute { oid: oids::cert_enhkey_usage_prop_id().into(), value: CTLEntryAttributeValues::CertEnhkeyUsagePropId( vec![OctetStringAsn1Container(vec![oids::kp_time_stamping().into()].into())].into(), ), }; check_serde!(cert_enhkey_usage_prop_id_entry: CTLEntryAttribute in ctl_hex[141..171]); let ctl = Ctl { signers: vec![oids::root_list_signer().into()].into(), sequence_number: ctl_hex[20..29].to_vec().into(), effective_date: UTCTime::new(2021, 5, 19, 19, 15, 3).unwrap().into(), digest_algorithm: AlgorithmIdentifier::new_sha(ShaVariant::SHA1), crl_entries: vec![CTLEntry { cert_fingerprint: ctl_hex[65..85].to_vec().into(), attributes: vec![ CTLEntryAttribute { oid: oids::cert_disallowed_filetime_prop_id().into(), value: CTLEntryAttributeValues::CertDisallowedFileTimePropId( vec![ctl_hex[107..115].to_vec().into()].into(), ), }, CTLEntryAttribute { oid: oids::unknown_reserved_prop_id_126().into(), value: CTLEntryAttributeValues::UnknownReservedPropId126( vec![ctl_hex[133..141].to_vec().into()].into(), ), }, cert_enhkey_usage_prop_id_entry, CTLEntryAttribute { oid: oids::cert_subject_name_md5_hash_prop_id().into(), value: CTLEntryAttributeValues::CertSubjectNameMd5HashPropId( vec![ctl_hex[189..205].to_vec().into()].into(), ), }, CTLEntryAttribute { oid: oids::cert_key_identifier_prop_id().into(), value: CTLEntryAttributeValues::CertKeyIdentifierPropId( vec![ctl_hex[223..243].to_vec().into()].into(), ), }, CTLEntryAttribute { oid: oids::cert_auto_root_sha256_hash_prop_id().into(), value: CTLEntryAttributeValues::CertAuthRootSha256HashPropId( vec![ctl_hex[261..293].to_vec().into()].into(), ), }, CTLEntryAttribute { oid: oids::cert_friendly_name_prop_id().into(), value: CTLEntryAttributeValues::CertFriendlyName(vec![ctl_hex[311..].to_vec().into()].into()), }, ] .into(), }] .into(), }; check_serde!(ctl: Ctl in ctl_hex); } } picky-asn1-x509-0.10.0/src/pkcs7/signed_data.rs000064400000000000000000000611770072674642500170560ustar 00000000000000use super::content_info::EncapsulatedContentInfo; use super::crls::RevocationInfoChoices; use super::signer_info::SignerInfo; use crate::cmsversion::CmsVersion; use crate::AlgorithmIdentifier; use picky_asn1::tag::{Tag, TagClass, TagPeeker}; use picky_asn1::wrapper::{Asn1SetOf, Optional}; use picky_asn1_der::Asn1RawDer; use serde::{de, ser, Deserialize, Serialize}; /// [RFC 5652 #5.1](https://datatracker.ietf.org/doc/html/rfc5652#section-5.1) /// ``` not_rust /// SignedData ::= SEQUENCE { /// version CMSVersion, /// digestAlgorithms DigestAlgorithmIdentifiers, /// encapContentInfo EncapsulatedContentInfo, /// certificates [0] IMPLICIT CertificateSet OPTIONAL, /// crls [1] IMPLICIT RevocationInfoChoices OPTIONAL, /// signerInfos SignerInfos } /// ``` #[derive(Serialize, Debug, PartialEq, Clone)] pub struct SignedData { pub version: CmsVersion, pub digest_algorithms: DigestAlgorithmIdentifiers, pub content_info: EncapsulatedContentInfo, pub certificates: Optional, #[serde(skip_serializing_if = "Option::is_none")] pub crls: Option, pub signers_infos: SignersInfos, } // Implement Deserialize manually to support absent RevocationInfoChoices impl<'de> de::Deserialize<'de> for SignedData { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SignedData; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SignedData") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let version = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; let digest_algorithms = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?; let content_info = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?; let certificates = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?; let tag_peeker: TagPeeker = seq_next_element!(seq, SignedData, "ApplicationTag1"); let crls = if tag_peeker.next_tag.class() == TagClass::ContextSpecific && tag_peeker.next_tag.number() == 1 { seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))? } else { None }; let signers_infos = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?; Ok(SignedData { version, digest_algorithms, content_info, certificates, crls, signers_infos, }) } } deserializer.deserialize_seq(Visitor) } } /// [RFC 5652 #5.1](https://datatracker.ietf.org/doc/html/rfc5652#section-5.1) /// ``` not_rust /// DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct DigestAlgorithmIdentifiers(pub Asn1SetOf); /// [RFC 5652 #5.1](https://datatracker.ietf.org/doc/html/rfc5652#section-5.1) /// ``` not_rust /// SignerInfos ::= SET OF SignerInfo /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct SignersInfos(pub Asn1SetOf); /// [RFC 5652 #10.2.3](https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.3) /// ``` not_rust /// CertificateSet ::= SET OF CertificateChoices /// ``` #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct CertificateSet(pub Vec); // This is a workaround for constructed encoding as implicit impl ser::Serialize for CertificateSet { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { let mut raw_der = picky_asn1_der::to_vec(&self.0).unwrap_or_else(|_| vec![0]); raw_der[0] = Tag::context_specific_constructed(0).inner(); picky_asn1_der::Asn1RawDer(raw_der).serialize(serializer) } } impl<'de> de::Deserialize<'de> for CertificateSet { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { let mut raw_der = picky_asn1_der::Asn1RawDer::deserialize(deserializer)?.0; raw_der[0] = Tag::SEQUENCE.inner(); let vec = picky_asn1_der::from_bytes(&raw_der).unwrap_or_default(); Ok(CertificateSet(vec)) } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum CertificateChoices { Certificate(Asn1RawDer), Other(Asn1RawDer), } impl Serialize for CertificateChoices { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { CertificateChoices::Certificate(certificate) => certificate.serialize(serializer), CertificateChoices::Other(other) => other.serialize(serializer), } } } impl<'de> de::Deserialize<'de> for CertificateChoices { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = CertificateChoices; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded CertificateChoices") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, CertificateChoices, "Any tag"); let certificate_choice = match tag_peeker.next_tag { Tag::SEQUENCE => { CertificateChoices::Certificate(seq_next_element!(seq, CertificateChoices, "Certificate")) } _ => { CertificateChoices::Other(seq_next_element!(seq, CertificateChoices, "Other certificate type")) } }; Ok(certificate_choice) } } deserializer.deserialize_enum("CertificateChoices", &["Certificate, Other"], Visitor) } } #[cfg(test)] mod tests { use super::*; use crate::crls::*; use crate::{ oids, Certificate, EncapsulatedRsaPublicKey, Extension, Extensions, KeyIdentifier, Name, NameAttr, PublicKey, RsaPublicKey, SubjectPublicKeyInfo, TbsCertificate, Validity, Version, }; use base64::{engine::general_purpose, Engine as _}; use picky_asn1::bit_string::BitString; use picky_asn1::date::UTCTime; use picky_asn1::restricted_string::{IA5String, PrintableString}; use picky_asn1::wrapper::{IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1Container, PrintableStringAsn1}; #[test] fn decode_test() { let pkcs7 = general_purpose::STANDARD .decode( "MIIGOgYJKoZIhvcNAQcCoIIGKzCCBicCAQExADALBgkqhkiG9w0BBwGgggYNMIIG\ CTCCA/GgAwIBAgIUOnS/zC1zk2aJttmSVNtzX8rhMXwwDQYJKoZIhvcNAQELBQAw\ gZMxCzAJBgNVBAYTAlVBMRIwEAYDVQQIDAlIdW1ibGVHdXkxETAPBgNVBAcMCFNv\ bWVDaXR5MRkwFwYDVQQKDBBTb21lT3JnYW5pemF0aW9uMREwDwYDVQQLDAhTb21l\ VW5pdDEMMAoGA1UEAwwDR3V5MSEwHwYJKoZIhvcNAQkBFhJzb21lZW1haWxAbWFp\ bC5jb20wHhcNMjEwNDIzMTQzMzQzWhcNMjIwNDIzMTQzMzQzWjCBkzELMAkGA1UE\ BhMCVUExEjAQBgNVBAgMCUh1bWJsZUd1eTERMA8GA1UEBwwIU29tZUNpdHkxGTAX\ BgNVBAoMEFNvbWVPcmdhbml6YXRpb24xETAPBgNVBAsMCFNvbWVVbml0MQwwCgYD\ VQQDDANHdXkxITAfBgkqhkiG9w0BCQEWEnNvbWVlbWFpbEBtYWlsLmNvbTCCAiIw\ DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM6JtTiGxVFdQr0r5hpBioObluoZ\ UW/u4RMPLlb4xwuIVM+q7Xk968c8FKoxMsTGPfjfF6CBHhvcZTojYRLqFdHaYRzl\ +m5gnR6ZJRYGOtH7dyFX+2UgTIuLxsBPoXoY/DICpUp2sch8eXmi+lwL1A8Kk9pM\ CB0s0+nVwNLqpa6aZg5kFkverZzn8tdV8z2yg/BV1fx7FGIDYFuoqc10azEg9aa8\ bq1psf4c4IrymFEBvuXlvi/vukY/hUPFLHDAjt6vQeDNT0GjsPIj7Fb5ISLEVbBb\ qMKq0Atr6Af2avtIMudTVm+BT9QlX1gUr83GLiIhsPbS/WBPJcdeWLxvjWIUIJNo\ hIJkL6YhYhkeniNN5Pq0zrhkSGNt5ai2ZeW/D4npEeCbR7gjsQm8LPJDrnDH3Iax\ KvPgxen/rCMfssgw6UUWUEGn3n6QPtBp7HcWe+oBQOEuL6zIJKG8XzEypn6EZm+x\ p7TjCcUgRm1X5OtDnc8E8yHsrs9dKLhzLARs6XDgcw1KhfhzryLY6VsjZD9mm5iu\ PVgw0Hg+v4cxekWYcjJWCf6EjsCV9iax4UwGb1G7yD5XsYULajZOYqRYNak2Jruu\ 18daA66TQ8HNas25YFFQQQtQG/1RrL1u853DBlrxaNcZQfR6mkE1D7O5MUADqjoM\ pgoL7k2XqkJMjs/PAgMBAAGjUzBRMB0GA1UdDgQWBBQAX8F1PgwVwxbjCDdpvYKI\ 0YW9DTAfBgNVHSMEGDAWgBQAX8F1PgwVwxbjCDdpvYKI0YW9DTAPBgNVHRMBAf8E\ BTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAH3Qweqt8+PnQoAKTPXTUMp+lE01a0\ 2vzoc7EPWiclyuQPatlUIyUEH5nbBiXu8v9X5wHIrfzkV7tO+clVy9w6a4Fhnejv\ 2zurSHf6vP/UEq2gPuBJ1jc1BDpE4TtlZdrO6GYBQwETRBbw44lFvyk6sjnCHPgz\ nl5dryWIyNSALFpSzUJ9xSdtzWEKnWe9NaxBc6b0RxJSsRl33Fx25WkKMuhY4j26\ wZvWMSj86eRdI7BP31UGEt8GdfQscz5JtMlY+eJbilAMTZt4iAEJFv9OI7/asVJv\ u8oNZJewGstWqRyRrJcHeEINjxeKL0quKJQF38fCd6pqRI7PlPBaGfVCSHTggpKO\ yD0ACcE13kcjnOwa8J/DFFZVpI3oofGUE+hajJT09vGJv4NJKUfdJIuieEFeJe8B\ TPkVjCHp6j6Vj56EdGqvkYtVsuzHUNlIsEcpXGEiODbwbps7GxPCiurVIldun2Gu\ 1mq8Q6aU+yh5Fs5ZsSXozzXyWqwPkT5WbJEOAUMd2+JSRHN83MOSqq+igpDBKQZQ\ t5vcoqFzuspOVIvdPLFY3pPZY9dxVNdDi4T6qJNZCq++Ukyc0LQOUkshF9HaHB3I\ xUDGjR5n4X0lkjgM5IvL+OaZREqWkD/tiCu4V/5Z86mZi6VwCcgYrp/Q4bFjsWBw\ p0mAUFZ9UjurAaEAMQA=", ) .unwrap(); assert_eq!(CmsVersion::V1, CmsVersion::from_u8(*pkcs7.get(25).unwrap()).unwrap()); let digest_algorithm_identifiers = DigestAlgorithmIdentifiers(vec![].into()); check_serde!(digest_algorithm_identifiers: DigestAlgorithmIdentifiers in pkcs7[26..28]); let content_info = EncapsulatedContentInfo { content_type: ObjectIdentifierAsn1::from(oids::pkcs7()), content: None, }; check_serde!(content_info: EncapsulatedContentInfo in pkcs7[28..41]); let mut issuer = Name::new(); issuer.add_attr( NameAttr::CountryName, PrintableStringAsn1::from(PrintableString::new("UA".as_bytes()).unwrap()), ); issuer.add_attr(NameAttr::StateOrProvinceName, "HumbleGuy"); issuer.add_attr(NameAttr::LocalityName, "SomeCity"); issuer.add_attr(NameAttr::OrganizationName, "SomeOrganization"); issuer.add_attr(NameAttr::OrganizationalUnitName, "SomeUnit"); issuer.add_attr(NameAttr::CommonName, "Guy"); issuer.add_email(IA5String::new("someemail@mail.com".as_bytes()).unwrap()); check_serde!(issuer: Name in pkcs7[95..245]); let validity = Validity { not_before: UTCTime::new(2021, 4, 23, 14, 33, 43).unwrap().into(), not_after: UTCTime::new(2022, 4, 23, 14, 33, 43).unwrap().into(), }; check_serde!(validity: Validity in pkcs7[245..277]); let subject = issuer.clone(); check_serde!(subject: Name in pkcs7[277..427]); let subject_public_key_info = SubjectPublicKeyInfo { algorithm: AlgorithmIdentifier::new_rsa_encryption(), subject_public_key: PublicKey::Rsa(EncapsulatedRsaPublicKey::from(RsaPublicKey { modulus: IntegerAsn1::from(pkcs7[459..972].to_vec()), public_exponent: IntegerAsn1::from(pkcs7[974..977].to_vec()), })), }; check_serde!(subject_public_key_info: SubjectPublicKeyInfo in pkcs7[427..977]); let extensions = Extensions(vec![ Extension::new_subject_key_identifier(pkcs7[992..1012].to_vec()), Extension::new_authority_key_identifier(KeyIdentifier::from(pkcs7[1025..1045].to_vec()), None, None), Extension::new_basic_constraints(*pkcs7.get(1054).unwrap() != 0, None), ]); check_serde!(extensions: Extensions in pkcs7[979..1062]); let full_certificate = Certificate { tbs_certificate: TbsCertificate { version: Version::V3.into(), serial_number: IntegerAsn1(pkcs7[60..80].to_vec()), signature: AlgorithmIdentifier::new_sha256_with_rsa_encryption(), issuer, validity, subject, subject_public_key_info, extensions: extensions.into(), }, signature_algorithm: AlgorithmIdentifier::new_sha256_with_rsa_encryption(), signature_value: BitString::with_bytes(&pkcs7[1082..1594]).into(), }; check_serde!(full_certificate: Certificate in pkcs7[45..1594]); let full_certificate = picky_asn1_der::from_bytes(&picky_asn1_der::to_vec(&full_certificate).unwrap()).unwrap(); let signed_data = SignedData { version: CmsVersion::V1, digest_algorithms: DigestAlgorithmIdentifiers(Vec::new().into()), content_info, certificates: CertificateSet(vec![CertificateChoices::Certificate(full_certificate)]).into(), crls: Some(RevocationInfoChoices(Vec::new())), signers_infos: SignersInfos(Vec::new().into()), }; check_serde!(signed_data: SignedData in pkcs7[19..1598]); } #[test] fn decode_with_crl() { let decoded = general_purpose::STANDARD .decode( "MIIIxwYJKoZIhvcNAQcCoIIIuDCCCLQCAQExADALBgkqhkiG9w0BBwGgggXJMIIF\ xTCCA62gAwIBAgIUFYedpm34R9SrNONqEn43NrNlDHMwDQYJKoZIhvcNAQELBQAw\ cjELMAkGA1UEBhMCZmYxCzAJBgNVBAgMAmZmMQswCQYDVQQHDAJmZjELMAkGA1UE\ CgwCZmYxCzAJBgNVBAsMAmZmMQ8wDQYDVQQDDAZDQU5hbWUxHjAcBgkqhkiG9w0B\ CQEWD2NhbWFpbEBtYWlsLmNvbTAeFw0yMTA0MTkxNTQxNDlaFw0yNjA0MTkxNTQx\ NDlaMHIxCzAJBgNVBAYTAmZmMQswCQYDVQQIDAJmZjELMAkGA1UEBwwCZmYxCzAJ\ BgNVBAoMAmZmMQswCQYDVQQLDAJmZjEPMA0GA1UEAwwGQ0FOYW1lMR4wHAYJKoZI\ hvcNAQkBFg9jYW1haWxAbWFpbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\ ggIKAoICAQCwVfg08dBZyObLkyZufYCZ396B17ICMAjYUWjk2pfK3Q/3C0vCjppd\ F5VW0g49D/ULV7tzRc3AZecw9RxHuwkeXioIZ6NQ92qdg8CnkOPLrSyDlMyDZgYU\ NSFdpz81Bu0v17sUHfREz41Wi5CvdK9qSS/IiuZhEpKYx1trGAc22YwXLBGs6Dcb\ jf3C8zRnG1FCsOYukaG6wUdzUtwkrgOIIMERTqZ1U5s0rXehg4Kb3chAsA31xvKT\ UhMNfovjI+5FDB/ZjZOOPMobnN6E7DLFjBzpa11eFywPFvimNxWjN26HkEceIh7y\ Hm/9GrlSvpXnZQRFNNKIIQBkHt6jbpByxIhU9Yq0uWSZNWk+c34H6sksWZtJpVvM\ YWIGziatkr2Rjskn9xjSNFNHacj5u3j2KKGxCtkxrCXiLY9Chf1CfbhmLpdECTPW\ fgOOzXu/GIFXaxsh0+NqodEChaA5GDztweqt7Ep3/V9c/ITWONzj8SOj97R5OYy8\ rtu24YY+ft2PkRYRSwsJzHs4KfDaf1yN0WCBZSl1itVW7qsEKQ60pp4qOna8XbyN\ 6VY3ce/qhKYPZKs9pFWX5vBTtAFcA4HjmT/EkHJ2ISJU0ueU0E6iH9Q01ENk1dso\ dDMKP354kqmuHW4I0Wc39tJsXdUsGaisVyfOZdJQpqc2sle6LR8WpQIDAQABo1Mw\ UTAdBgNVHQ4EFgQUxy69gEp87JxLsLZKmjYUObTIei4wHwYDVR0jBBgwFoAUxy69\ gEp87JxLsLZKmjYUObTIei4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsF\ AAOCAgEAZzl2JCK3+5lm5vkcidD4swafCkE+kKuS27Ta7RVjfvEmZQmE69meMxuS\ PZ/xcB2EVF+zTMw5qmtOiCFrN90sR5lGHhdkqFDMAhQtu7aGz9rJ8s9R4Hx8NWXk\ d9H6USqWd6O3OIVl4cB7HZEdn4Y+oVgtSd48EAK4C/a2a9NLsrpPgGAB2m3wGRBY\ l9ynvaR9hMXc0qQ/s/BseA/UvEaVVd1mxKcTeL4dGQpBJ9bKYbesJGHJssRnSlDb\ AMI5tgoIYPadumNEqmDZL+eH798lfnyXCLokSZk/I7a4TLgtTnlubbFnapu8M645\ qOS2VWzKnqRYC31DDWy5PcI/eDfLNCzrben9I6nSAx5AFOfi8z7znAwXw1fGcUEw\ BPSzK91KHZkqsOaK/kExja32xeSSy7SW1dBHmwaDbA0kv7COPYCHIWmFsrxFkB9E\ O5P1hSnFMZmdAO2jm/k0cZQxlaYZuio0XCQEJZMvfsGL8qWV5uRdUx8D5zZQem/R\ OHEe1tMTIqJ3BoGgX15atokFY++iVLjk/2eKv1k5Sw5m/4cxxDgcK89UH4Y1UR3u\ ah3emGU6zySj/Y3HpFfKslewb59FZXS/RKgRHhIw1TfauuTNtT5D2LpXPYfLuTrs\ aCpH/QGsSBGiMTmrdXukRCIsz663TKiLVYOdvY4Y+cBcJlk/YMChggLPMIICyzCB\ tAIBATANBgkqhkiG9w0BAQUFADByMQswCQYDVQQGEwJmZjELMAkGA1UECAwCZmYx\ CzAJBgNVBAcMAmZmMQswCQYDVQQKDAJmZjELMAkGA1UECwwCZmYxDzANBgNVBAMM\ BkNBTmFtZTEeMBwGCSqGSIb3DQEJARYPY2FtYWlsQG1haWwuY29tFw0yMTA0MjAw\ NjUyMjRaFw0yMzA0MjAwNjUyMjRaoA4wDDAKBgNVHRQEAwIBAzANBgkqhkiG9w0B\ AQUFAAOCAgEAW/+H6pzGp6cRUssck1a5pAJo2V98gpLX5gnPoorEIE2nkcLChiWc\ RCdJuc5PtOisM/FRl9IxQWpePISB3I15jaL1u1ol5ISNn69f3eWwvVEw3kJSEeb/\ TYvqW0+k1CgMr84oP38K4/434FwfotULX36FdU04MSzMirAszjZ0kLMsb3mNSSaH\ VC0kZs7AnvzwKBXsB143ouNAH5mmLom1EyRAWU1ZP/pFZXDGE1ct2jB+oOdZebQj\ /4VjfGDyvzUd9cNu9i6ZqNf49E9vhemrCdkZHc94QkwO92FhBROZhQ9fKelV8CRs\ jf2oyToe+2NN2eXj+DY/s13Knoeqb7FcD3BFObtrILvE/rrCxZa0JeHfdg79nIiG\ BCfQloA+cZdQsCQ1H1Qd3kwqo6ZLQpeTyW0UeIJNLQiSMATvpMAtunwT/OgxSP/Q\ eTXV+221Eu2tDhXYMVkFtjgFdp0O5XqPU5fNPF/5XL3DlgAaWe9ULl4ZwBNPSkOm\ LiFMcN1hzGQQo00ycuU6eF+Iz+H/olJyrpdJxf0jh2Sok71LX6YlALvfvZjW5eYc\ 8AvDttigOLiDwm8eYAxsC8Ku4cMiMSkgs71vvmz0U/LHypZiNJsEEaR76NH9OLiz\ XCIYfP7WudYgfGBRRiw4WeB7jZNtVzFzkyiwliZLqocBuM8f1O2pv/QxAA==", ) .unwrap(); let mut issuer = Name::new(); issuer.add_attr(NameAttr::CountryName, PrintableString::new("ff").unwrap()); issuer.add_attr(NameAttr::StateOrProvinceName, "ff"); issuer.add_attr(NameAttr::LocalityName, "ff"); issuer.add_attr(NameAttr::OrganizationName, "ff"); issuer.add_attr(NameAttr::OrganizationalUnitName, "ff"); issuer.add_attr(NameAttr::CommonName, "CAName"); issuer.add_email(IA5String::new("camail@mail.com").unwrap()); let tbs_cert_list = TbsCertList { version: Some(Version::V2), signature: AlgorithmIdentifier::new_sha1_with_rsa_encryption(), issuer, this_update: UTCTime::new(2021, 4, 20, 6, 52, 24).unwrap().into(), next_update: Some(UTCTime::new(2023, 4, 20, 6, 52, 24).unwrap().into()), revoked_certificates: None, crl_extension: Some(Extensions(vec![Extension::new_crl_number(OctetStringAsn1Container( IntegerAsn1::from(decoded[1716..1717].to_vec()), ))])) .into(), }; check_serde!(tbs_cert_list: TbsCertList in decoded[1534..1717]); let crl = RevocationInfoChoices(vec![RevocationInfoChoice::Crl(CertificateList { tbs_cert_list, signature_algorithm: AlgorithmIdentifier::new_sha1_with_rsa_encryption(), signature_value: BitString::with_bytes(&decoded[1737..2249]).into(), })]); check_serde!(crl: RevocationInfoChoices in decoded[1526..2249]); } #[test] fn decode_certificate_trust_list_certificate_set() { let decoded = general_purpose::STANDARD .decode( "\ oIINKTCCBhQwggP8oAMCAQICEzMAAABWo7N5AjhScwQAAAAAAFYwDQYJKoZIhvcN\ AQELBQAwgYExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD\ VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzAp\ BgNVBAMTIk1pY3Jvc29mdCBDZXJ0aWZpY2F0ZSBMaXN0IENBIDIwMTEwHhcNMjAx\ MjE1MjEyNTI0WhcNMjExMjAyMjEyNTI0WjCBiTELMAkGA1UEBhMCVVMxEzARBgNV\ BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv\ c29mdCBDb3Jwb3JhdGlvbjEzMDEGA1UEAxMqTWljcm9zb2Z0IENlcnRpZmljYXRl\ IFRydXN0IExpc3QgUHVibGlzaGVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\ CgKCAQEAwXbtTwWpw7LVTZ3PWvV+xvhv7FZALwvCKjBVWINWi02HmMvxlgJM7y4Z\ EOfG4A6PUyXBn7rSSLx1zz309yRYvBjkkY7Ai7S+eG8z99P5AJmVXAkm9AJccFEb\ e5jw3HyLAYTEJiR9X550pYb3w29HLrCz9hGDZ+PJ2nGiXGQFnNkrJuY9yYBoEYRU\ CFWqs1HvX5GcfDT0MuJZpMlg5bG4rvm5+t6Ge5aq9qlgD5YAxFLrEQbZ89BBHRG4\ PODqrYm4+CYWmZADIBxc9aPC5ZWAGYjBc3vu7iMAZu8IDpSeOre2EChCzHiJZn/b\ nguWA6sUvJ3QKx0Gsyld4xbLWhfWGwIDAQABo4IBeTCCAXUwFQYDVR0lBA4wDAYK\ KwYBBAGCNwoDCTAdBgNVHQ4EFgQUIIpJhsQJpHIBXXfSdyHTm19nzx4wVAYDVR0R\ BE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMg\ TGltaXRlZDEWMBQGA1UEBRMNMjI5ODg3KzQ2Mjk5MjAfBgNVHSMEGDAWgBRB8CHH\ 7cSH+oN1/woM3C3sqGqrWTBZBgNVHR8EUjBQME6gTKBKhkhodHRwOi8vY3JsLm1p\ Y3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNDZXJMaXNDQTIwMTFfMjAx\ MS0wMy0yOS5jcmwwXQYIKwYBBQUHAQEEUTBPME0GCCsGAQUFBzAChkFodHRwOi8v\ d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY0Nlckxpc0NBMjAxMV8yMDEx\ LTAzLTI5LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQB36571\ 4rZo6lGtH4lxhxjjgzdsFuJs3vQwuIMulUuaF0+viu4966O+SqX3PtRLj97CqgI5\ wLZK5Ib03ytIFlZ35Q1AE63yPl5gU8LDF2KkE+/kuWkHhxCNMXbQWfsH/7mIbzbo\ PXoixiMHwBsWmEg/Nmk2Ya23NBdnKeGxEv7EI81kejbePacEMzIeXha4vLFrWzsT\ FXICjVh47GcHSCwcGRp2G/wkItekTpXMrkdWr1cjaXHqxqlPorfr7zAoBBkJWMKC\ Wfo09voRYXEhp4TE4ZkMzS+Q4GWyOxU0hCBaQPEt4lm5x5exJPkByGfKVZRhzr4z\ 9IRIptO0ozTSjs9nl7eg5dDqgf/MfoZyTY4mhuGsJqGbwxIoBC/kTbvQP35zjMeT\ 66w8pxx/E+qDunzBWZkKXS4kdgpnb4Mpr3gAIeHpPb+ijiqhB0mS1gKvbCAx4OIZ\ YHJcZU92trWagrRLzS0rvd2WVC/3kkFvcSt5AQg2cJkeKEUOHKGtH6gUzxd1GE11\ XhQO+GTMihVqApJ1KFxrjtZ5J2ZZVM+bd908OAfCEpG5+fFi2FhJZ7LKydWzCGbH\ P7YASXdZ94lGtBqGm8a5FiQAwTOuUaIaHXql8IQAVAqyUpKEDBjl1BcKvb7drWHV\ HeNYLDMntpdv+KAX/WtLapSBsrbxSFlCE3Ag8TCCBw0wggT1oAMCAQICCmERbJIA\ AAAAAAcwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX\ YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg\ Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl\ IEF1dGhvcml0eSAyMDEwMB4XDTExMDMyOTE4NTgzOVoXDTI2MDMyOTE5MDgzOVow\ gYExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS\ ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMT\ Ik1pY3Jvc29mdCBDZXJ0aWZpY2F0ZSBMaXN0IENBIDIwMTEwggIiMA0GCSqGSIb3\ DQEBAQUAA4ICDwAwggIKAoICAQC4hHqA/U0np9LvgjbxUwWdvkJtjjEIR8vNs4MK\ S04zGi583XKBjc6Q/DwFyy80hTiPPBxobWBRUk2s2m0rfrNzR6vS3JVxSjGBWqEf\ q4ImRS2M6IR4vSDwDcb1riaHHlaOVTwIMDKUlCKTC6Wwxl3mLYE5zenHrujYSXFJ\ q5F0uE+NL0ezP9CTg1wCGt5LuLI8N+mT6nJbmMfjrBjg5n5KwYEs/SIUdnPhaNwg\ CcDzRs0jJshFIsrHvHT8if9X4M+9jrAr7ybWd6sa9GdB8V4McaoCf17AgqoJi+yJ\ iEH1A0Jp2R9F2Vc+BJZK1TK30WEmaMfBsaDgegVOtW3CguAutudknxZ9lSqGMtAh\ yF34ywUwHrkCmGyzk2vIg2chXdZlmCBk3cu/R5v/GPrxkN6neM17BIZ+J4q3lZwm\ 3bGW/E/gQCCDaN3sM/IqoAen65H6rA9RQYjxxYdBTIdHYp1YwJ5/uxJ93tOf/cHH\ FL1/mNBXm+HjbFfhZV/w3CucoVTCVioVZMuqTuT9w+h3iP/bDa+Qn9dogQEvlOGv\ xuTGdtt12t/QEkzyiTZvSICBWN0XCSgrVayTI+WOMWWtDY6T03GngRSY6ayqBVju\ 10RDMG0dx7rCf/VIxOWgjlWOtAnAAcOdHUb1/ka1OgCII7XwykHNOw3G9spABOqb\ 5Yg2nwIDAQABo4IBfDCCAXgwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEHw\ IcftxIf6g3X/CgzcLeyoaqtZMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsG\ A1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJc\ YmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9z\ b2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIz\ LmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWlj\ cm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0\ MDcGA1UdJQQwMC4GCCsGAQUFBwMDBgorBgEEAYI3CgMBBgorBgEEAYI3CgMJBgor\ BgEEAYI3CgMTMA0GCSqGSIb3DQEBCwUAA4ICAQCC96mls7/lyFlBJzQPYpxB8Ksr\ ffmnqMioD11Dvq3ymfj/+/Z5UEQMUOpC250B6aVJeSgpEz5ANnQW248gzI0tURDc\ K0E2fLbQQBObHAA3TIFpaLEagpY7aXXH5TTYPtxaCavTv6mvxAhv40fGMu8l6QsL\ VRKU74cUGdLhId43z66mNF0jKQQEbW3nGt1EMHl0Go2Mwfk++a0wa6O0anRJOVs3\ LQEZ7gMp3a5KL/mCr0gfFJqcPSUxVuo6p023/Ys//r93NotV5bMQUO5U1L9r2Cry\ M3guvzH5NhHvMAv5TEOD41upXB1bpnYFuPB1T+m4HzZEpn9m0EsNGFUedC4nJ+Um\ QoNuu6Tve/nkmL3VO4nTWJK40c0Wfzl+ZiUN24NZv1cfm9LpG3InXWsz0f6ikkxR\ PcbMlDpW/+sQQS7dklPNEPEdNusEGts12ZG2mWAP4AusZwxEFpwCR8u3RpZJD98D\ sQ+tDhKtSAU24S0/u1rglNSXkuk+5uslGcsz8d+TYJCmuQ5W9ijpQsceEFumLg+5\ 26lk147lM9KdQ4JLbjdmuQ1nV1RaSA7jivsf7QomvA000gpHhWEqI7HgVIpQFFaF\ wP8t92mZRH0a9E18GA7hBwfuCWZSSnoaYqTli8+FooaKcZCxfdYR01Ee2lznzNYS\ EHaork+TtWTJve3c+w==", ) .unwrap(); let certificate_set: CertificateSet = picky_asn1_der::from_bytes(&decoded).unwrap(); check_serde!(certificate_set: CertificateSet in decoded); } } picky-asn1-x509-0.10.0/src/pkcs7/signer_info.rs000064400000000000000000000313560072674642500171120ustar 00000000000000use crate::cmsversion::CmsVersion; use crate::pkcs7::Pkcs7Certificate; use crate::{oids, AlgorithmIdentifier, Attribute, Name, SubjectKeyIdentifier}; use picky_asn1::tag::{Tag, TagClass, TagPeeker}; use picky_asn1::wrapper::{ Asn1SequenceOf, Asn1SetOf, ImplicitContextTag0, IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1, Optional, }; use serde::{de, ser, Deserialize, Serialize}; /// [RFC 5652 #5.3](https://datatracker.ietf.org/doc/html/rfc5652#section-5.3) /// ``` not_rust /// SignerInfo ::= SEQUENCE { /// version CMSVersion, /// sid SignerIdentifier, /// digestAlgorithm DigestAlgorithmIdentifier, /// signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, /// signatureAlgorithm SignatureAlgorithmIdentifier, /// signature SignatureValue, /// unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL } /// /// SignedAttributes ::= SET SIZE (1..MAX) OF Attribute /// /// UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute /// ``` #[derive(Serialize, Debug, PartialEq, Clone)] pub struct SignerInfo { pub version: CmsVersion, pub sid: SignerIdentifier, pub digest_algorithm: DigestAlgorithmIdentifier, pub signed_attrs: Optional, pub signature_algorithm: SignatureAlgorithmIdentifier, pub signature: SignatureValue, #[serde(skip_serializing_if = "Optional::is_default")] pub unsigned_attrs: Optional, } impl<'de> de::Deserialize<'de> for SignerInfo { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SignerInfo; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SignerInfo") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let version = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; if version != CmsVersion::V1 { return Err(serde_invalid_value!( SignerInfo, "wrong version field", "Version equal to 1" )); } Ok(SignerInfo { version, sid: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?, digest_algorithm: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?, signed_attrs: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?, signature_algorithm: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?, signature: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?, unsigned_attrs: seq .next_element() .unwrap_or_default() .unwrap_or_else(|| Optional::from(UnsignedAttributes::default())), }) } } deserializer.deserialize_seq(Visitor) } } // This is a workaround for constructed encoding as implicit #[derive(Debug, Deserialize, PartialEq, Clone, Default)] pub struct Attributes(pub Asn1SequenceOf); impl ser::Serialize for Attributes { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { let mut raw_der = picky_asn1_der::to_vec(&self.0).unwrap_or_else(|_| vec![0]); raw_der[0] = Tag::context_specific_constructed(0).inner(); picky_asn1_der::Asn1RawDer(raw_der).serialize(serializer) } } /// [RFC 5652 #5.3](https://datatracker.ietf.org/doc/html/rfc5652#section-5.3) /// ``` not_rust /// SignerIdentifier ::= CHOICE { /// issuerAndSerialNumber IssuerAndSerialNumber, /// subjectKeyIdentifier [0] SubjectKeyIdentifier } /// ``` #[derive(Debug, PartialEq, Eq, Clone)] pub enum SignerIdentifier { IssuerAndSerialNumber(IssuerAndSerialNumber), SubjectKeyIdentifier(ImplicitContextTag0), } impl Serialize for SignerIdentifier { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { SignerIdentifier::IssuerAndSerialNumber(issuer_and_serial_number) => { issuer_and_serial_number.serialize(serializer) } SignerIdentifier::SubjectKeyIdentifier(subject_key_identifier) => { subject_key_identifier.serialize(serializer) } } } } impl<'de> Deserialize<'de> for SignerIdentifier { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SignerIdentifier; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded SpcLink") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, SignerIdentifier, "a choice tag"); let signer_identifier = if tag_peeker.next_tag.class() == TagClass::ContextSpecific && tag_peeker.next_tag.number() == 0 { SignerIdentifier::SubjectKeyIdentifier(seq_next_element!( seq, ImplicitContextTag0, SignerIdentifier, "SubjectKeyIdentifier" )) } else { SignerIdentifier::IssuerAndSerialNumber(seq_next_element!( seq, IssuerAndSerialNumber, "IssuerAndSerialNumber" )) }; Ok(signer_identifier) } } deserializer.deserialize_enum( "SignerIdentifier", &["SubjectKeyIdentifier, IssuerAndSerialNumber"], Visitor, ) } } /// [RFC 5652 #5.3](https://datatracker.ietf.org/doc/html/rfc5652#section-5.3) /// ``` not_rust /// SignatureValue ::= OCTET STRING /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct SignatureValue(pub OctetStringAsn1); /// [RFC 5652 #10.1.1](https://datatracker.ietf.org/doc/html/rfc5652#section-10.1.1) /// ``` not_rust /// DigestAlgorithmIdentifier ::= AlgorithmIdentifier /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct DigestAlgorithmIdentifier(pub AlgorithmIdentifier); /// [RFC 5652 #10.1.2](https://datatracker.ietf.org/doc/html/rfc5652#section-10.1.2) /// ``` not_rust /// SignatureAlgorithmIdentifier ::= AlgorithmIdentifier /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct SignatureAlgorithmIdentifier(pub AlgorithmIdentifier); /// [RFC 5652 #10.2.4](https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.4) /// ``` not_rust /// IssuerAndSerialNumber ::= SEQUENCE { /// issuer Name, /// serialNumber CertificateSerialNumber } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct IssuerAndSerialNumber { pub issuer: Name, pub serial_number: CertificateSerialNumber, } /// [RFC 5652 #10.2.4](https://datatracker.ietf.org/doc/html/rfc5652#section-10.2.4) /// ``` not_rust /// CertificateSerialNumber ::= INTEGER /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct CertificateSerialNumber(pub IntegerAsn1); #[derive(Deserialize, Debug, PartialEq, Clone, Default)] pub struct UnsignedAttributes(pub Vec); // This is a workaround for constructed encoding as implicit impl ser::Serialize for UnsignedAttributes { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { let mut raw_der = picky_asn1_der::to_vec(&self.0).unwrap_or_else(|_| vec![0]); raw_der[0] = Tag::context_specific_constructed(1).inner(); picky_asn1_der::Asn1RawDer(raw_der).serialize(serializer) } } #[derive(Serialize, Debug, PartialEq, Clone)] pub struct UnsignedAttribute { pub ty: ObjectIdentifierAsn1, pub value: UnsignedAttributeValue, } impl<'de> de::Deserialize<'de> for UnsignedAttribute { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = UnsignedAttribute; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded ContentInfo") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let ty: ObjectIdentifierAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; let value = match Into::::into(&ty.0).as_str() { oids::MS_COUNTER_SIGN => UnsignedAttributeValue::MsCounterSign(seq_next_element!( seq, Asn1SetOf, UnsignedAttributeValue, "McCounterSign" )), oids::COUNTER_SIGN => UnsignedAttributeValue::CounterSign(seq_next_element!( seq, Asn1SetOf, UnsignedAttributeValue, "CounterSign" )), _ => { return Err(serde_invalid_value!( UnsignedAttributeValue, "unknown oid type", "MS_COUNTER_SIGN or CounterSign oid" )) } }; Ok(UnsignedAttribute { ty, value }) } } deserializer.deserialize_seq(Visitor) } } #[derive(Debug, PartialEq, Clone)] pub enum UnsignedAttributeValue { MsCounterSign(Asn1SetOf), CounterSign(Asn1SetOf), } impl Serialize for UnsignedAttributeValue { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { UnsignedAttributeValue::MsCounterSign(ms_counter_sign) => ms_counter_sign.serialize(serializer), UnsignedAttributeValue::CounterSign(counter_sign) => counter_sign.serialize(serializer), } } } #[cfg(test)] mod tests { use super::*; use base64::{engine::general_purpose, Engine as _}; #[cfg(feature = "ctl")] #[test] fn decode_certificate_trust_list_signer_info() { let decoded = general_purpose::STANDARD .decode( "MIICngIBATCBmTCBgTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x\ EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv\ bjErMCkGA1UEAxMiTWljcm9zb2Z0IENlcnRpZmljYXRlIExpc3QgQ0EgMjAxMQIT\ MwAAAFajs3kCOFJzBAAAAAAAVjANBglghkgBZQMEAgEFAKCB2jAYBgkqhkiG9w0B\ CQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCDKbAY82LhZRyLtnnizMz42\ OJp0yEyTg/jBC9lXDMyatTCBjAYKKwYBBAGCNwIBDDF+MHygVoBUAE0AaQBjAHIA\ bwBzAG8AZgB0ACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAVAByAHUAcwB0ACAA\ TABpAHMAdAAgAFAAdQBiAGwAaQBzAGgAZQByoSKAIGh0dHA6Ly93d3cubWljcm9z\ b2Z0LmNvbS93aW5kb3dzMA0GCSqGSIb3DQEBAQUABIIBAJolH27b3wLNu+E2Gh+B\ 9FFUsp5eiF1AGyUQb6hcjoYJIUjQgqW1shr+P4z9MI0ziTVWc1qVYh8LgXBAcuzN\ pGu7spEFIckf40eITNeB5KUZFtHWym+MUIQERfs/C+iqCiSgtSiWxUIci7h/VF39\ vhRTABMyZQddozLldJMsawRIhlceaOCTrp9tLQLLHHkEVDHSMOkbd4S9IOhw/YY9\ cwcGic2ebDrpSZe0VVEgF9Blqk49W+JRwADVNdWFcDZbiAQv63vSy+VdFzKZer07\ JAVDdVamvS5pk4MvNkszAG2KHsij6J3M97KcJY0FKuhPsfb9pnR61nmfDaFzoHOY\ pkw=", ) .unwrap(); let signer_info: SignerInfo = picky_asn1_der::from_bytes(&decoded).unwrap(); check_serde!(signer_info: SignerInfo in decoded); } } picky-asn1-x509-0.10.0/src/pkcs7/timestamp.rs000064400000000000000000000032670072674642500166130ustar 00000000000000use crate::pkcs7::content_info::EncapsulatedContentInfo; use picky_asn1::wrapper::ObjectIdentifierAsn1; use serde::{Deserialize, Serialize}; /// ``` not_rust /// [Time Stamping Authenticode Signatures](https://docs.microsoft.com/en-us/windows/win32/seccrypto/time-stamping-authenticode-signatures) /// TimeStampRequest ::= SEQUENCE { /// countersignatureType OBJECT IDENTIFIER, /// attributes Attributes OPTIONAL, /// content ContentInfo /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct TimestampRequest { pub countersignature_type: ObjectIdentifierAsn1, // MSDN: No attributes are currently included in the time stamp request. // attributes pub content: EncapsulatedContentInfo, } #[cfg(test)] mod tests { use super::*; use base64::{engine::general_purpose, Engine as _}; #[test] fn decode_timestamp_request() { let decoded = general_purpose::STANDARD .decode( "MIIBIwYKKwYBBAGCNwMCATCCARMGCSqGSIb3DQEHAaCCAQQEggEAQrCUqwV+9+Fl\ bgfJUwua28rmolLOZf/d3alzHUu6P1iV/9crZeu9ShrFdmQ4ZVWTpcR7bcVGPVsW\ QdUgx2n1mJCPied8YPjzC0wfJPvzZzOz9X919EAFRUi4VPs5qEsHJV57YP5mJ2UC\ XqXKSR9HhRO/06TSGz7hkFh+vpsKYVvIZpDXNJPRUilgEDQXjHCdMZyOzPb9wO8k\ cFHbUYZT9lVp9p8Wg+P56RdOANgtS5GKfku2BTsgbwxh5k8GzMnsiaf++O8LgMaE\ zsvEwbfbi+Egxi+7An0T7EttZcn6vbS28vtGKXOg6uzaiBN2u2KYq7f3KTq32sgD\ QgPEuhsAhQ==", ) .unwrap(); let timestamp_request: TimestampRequest = picky_asn1_der::from_bytes(&decoded).unwrap(); check_serde!(timestamp_request: TimestampRequest in decoded); } } picky-asn1-x509-0.10.0/src/pkcs7.rs000064400000000000000000001260710072674642500146070ustar 00000000000000pub mod cmsversion; pub mod content_info; pub mod crls; #[cfg(feature = "ctl")] pub mod ctl; pub mod signed_data; pub mod signer_info; pub mod timestamp; use crate::oids; use signed_data::SignedData; use picky_asn1::wrapper::{ExplicitContextTag0, ObjectIdentifierAsn1}; use serde::{de, Serialize}; #[derive(Serialize, Debug, PartialEq, Clone)] pub struct Pkcs7Certificate { pub oid: ObjectIdentifierAsn1, pub signed_data: ExplicitContextTag0, } impl<'de> de::Deserialize<'de> for Pkcs7Certificate { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { use std::fmt; struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Pkcs7Certificate; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded pcks7 certificate") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let oid: ObjectIdentifierAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; let signed_data: ExplicitContextTag0 = match Into::::into(&oid.0).as_str() { oids::SIGNED_DATA => seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?, _ => { return Err(serde_invalid_value!( Pkcs7Certificate, "unknown oid type", "SignedData oid" )) } }; Ok(Pkcs7Certificate { oid, signed_data }) } } deserializer.deserialize_seq(Visitor) } } #[cfg(test)] mod tests { use super::Pkcs7Certificate; use crate::pkcs7::signer_info::UnsignedAttributeValue; use crate::{Attribute, AttributeValues}; use base64::{engine::general_purpose, Engine as _}; use picky_asn1::wrapper::{Asn1SetOf, OctetStringAsn1}; #[test] fn full_pkcs7_decode() { let decoded = general_purpose::STANDARD .decode( "MIIF1AYJKoZIhvcNAQcCoIIFxTCCBcECAQExADALBgkqhkiG9w0BBwGgggWnMIIF\ ozCCA4ugAwIBAgIUXp0J4CZ0ZSsXBh952AQo0XUudpwwDQYJKoZIhvcNAQELBQAw\ YTELMAkGA1UEBhMCQVIxCzAJBgNVBAgMAkFSMQswCQYDVQQHDAJBUjELMAkGA1UE\ CgwCQVIxCzAJBgNVBAsMAkFSMQswCQYDVQQDDAJBUjERMA8GCSqGSIb3DQEJARYC\ QVIwHhcNMjEwNDE5MTEzMzEzWhcNMjIwNDE5MTEzMzEzWjBhMQswCQYDVQQGEwJB\ UjELMAkGA1UECAwCQVIxCzAJBgNVBAcMAkFSMQswCQYDVQQKDAJBUjELMAkGA1UE\ CwwCQVIxCzAJBgNVBAMMAkFSMREwDwYJKoZIhvcNAQkBFgJBUjCCAiIwDQYJKoZI\ hvcNAQEBBQADggIPADCCAgoCggIBAK96+zZ3Ik9K9yqHCz5uTMLAAEKCGo7IjBzc\ KDY4DlhfSJ1N6MC2US/QBCpLQprLVw0JToMgBt0ErHhLzuACXnpZk6lPqaXruv5+\ h6bRU3nVcEgkinShGTtybEDHCjbRBODg5aMbsb4vFSzVdk7PijqlUXVn1/1ke3KZ\ GGYQ/EKweqElpOkjnrawP98gQqVS2wJO++B5DmaEYbe3ketnfv/pPNyQzehyjrsf\ 3jO/5hsRRxHwc6IgsWajRxU12bx3fBqs5CWqe4sZCfJIfpNLkDeE5cvz36P9BLr3\ s4nKGdDAhMUOYZh6pqX9uZoq3Hm5a76HglY0SpmqOYums97dVcVMxbkMCPPawd+q\ jz4izc4kEWhDMal3rKr8QqaoFS6ez2hUsUoJW9fPfgiLAArfXLvpWRZCuEGWjWAq\ US/Kzfc3SvOI4095++IvLxTuvTw+iaEi0J4BLzFBnZs8lUBENJI+zYnhwEhwU8/V\ vSpjWmvly0RtbCiImFtYpd0y2/TlaJ4jupdR9//8gb21W/nKNzRrlYzVhYfdM+BP\ itNzeHKQHNzfy18LHBRpvqlp/nV3KhxTuEe/CvIsFS5xRtTmUICBwBC4ycq8cV/6\ Ek4FQTCYo08VQ9F68qX9iVAk+ajaRr3cE6+oX+aXIRx6GZ2KkC/NWcLnjOPy2flR\ y1lBxUmfAgMBAAGjUzBRMB0GA1UdDgQWBBRxxrWG5tXUbtOytggfIuTu/sP/4TAf\ BgNVHSMEGDAWgBRxxrWG5tXUbtOytggfIuTu/sP/4TAPBgNVHRMBAf8EBTADAQH/\ MA0GCSqGSIb3DQEBCwUAA4ICAQBZ5AyGS/U5Fqo5MpMvTlIuEXKDL4tTFyFXvBg6\ iowN9NylLeH2xyD5Deb2EOjoAJTnrhtCNmyOh3JmPpyFMN2S0LdzGeQS5idHlCsT\ PZpj0qJp+iexDS0syrYX/9/+L+5cR1LBVXEoK3kF0ZpSHUOxZkIx11zHjKohs+FF\ Hanhmx7cyVdDhBwtBcQfR41al6idt155xyhuYVTyXi0XSKr3YgBWXzjKnfrxsEe0\ 7Zo18ZtMe0p42yYwEhQaPL0UQkuSC7hAilOO3YWQ51Vnk3QJ7kw+EEqed6LNuAsS\ Qt8h4F7fiVuO4UG5CToDwK9bEw4zfQ8Xrm+6rwy/3CWC8L/YZ8Paj89+2JB3woIv\ F+6QvKTPpQ0arM4dI82nsyHSdt2bxXkLJ7iZ/D1vJZkWzBrpvuTmeHAKiFIj2hfJ\ 5FruZrC/60BKrbx4FAniXinNSzk43l4Q42JD6zGkex7mxXURkxqbbz6TAqSmbwgp\ ygxNWPIKIvldXq1yeNt9lPfHANqd+dCrpnkNCXVwaFbDqTaltYdaB4zg9HlzvlBK\ Eh49eilHGchkyMBawo80ctWy9UNH/Yt3uiwuga0Q2sCLlrbPxE5Ro3Ou/SZF9YtZ\ Ee/Xdsl0pUmdAylBzp08AJWCuPheE7XjrnfDlPz4BRuiB+qOMDO/ngLNZ0rFbiIV\ 3ojRzKEAMQA=", ) .unwrap(); let pkcs7: Pkcs7Certificate = picky_asn1_der::from_bytes(decoded.as_slice()).unwrap(); check_serde!(pkcs7: Pkcs7Certificate in decoded); } #[test] fn full_pkcs7_decode_with_ca_root() { let decoded = general_purpose::STANDARD .decode( "MIIIwgYJKoZIhvcNAQcCoIIIszCCCK8CAQExADALBgkqhkiG9w0BBwGgggiVMIIE\ nDCCAoQCFGe148Dqygm4BCpH70eVHP64Kf3zMA0GCSqGSIb3DQEBCwUAMIGHMQsw\ CQYDVQQGEwJUWTELMAkGA1UECAwCVFkxETAPBgNVBAcMCFNvbWVDaXR5MRQwEgYD\ VQQKDAtTb21lQ29tcGFueTERMA8GA1UECwwIU29tZVVuaXQxDzANBgNVBAMMBk15\ TmFtZTEeMBwGCSqGSIb3DQEJARYPbXltYWlsQG1haWwuY29tMB4XDTIxMDQxOTEy\ NDgwMloXDTI0MDExNDEyNDgwMlowgYwxCzAJBgNVBAYTAlRZMQswCQYDVQQIDAJU\ WTERMA8GA1UEBwwIU29tZUNpdHkxGTAXBgNVBAoMEFNvbWVPcmdhbml6YXRpb24x\ ETAPBgNVBAsMCFNvbWVVbml0MQ8wDQYDVQQDDAZNeU5hbWUxHjAcBgkqhkiG9w0B\ CQEWD215bWFpbEBtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\ ggEBAJ3PyrdEwqN4SBK/1RIkH9s0MSJ+qWLvgPWozvWgOmK1peYWEpcaR/Kph8rq\ rPrePLXX6ZM7Oyy/rtf1A521hNdPAvNbJ/Dhxou/ivavqoLoc6cMhM/0LFKrN0en\ BCwfC1XKOF3F+LgkitKtbF9iWGbt4Pp5GtrEjjCdwHzF/5tmsq+yzcnQGTiX24BA\ pvhlHuXpcLvBEDwNXJ2Tj812hJO1gZ8iOyIKY8BMBygRLp2pE3z/9w1E0gF03Y3C\ N4ts4VDi/pFstxHRfSX7V7TdLxcm8CsZhmbxYzKOG95TORwY9q2nGQcRKAzyHd5w\ a25ri/LbuHaz2LnUQrLYMXOHag0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAGaKl\ 8Bq5M6BIU8gYXIx1kRE/WOMonUAwn6agoqeRVROiHz0XSkSArUM76WZ98Ndvbbvj\ 4p+yCMeiX12zro0qj1hxxBF6cYxRxutukJjU/OjTCR7jR57QHVVHzjBDW2PQoHIE\ fX399yr2IILzAeI8xvLx8v2y+o7vfCWW5Sd8x7puww1FGNA/QqKBu/kw4NobybwS\ j+SC38qfBQQsKghbuJbxpwLuY3yixqwPV8Swlzc1PrAwtA+RWabQDHjShnTBppqu\ XNTFhGbPDdTHzECnRxg9cQbqSpiOkdnxEpagy8c7ccCwvHjD2SQGr44ydHg5FYPB\ k2wXKc8EFtmR4kyWg1zYjuboY0/0iaUkyWOZYy6NZE/ktwZKR02gXoN1I3YSsbw/\ fytr4ldkqq6bkpgMK/60CKiQvI8tdxcnQejeSlJfYqzptIlnsPH8X1x/kfQ8dWFj\ YIyxvRDe+26/1wOXodgTNwrn02FzNEcxqyOLL9YzYvbq1UiNi2n6CBaAJKdU7NhE\ dnzb81P4uOfs0QbsWGkVE3T9NzRlJlEPjei+srUZDvFYDjTTo2rTITOPDLPzSJlE\ UfxVV3uaRHc8Z07oTiaW2H/eizOwwBLbgVRKMy74dk4wC/3P1CSwyd4Z+c/l2LZ5\ 8Z2LMjIFw/eVYCfAiOmS/xyGqYZoXNXVZlp2/UswggPxMIIC2aADAgECAhQY0ZCe\ SXAknAwNvZQgZvONNLI/xjANBgkqhkiG9w0BAQsFADCBhzELMAkGA1UEBhMCVFkx\ CzAJBgNVBAgMAlJGMREwDwYDVQQHDAhTb21lQ2l0eTEUMBIGA1UECgwLU29tZUNv\ bXBhbnkxETAPBgNVBAsMCFNvbWVVbml0MQ8wDQYDVQQDDAZNeU5hbWUxHjAcBgkq\ hkiG9w0BCQEWD215bWFpbEBtYWlsLmNvbTAeFw0yMTA0MTkxMjI4MzNaFw0yNDA0\ MDkxMjI4MzNaMIGHMQswCQYDVQQGEwJUWTELMAkGA1UECAwCUkYxETAPBgNVBAcM\ CFNvbWVDaXR5MRQwEgYDVQQKDAtTb21lQ29tcGFueTERMA8GA1UECwwIU29tZVVu\ aXQxDzANBgNVBAMMBk15TmFtZTEeMBwGCSqGSIb3DQEJARYPbXltYWlsQG1haWwu\ Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1a+nyoNA6hkFplgN\ ajApR5lITmhxGJGYvrm3w4fYTqQ+eGr9PxQsEfhQQqy8+nOyCSRL9FJ2YwGdwrPZ\ KjJc1RIVWpCk+vxbm04C8PMlDiygpeD9l7tfZDTdJD4npRvHlltUSbK69/j0djC+\ aJr+DMT3h2fU/9mDDfVaKB30Q0mwOdmtLGcOAXddN9AJBVP9b6GekAE7jKC037jK\ UUrA3h5bw0rvic+jvtKnf1rsh5VYfHelJnxRnZ/XBijy99fZRf260i8gzp0+/OSg\ 39ggjOPlrGcPpLPcHShMlTK553GmO64T7IgBtmH8LdG/XInkcRw0oZ6BK5lUCSPp\ UQ4TfQIDAQABo1MwUTAdBgNVHQ4EFgQUznGR6rk3Nzi+3z80yteN8IPI3TYwHwYD\ VR0jBBgwFoAUznGR6rk3Nzi+3z80yteN8IPI3TYwDwYDVR0TAQH/BAUwAwEB/zAN\ BgkqhkiG9w0BAQsFAAOCAQEAjcK4L3VqsdUHmAyL9VBxBK4msWOzKMStUJ0d9+j6\ LLMMo39/0RLqvwiREP1JELCzWdKCrMKRtKbQh/e7dQoFR4zWezJ5ChtKRqAlUdVt\ m7yr61Ua0ftpkJtcb5+b8vP+cruAvnrjpW8VQNOkbce+VjOjl287FdfliZgpTCee\ 5UPxb2USETPoTohJOPpE27w6/1Ztb8AUgDzjZwd50Ty1ci6SBeFsD7YBgnSZO9ze\ S8zhKeY18ivsny0RKPO28/fO7rhExogn4sg12H6kaM3NokmDUf8FVy/Np0LCFreU\ cjZ0Bdoi73yZiQcNp8lb1Hm5jJbq2Oa1aY88KZ1Hdyx+jqEAMQA=", ) .unwrap(); let pkcs7: Pkcs7Certificate = picky_asn1_der::from_bytes(decoded.as_slice()).unwrap(); check_serde!(pkcs7: Pkcs7Certificate in decoded); } #[test] fn decode_pkcs7_with_timestamp() { let decoded = general_purpose::STANDARD .decode( "MIIkWwYJKoZIhvcNAQcCoIIkTDCCJEgCAQExDzANBglghkgBZQMEAgEFADB5Bgor\ BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG\ KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD+xe8u4YoS6UEO\ jtW70wceL89huvuluOvdcbeefpOXLqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX\ chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD\ VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy\ b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p\ bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw\ CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u\ ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy\ b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\ AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB\ znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH\ sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d\ weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ\ itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV\ Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE\ AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw\ UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1\ ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu\ ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu\ bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w\ Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3\ Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx\ MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy\ S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K\ NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV\ BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr\ qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx\ zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe\ yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g\ yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf\ AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI\ 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5\ GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea\ jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS\ AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK\ V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0\ IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0\ ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla\ MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS\ ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT\ H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB\ AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG\ OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S\ 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz\ y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7\ 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u\ M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33\ X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl\ XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP\ 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB\ l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF\ RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM\ CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ\ BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud\ DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO\ 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0\ LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y\ Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p\ Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y\ Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB\ FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw\ cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA\ XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY\ 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj\ 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd\ d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ\ Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf\ wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ\ aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j\ NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B\ xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96\ eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7\ r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I\ RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIWMDCCFiwCAQEwgZUwfjELMAkG\ A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx\ HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z\ b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN\ BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor\ BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgCZ2K5xbK\ 27tyibqtMV5AHpyNN7lNy3nCNEZ+gshCPtAwQgYKKwYBBAGCNwIBDDE0MDKgFIAS\ AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN\ BgkqhkiG9w0BAQEFAASCAQAyeEIPHCeezdvuJlvHLPAx0mBb2+36yOGP5QAIT6x/\ eMCsK3QEYylFlfSjDsBWHUFweyharjwu/sw/KDZcbfwz82aaOpkxbuAhPjHRX84S\ pqHxNxw7PHq6N6XOoe61YLEa93OF3A9WACsenIrjBssA6IhSr/Q/oVR78MKxh6VH\ qGAthEaOm/NKb8c0DmhUiHKtZMy05oNYDMkYcCyehhCBLf9/nehabRuHk8vMf0AF\ 5WsJQaUvARJRhXJamv1Mr9WVKXhW+nBCmauazkJIagJNuakh1ype4/gNAKTit4H5\ MghO2ERzx1XVg1Kvrsu87VdPXBHf+remb0JvXcYQyXndoYITujCCE7YGCisGAQQB\ gjcDAwExghOmMIITogYJKoZIhvcNAQcCoIITkzCCE48CAQMxDzANBglghkgBZQME\ AgEFADCCAVgGCyqGSIb3DQEJEAEEoIIBRwSCAUMwggE/AgEBBgorBgEEAYRZCgMB\ MDEwDQYJYIZIAWUDBAIBBQAEIAMc3Z52CqtJPqjV3aVgo0scv2C5S/V6jiQGZXcy\ SupuAgZeoMfc5PYYEzIwMjAwNDI0MDExNzQ0LjI2NlowBwIBAYACAfSggdSkgdEw\ gc4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS\ ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsT\ IE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFs\ ZXMgVFNTIEVTTjo3MjhELUM0NUYtRjlFQjElMCMGA1UEAxMcTWljcm9zb2Z0IFRp\ bWUtU3RhbXAgU2VydmljZaCCDyIwggT1MIID3aADAgECAhMzAAABBAkBdQhYhy0p\ AAAAAAEEMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX\ YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg\ Q29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAy\ MDEwMB4XDTE5MDkwNjIwNDExOFoXDTIwMTIwNDIwNDExOFowgc4xCzAJBgNVBAYT\ AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD\ VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP\ cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo3\ MjhELUM0NUYtRjlFQjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy\ dmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMgtB6ARuhhmlpTh\ YPwWgmtO2oNVTTZyHgYQBc3GH/J1w6bhgTcgpNiZnGZe2kv1Abyg7ABSP6ekgpRh\ WpByx5gOeOxpllPXkCxpiMlKFFx++Rnxg0N1YFN2aAsVj9GRMWc3R6hPKtgFMHXU\ LPxji3fu6DTgjfOi2pih5r/O+cp1Oi8KvdT+8p5JlROk1/85nsTggE80CudP/Nhu\ iIrSvmDNKVmOMF3afUWUswVP6v6t9cGjSWG3GMGNZe8FB3VVOL+pNtCbRV83qhQt\ kyIyA8HvGaciAfrXZi/QD5C/vK7XcvoeHbizh7j5lXUD3PiH0ffqHvMp58lsU/Aj\ pqr5ZGcCAwEAAaOCARswggEXMB0GA1UdDgQWBBSY1V7fwkQaDhcBi/GZ08MisOia\ 6jAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEug\ SaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9N\ aWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsG\ AQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Rp\ bVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG\ CCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQA9FdSzd2l8NAMX17RFeWLhOqnO\ AgyXIjH8tdW1yA94Zdzyn8NeukcjyIL7/Pkj8R7KEtEUL0cfRnds6KITaPBXxlos\ z1i+kMhfd6d4kSgnPWm0qoA14fqxJUM6P5fZfWRGUrtkNJha6N8Id1Ciuyibq7K0\ 3EnTLgli3EX1LXlzBOyyyjM3hDGVxgPk9D7Bw5ikgVju+Yql+tXjjgG/oFw+WJvw\ BN7YunaRV06JKZwsYGPsOYA1qyc8VXBoyeKGFKhI2oThT/P7IM3hCxLNc4fix3sL\ aKe4NZta0rjdssY8Kz+Z4sr8T9daXSFa7kUpKVw5277+0QFCc6bkrHjlKB/lMIIG\ cTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE\ BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc\ BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0\ IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1\ WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu\ Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv\ cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC\ ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9p\ lGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEw\ WbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeG\ MoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJ\ UGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw\ 2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0C\ AwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ\ 80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8E\ BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2U\ kFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5j\ b20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmww\ WgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29m\ dC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYD\ VR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6\ Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYI\ KwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0\ AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9\ naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtR\ gkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzy\ mXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCf\ Mkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3D\ nKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs\ 9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110\ mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL\ 2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffI\ rE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxE\ PJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc\ 1bN+NR4Iuto229Nfj950iEkSoYIDsDCCApgCAQEwgf6hgdSkgdEwgc4xCzAJBgNV\ BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w\ HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29m\ dCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVT\ Tjo3MjhELUM0NUYtRjlFQjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg\ U2VydmljZaIlCgEBMAkGBSsOAwIaBQADFQCzRh5/R0jzKEyIVLZzGHgW3BUKfaCB\ 3jCB26SB2DCB1TELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO\ BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEp\ MCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJzAlBgNV\ BAsTHm5DaXBoZXIgTlRTIEVTTjo0REU5LTBDNUUtM0UwOTErMCkGA1UEAxMiTWlj\ cm9zb2Z0IFRpbWUgU291cmNlIE1hc3RlciBDbG9jazANBgkqhkiG9w0BAQUFAAIF\ AOJMR0MwIhgPMjAyMDA0MjQwMDU2MzVaGA8yMDIwMDQyNTAwNTYzNVowdzA9Bgor\ BgEEAYRZCgQBMS8wLTAKAgUA4kxHQwIBADAKAgEAAgIQsAIB/zAHAgEAAgIY9jAK\ AgUA4k2YwwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMBoAowCAIB\ AAIDFuNgoQowCAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IBAQCU62Zx3p4eDKjR\ X6SThnb5SUK7xq3KMucFnp+c0dUmGrX2yQLhXkhHloWkKw7mjEdjZrMc98gYZGqA\ Y0ziY9vEjIQWvsF9F2xENTDkTmPalXGX4M30fW4HjGpids9Ey5G6un4sOV78hzsD\ eI/p41S2KXuWRwBIpG0s/h/eA7Kn7mkWpBDVjVuk4t4xTCTR7z/ms+itrqCOuFcV\ HRhoWWkE+OZl/Din7GejZIuo0o1oKcFAIuEOG/WODi0uSZcLFpKLPfiK/BK2nFFv\ Gha8r9VK7hXGCCeIFUQ7cRGYAUuYjc5BsGLCaDeq6p6NK9/VwC1r9wmt2wzEdA/h\ a6OIafxiMYIC9TCCAvECAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh\ c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD\ b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw\ MTACEzMAAAEECQF1CFiHLSkAAAAAAQQwDQYJYIZIAWUDBAIBBQCgggEyMBoGCSqG\ SIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgHn2Eb60N7JNv\ 3A3vUOf5J/ZyYXxiwaHelyJ17ZCZDeYwgeIGCyqGSIb3DQEJEAIMMYHSMIHPMIHM\ MIGxBBSzRh5/R0jzKEyIVLZzGHgW3BUKfTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVT\ MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK\ ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l\ LVN0YW1wIFBDQSAyMDEwAhMzAAABBAkBdQhYhy0pAAAAAAEEMBYEFPnzEPMl8q6k\ HOyQFJu9m/ZL40eoMA0GCSqGSIb3DQEBCwUABIIBAIbZuY7fGpCJcVSCjgBYq8ny\ ZS18sH7qH0BTCqgcgR8ghDn1NZ4Y6bKDfBQkwfwDWNWVpwAex06skEbw/tdmv70b\ nIzcTGjatnE2UhTLhf5DkkIb909A6lcjHa8z1Jirp2CYS4XFchzBRUmTvbFUeARn\ ou5BeU6YgdUYlkGnJ9xD3loc6+HvRz09U9XxSZmy0tctC3jNMv92wSFi2kKcmt7i\ 9xzUxj4Ia+jA8G4zr6nZ1bKSIZ4PJ2AhgU0ZC6FFtrj5+7Q+xwaWiHHqyILB35/K\ k7qonnRYsNjUjEV6mMWyBU1uS4JTUWYJXNDXvi08PG0tf5gKtPAvt8NixqJE43w=", ) .unwrap(); let pkcs7: Pkcs7Certificate = picky_asn1_der::from_bytes(&decoded).unwrap(); let signer_info = pkcs7 .signed_data .signers_infos .0 .0 .first() .expect("One SignedInfo always is present"); let unsigned_attrs = signer_info.unsigned_attrs.0 .0.first().unwrap(); let mc_counter_sign = match &unsigned_attrs.value { UnsignedAttributeValue::MsCounterSign(mc_counter_sign) => mc_counter_sign, _ => unreachable!(), }; assert_ne!(mc_counter_sign.0.len(), 0); check_serde!(pkcs7: Pkcs7Certificate in decoded); } #[test] fn deserialize_problem_signature() { let decoded = general_purpose::STANDARD .decode( "MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor\ BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG\ KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBOlmcSb72K+wH5\ 7rgEoyM/xepQH0ZFeACdfeWgW6yh06CCDYEwggX/MIID56ADAgECAhMzAAABh3IX\ chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD\ VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy\ b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p\ bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw\ CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u\ ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy\ b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\ AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB\ znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH\ sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d\ weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ\ itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV\ Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE\ AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw\ UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1\ ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu\ ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu\ bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w\ Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3\ Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx\ MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy\ S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K\ NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV\ BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr\ qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx\ zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe\ yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g\ yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf\ AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI\ 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5\ GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea\ jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS\ AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK\ V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0\ IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0\ ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla\ MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS\ ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT\ H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB\ AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG\ OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S\ 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz\ y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7\ 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u\ M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33\ X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl\ XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP\ 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB\ l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF\ RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM\ CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ\ BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud\ DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO\ 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0\ LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y\ Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p\ Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y\ Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB\ FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw\ cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA\ XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY\ 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj\ 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd\ d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ\ Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf\ wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ\ aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j\ NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B\ xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96\ eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7\ r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I\ RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG\ A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx\ HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z\ b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN\ BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor\ BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgSI3mmyEc\ XjWLEpbhWFEEl6gPBJhjiWhxF4WcneiXnlYwQgYKKwYBBAGCNwIBDDE0MDKgFIAS\ AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN\ BgkqhkiG9w0BAQEFAASCAQCyr15gPEMGURRpVeQjtCEpn9waDuDlkW11PiBt2A/j\ PdbhN4JupkncXgZtKt29s1usM8p+bSTkao5bpeIEV5UEMxgbsaxUCipxNki+z7LW\ KmFzviTsUU1/CqSJ2EKZdhQENUtpmgOr0D/CHTbbAVSpiVcfQuZI8hWulziFVqRE\ 4xGCR/sKOfQ1DT2DiOwlbf6tmceD04QaDlioZ8SVXTEvlP36a5rv8tmyw9lkkBgV\ B824Xh0H8CrqajF+x9zR9CjBox4Y/bf3Oe1Pir6k5IT7ZEkSQ9XRJfaNNm42i/9h\ IUPesYs9gr0zXJdxlri7Y2PPkphB9JQ+k+wa20nxBBIDoYIS8TCCEu0GCisGAQQB\ gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME\ AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB\ MDEwDQYJYIZIAWUDBAIBBQAEIPeHx1THLsARquah0ml1x5Wutabkis4dsFKSE3WJ\ HwZlAgZfYPphXw0YEzIwMjAwOTIyMjIxOTUzLjI1NVowBIACAfSggdSkgdEwgc4x\ CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt\ b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p\ Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg\ VFNTIEVTTjowQTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt\ U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABJy9uo++RqBmoAAAA\ AAEnMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo\ aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y\ cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw\ MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT\ MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK\ ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy\ YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowQTU2\ LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj\ ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPgB3nERnk6fS40vvWeD\ 3HCgM9Ep4xTIQiPnJXE9E+HkZVtTsPemoOyhfNAyF95E/rUvXOVTUcJFL7Xb16jT\ KPXONsCWY8DCixSDIiid6xa30TiEWVcIZRwiDlcx29D467OTav5rA1G6TwAEY5rQ\ jhUHLrOoJgfJfakZq6IHjd+slI0/qlys7QIGakFk2OB6mh/ln/nS8G4kNRK6Do4g\ xDtnBSFLNfhsSZlRSMDJwFvrZ2FCkaoexd7rKlUNOAAScY411IEqQeI1PwfRm3aW\ bS8IvAfJPC2Ah2LrtP8sKn5faaU8epexje7vZfcZif/cbxgUKStJzqbdvTBNc93n\ /Z8CAwEAAaOCARswggEXMB0GA1UdDgQWBBTl9JZVgF85MSRbYlOJXbhY022V8jAf\ BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH\ hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU\ aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF\ BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0\ YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG\ AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAKyo180VXHBqVnjZwQy7NlzXbo2+W5\ qfHxR7ANV5RBkRkdGamkwUcDNL+DpHObFPJHa0oTeYKE0Zbl1MvvfS8RtGGdhGYG\ CJf+BPd/gBCs4+dkZdjvOzNyuVuDPGlqQ5f7HS7iuQ/cCyGHcHYJ0nXVewF2Lk+J\ lrWykHpTlLwPXmCpNR+gieItPi/UMF2RYTGwojW+yIVwNyMYnjFGUxEX5/DtJjRZ\ mg7PBHMrENN2DgO6wBelp4ptyH2KK2EsWT+8jFCuoKv+eJby0QD55LN5f8SrUPRn\ K86fh7aVOfCglQofo5ABZIGiDIrg4JsV4k6p0oBSIFOAcqRAhiH+1spCMIIGcTCC\ BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC\ VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV\ BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv\ b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN\ MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv\ bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0\ aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw\ DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0\ VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw\ RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe\ dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx\ Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G\ kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA\ AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7\ fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC\ AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX\ zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v\ cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI\ KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j\ b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g\ AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93\ d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB\ BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA\ bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh\ IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS\ +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK\ kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon\ /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi\ PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/\ fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII\ YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0\ cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a\ KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ\ cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+\ NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT\ AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD\ VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP\ cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow\ QTU2LUUzMjktNEQ0RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy\ dmljZaIjCgEBMAcGBSsOAwIaAxUAs5W4TmyDHMRM7iz6mgGojqvXHzOggYMwgYCk\ fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH\ UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD\ Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF\ AOMUsu8wIhgPMjAyMDA5MjIyMTI5MTlaGA8yMDIwMDkyMzIxMjkxOVowdzA9Bgor\ BgEEAYRZCgQBMS8wLTAKAgUA4xSy7wIBADAKAgEAAgIVPgIB/zAHAgEAAgIRtjAK\ AgUA4xYEbwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB\ AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAEMD4esQRMLwQdhk\ Co1zgvmclcwl3lYYpk1oMh1ndsU3+97Rt6FV3adS4Hezc/K94oQKjcxtMVzLzQhG\ agM6XlqB31VD8n2nxVuaWD1yp2jm/0IvfL9nFMHJRhgANMiBdHqvqNrd86c/Kryq\ sI0Ch0sOx9wg3BozzqQhmdNjf9c6MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC\ VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV\ BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp\ bWUtU3RhbXAgUENBIDIwMTACEzMAAAEnL26j75GoGagAAAAAAScwDQYJYIZIAWUD\ BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B\ CQQxIgQgcyC5Zi6T5dXlcj+V9kHGOarq/wFRtxNkp+J8JwTtAV0wgfoGCyqGSIb3\ DQEJEAIvMYHqMIHnMIHkMIG9BCAbkuhLEoYdahb/BUyVszO2VDi6kB3MSaof/+8u\ 7SM+IjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u\ MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp\ b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB\ Jy9uo++RqBmoAAAAAAEnMCIEIK4r6N3NISekswMCG1kSBJCCCePrlLDQWbMKz0wt\ Lj6CMA0GCSqGSIb3DQEBCwUABIIBAASNHnbCvOgFNv5zwj0UKuGscSrC0R2GxT2p\ H6E/QlYix36uklxd1YSqolAA30q/2BQg23N75wfA8chIgOMnaRslF9uk/oKxKHAK\ WezF5wx3Qoc08MJmgBQ+f/vkMUr05JIoSjgCVhlnQbO7S+aqV9ZFPDcO6IzlrmiA\ okZONeswosfnv1puWHRUhFJx6v3L1y+YKrRfhytDIIw1biSQ/VTO8Wnf06H0miJC\ 1VLKNa5p8Uwx4tsWz6RvIhztN/wvOo5yUoXR55DLKUMAp283TM4A3n6exf7iEb5N\ 4jvlHkA6au1Uan+buR92YRqCvyUjqSzSJZo7w3NwLUM6GdFUIY0=\ ", ) .unwrap(); let pkcs7: Pkcs7Certificate = picky_asn1_der::from_bytes(&decoded).unwrap(); let message_digest = Attribute { ty: crate::oids::message_digest().into(), value: AttributeValues::MessageDigest(Asn1SetOf(vec![OctetStringAsn1::from(decoded[3882..3914].to_vec())])), }; check_serde!(message_digest: Attribute in decoded[3865..3914]); check_serde!(pkcs7: Pkcs7Certificate in decoded); } } picky-asn1-x509-0.10.0/src/private_key_info.rs000064400000000000000000000655310072674642500171200ustar 00000000000000use crate::{oids, AlgorithmIdentifier, AlgorithmIdentifierParameters, EcParameters, EncapsulatedEcPoint}; use picky_asn1::tag::TagPeeker; use picky_asn1::wrapper::{ BitStringAsn1, ExplicitContextTag0, ExplicitContextTag1, IntegerAsn1, ObjectIdentifierAsn1, OctetStringAsn1, OctetStringAsn1Container, Optional, }; #[cfg(not(feature = "legacy"))] use serde::Deserialize; use serde::{de, ser, Serialize}; #[cfg(feature = "zeroize")] use zeroize::Zeroize; use oid::ObjectIdentifier; use picky_asn1::bit_string::BitString; use picky_asn1::Asn1Type; use std::fmt; /// When `PrivateKeyInfo` have this version specified, it should not have `public_key` field set. /// This version of `PrivateKeyInfo` mostly used to represent RSA and EC private keys. pub const PRIVATE_KEY_INFO_VERSION_1: u8 = 0; /// When `PrivateKeyInfo` have this version specified, it should have `public_key` field set. /// This version of `PrivateKeyInfo` mostly used to represent Ed25519 and Ed448 private keys and /// defined as `OneAsymmetricKey` in [RFC5958](https://tools.ietf.org/html/rfc5958#section-2). pub const PRIVATE_KEY_INFO_VERSION_2: u8 = 1; pub type EncapsulatedEcSecret = OctetStringAsn1; /// [Public-Key Cryptography Standards (PKCS) #8](https://tools.ietf.org/html/rfc5208#section-5) /// [Asymmetric Key Packages](https://tools.ietf.org/html/rfc5958#section-2) /// /// # Section 5 /// /// Private-key information shall have ASN.1 type `OneAsymmetricKey` (Backwards-compatible with /// `PrivateKeyInfo` from RFC5208): /// /// ```not_rust /// OneAsymmetricKey ::= SEQUENCE { /// version Version, /// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, /// privateKey PrivateKey, /// attributes [0] IMPLICIT Attributes OPTIONAL, /// ..., /// [[2: publicKey [1] PublicKey OPTIONAL ]], /// ... /// } /// /// Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) /// /// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier /// /// PrivateKey ::= OCTET STRING /// /// PublicKey ::= BIT STRING /// /// Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } /// ``` /// /// The fields of type `OneAsymmetricKey` have the following meanings: /// /// `version` identifies the version of OneAsymmetricKey. If publicKey /// is present, then version is set to v2(1) else version is set to v1(0). /// /// `privateKeyAlgorithm` identifies the private-key algorithm. One /// example of a private-key algorithm is PKCS #1's rsaEncryption. /// /// `privateKey` is an octet string whose contents are the value of the /// private key. The interpretation of the contents is defined in the /// registration of the private-key algorithm. For an RSA private /// key, for example, the contents are a BER encoding of a value of /// type RSAPrivateKey. /// /// `publicKey` is OPTIONAL. When present, it contains the public key /// encoded in a BIT STRING. The structure within the BIT STRING, if /// any, depends on the `privateKeyAlgorithm` /// /// `attributes` is a set of attributes. These are the extended /// information that is encrypted along with the private-key /// information. #[derive(Serialize, Debug, Clone, PartialEq, Eq)] pub struct PrivateKeyInfo { pub version: u8, pub private_key_algorithm: AlgorithmIdentifier, pub private_key: PrivateKeyValue, // -- attributes (not supported) pub public_key: Option>>, } impl PrivateKeyInfo { pub fn new_rsa_encryption( modulus: IntegerAsn1, public_exponent: IntegerAsn1, private_exponent: IntegerAsn1, primes: (IntegerAsn1, IntegerAsn1), exponents: (IntegerAsn1, IntegerAsn1), coefficient: IntegerAsn1, ) -> Self { let private_key = PrivateKeyValue::RSA( RsaPrivateKey { version: vec![0].into(), modulus, public_exponent, private_exponent, prime_1: primes.0, prime_2: primes.1, exponent_1: exponents.0, exponent_2: exponents.1, coefficient, } .into(), ); Self { version: PRIVATE_KEY_INFO_VERSION_1, private_key_algorithm: AlgorithmIdentifier::new_rsa_encryption(), private_key, public_key: None, } } /// Creates a new `PrivateKeyInfo` with the given `curve_oid` and `secret`. /// /// If `skip_optional_params` is `true`, the `parameters` field will be omitted from internal /// `ECPrivateKey` ASN.1 structure, reducing duplication. This information is still present in /// the `private_key_algorithm` field. pub fn new_ec_encryption( curve_oid: ObjectIdentifier, secret: impl Into, public_point: Option, skip_optional_params: bool, ) -> Self { let curve_oid: ObjectIdentifierAsn1 = curve_oid.into(); let secret: OctetStringAsn1 = secret.into(); let point: Option = public_point.map(Into::into); let parameters: ExplicitContextTag0> = (!skip_optional_params).then(|| curve_oid.clone().into()).into(); let public_point: ExplicitContextTag1> = point.into(); let private_key = PrivateKeyValue::EC( ECPrivateKey { version: vec![1].into(), private_key: secret, parameters: parameters.into(), public_key: public_point.into(), } .into(), ); Self { version: PRIVATE_KEY_INFO_VERSION_1, private_key_algorithm: AlgorithmIdentifier::new_elliptic_curve(curve_oid.into()), private_key, public_key: None, } } pub fn new_ed_encryption( algorithm: ObjectIdentifier, secret: impl Into, public_key: Option, ) -> Self { let secret: OctetStringAsn1 = secret.into(); let (version, public_key) = if let Some(public_key) = public_key { // If the public key is present, the version MUST be set to v2(1) ( PRIVATE_KEY_INFO_VERSION_2, Some(ExplicitContextTag1(Optional(public_key.into()))), ) } else { (PRIVATE_KEY_INFO_VERSION_1, None) }; Self { version, private_key_algorithm: AlgorithmIdentifier::new_unchecked(algorithm, AlgorithmIdentifierParameters::None), private_key: PrivateKeyValue::ED(secret.into()), public_key, } } } impl<'de> de::Deserialize<'de> for PrivateKeyInfo { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = PrivateKeyInfo; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded PrivateKeyInfo (pkcs8)") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let version = seq_next_element!(seq, PrivateKeyInfo, "version"); if version != 0 && version != 1 { return Err(serde_invalid_value!( PrivateKeyInfo, "unsupported version (valid versions: [v1(0), v2(1)])", "a supported PrivateKeyInfo" )); } let private_key_algorithm: AlgorithmIdentifier = seq_next_element!(seq, PrivateKeyInfo, "private key algorithm"); let private_key = if private_key_algorithm.is_a(oids::rsa_encryption()) { PrivateKeyValue::RSA(seq_next_element!(seq, PrivateKeyInfo, "rsa oid")) } else if matches!(private_key_algorithm.parameters(), AlgorithmIdentifierParameters::Ec(_)) { PrivateKeyValue::EC(seq_next_element!(seq, PrivateKeyInfo, "ec private key")) } else if private_key_algorithm.is_one_of([oids::ed25519(), oids::x25519()]) { PrivateKeyValue::ED(seq_next_element!(seq, PrivateKeyInfo, "curve25519 private key")) } else if private_key_algorithm.is_one_of([oids::ed448(), oids::x448()]) { PrivateKeyValue::ED(seq_next_element!(seq, PrivateKeyInfo, "curve448 private key")) } else { return Err(serde_invalid_value!( PrivateKeyInfo, "unsupported algorithm", "a supported algorithm" )); }; let mut last_tag = seq.next_element::()?; if let Some(tag) = &last_tag { if tag.next_tag != ExplicitContextTag1::::TAG { // We found attributes, we don't support them yet so skip them last_tag = seq.next_element::()?; } } // OneAssymmetricKey has a public key field, but it's optional let public_key = match last_tag { Some(tag) if tag.next_tag == ExplicitContextTag1::::TAG => { let public_key = seq_next_element!(seq, ExplicitContextTag1, PrivateKeyInfo, "BitStringAsn1"); Some(ExplicitContextTag1(Optional(public_key.0))) } _ => None, }; Ok(PrivateKeyInfo { version, private_key_algorithm, private_key, public_key, }) } } deserializer.deserialize_seq(Visitor) } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum PrivateKeyValue { RSA(OctetStringAsn1Container), EC(OctetStringAsn1Container), // Used by Ed25519, Ed448, X25519, and X448 keys ED(OctetStringAsn1Container), } impl ser::Serialize for PrivateKeyValue { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match self { PrivateKeyValue::RSA(rsa) => rsa.serialize(serializer), PrivateKeyValue::EC(ec) => ec.serialize(serializer), PrivateKeyValue::ED(ed) => ed.serialize(serializer), } } } #[cfg(feature = "zeroize")] impl Drop for PrivateKeyValue { fn drop(&mut self) { if let PrivateKeyValue::ED(ed) = self { ed.0 .0.zeroize() } } } /// [PKCS #1: RSA Cryptography Specifications Version 2.2](https://tools.ietf.org/html/rfc8017.html#appendix-A.1.2) /// /// # Section A.1.2 /// /// An RSA private key should be represented with the ASN.1 type RSAPrivateKey: /// /// ```not_rust /// RSAPrivateKey ::= SEQUENCE { /// version Version, /// modulus INTEGER, -- n /// publicExponent INTEGER, -- e /// privateExponent INTEGER, -- d /// prime1 INTEGER, -- p /// prime2 INTEGER, -- q /// exponent1 INTEGER, -- d mod (p-1) /// exponent2 INTEGER, -- d mod (q-1) /// coefficient INTEGER, -- (inverse of q) mod p /// otherPrimeInfos OtherPrimeInfos OPTIONAL /// } /// ``` #[derive(Serialize, Debug, Clone, PartialEq, Eq)] #[cfg_attr(not(feature = "legacy"), derive(Deserialize))] pub struct RsaPrivateKey { pub version: IntegerAsn1, pub modulus: IntegerAsn1, pub public_exponent: IntegerAsn1, pub private_exponent: IntegerAsn1, pub prime_1: IntegerAsn1, pub prime_2: IntegerAsn1, pub exponent_1: IntegerAsn1, pub exponent_2: IntegerAsn1, pub coefficient: IntegerAsn1, } #[cfg(feature = "zeroize")] impl Drop for RsaPrivateKey { fn drop(&mut self) { self.private_exponent.zeroize(); } } #[cfg(feature = "legacy")] impl<'de> de::Deserialize<'de> for RsaPrivateKey { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = RsaPrivateKey; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct RSAPrivateKey with 6 or 9 elements") } fn visit_seq(self, mut seq: V) -> Result where V: de::SeqAccess<'de>, { let version: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; let modulus: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?; let public_exponent: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?; let private_exponent: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?; let prime_1: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?; let prime_2: IntegerAsn1 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?; let (exponent_1, exponent_2, coefficient) = if let Some(exponent_1) = seq.next_element()? { let exponent_2 = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(7, &self))?; let coefficient = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(8, &self))?; (exponent_1, exponent_2, coefficient) } else { use num_bigint_dig::{BigUint, ModInverse}; // conversion to num_bigint_dig format BigUint let private_exponent = BigUint::from_bytes_be(private_exponent.as_unsigned_bytes_be()); let prime_1 = BigUint::from_bytes_be(prime_1.as_unsigned_bytes_be()); let prime_2 = BigUint::from_bytes_be(prime_2.as_unsigned_bytes_be()); let exponent_1 = &private_exponent % (&prime_1 - 1u8); let exponent_2 = &private_exponent % (&prime_2 - 1u8); let coefficient = prime_2 .mod_inverse(prime_1) .ok_or_else(|| { de::Error::invalid_value( de::Unexpected::Other("[RSAPrivateKey] no modular inverse for prime 1"), &"an invertible prime 1 value", ) })? .to_biguint() .ok_or_else(|| { de::Error::invalid_value( de::Unexpected::Other("[RSAPrivateKey] BigUint conversion failed"), &"a valid prime 1 value", ) })?; // conversion to IntegerAsn1 let exponent_1 = IntegerAsn1::from_bytes_be_unsigned(exponent_1.to_bytes_be()); let exponent_2 = IntegerAsn1::from_bytes_be_unsigned(exponent_2.to_bytes_be()); let coefficient = IntegerAsn1::from_bytes_be_unsigned(coefficient.to_bytes_be()); (exponent_1, exponent_2, coefficient) }; Ok(RsaPrivateKey { version, modulus, public_exponent, private_exponent, prime_1, prime_2, exponent_1, exponent_2, coefficient, }) } } deserializer.deserialize_seq(Visitor) } } impl RsaPrivateKey { #[deprecated(note = "field is now public")] pub fn modulus(&self) -> &IntegerAsn1 { &self.modulus } #[deprecated(note = "field is now public")] pub fn public_exponent(&self) -> &IntegerAsn1 { &self.public_exponent } #[deprecated(note = "field is now public")] pub fn private_exponent(&self) -> &IntegerAsn1 { &self.private_exponent } #[deprecated(note = "field is now public")] pub fn prime_1(&self) -> &IntegerAsn1 { &self.prime_1 } #[deprecated(note = "field is now public")] pub fn prime_2(&self) -> &IntegerAsn1 { &self.prime_2 } #[deprecated(note = "field is now public")] pub fn primes(&self) -> (&IntegerAsn1, &IntegerAsn1) { (&self.prime_1, &self.prime_2) } #[deprecated(note = "field is now public")] pub fn exponent_1(&self) -> &IntegerAsn1 { &self.exponent_1 } #[deprecated(note = "field is now public")] pub fn exponent_2(&self) -> &IntegerAsn1 { &self.exponent_2 } #[deprecated(note = "field is now public")] pub fn exponents(&self) -> (&IntegerAsn1, &IntegerAsn1) { (&self.exponent_1, &self.exponent_2) } #[deprecated(note = "field is now public")] pub fn coefficient(&self) -> &IntegerAsn1 { &self.coefficient } #[deprecated(note = "field is now public")] pub fn into_public_components(mut self) -> (IntegerAsn1, IntegerAsn1) { ( std::mem::take(&mut self.modulus), std::mem::take(&mut self.public_exponent), ) } } /// [Elliptic Curve Private Key Structure](https://datatracker.ietf.org/doc/html/rfc5915#section-3) /// /// EC private key information SHALL have ASN.1 type ECPrivateKey: /// /// ```not_rust /// ECPrivateKey ::= SEQUENCE { /// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), /// privateKey OCTET STRING, /// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, /// publicKey [1] BIT STRING OPTIONAL /// } /// ``` #[derive(Serialize, Debug, Clone, PartialEq, Eq)] pub struct ECPrivateKey { pub version: IntegerAsn1, pub private_key: EncapsulatedEcSecret, #[serde(skip_serializing_if = "Optional::is_default")] pub parameters: Optional>>, #[serde(skip_serializing_if = "Optional::is_default")] pub public_key: Optional>>, } impl<'de> serde::Deserialize<'de> for ECPrivateKey { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = ECPrivateKey; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded PrivateKeyInfo (pkcs8)") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let version: IntegerAsn1 = seq_next_element!(seq, IntegerAsn1, "IntegerAsn1"); if version.0 != [1] { return Err(serde_invalid_value!( ECPrivateKey, "ECPrivateKey's version is not 1", "ECPrivateKey's version equals to 1" )); } let private_key = seq_next_element!(seq, OctetStringAsn1, "OctetStringAsn1"); let mut ec_private_key = ECPrivateKey { version, private_key, parameters: Optional::default(), public_key: Optional::default(), }; let mut last_tag = seq.next_element::()?; if let Some(tag) = &last_tag { if tag.next_tag == ExplicitContextTag0::::TAG { let parameters = seq_next_element!(seq, ExplicitContextTag0, ECPrivateKey, "EcParameters"); ec_private_key.parameters = Optional(ExplicitContextTag0(Some(parameters.0))); // Query next tag, as we still could encounter public key tag last_tag = seq.next_element::()?; } } if let Some(tag) = last_tag { if tag.next_tag == ExplicitContextTag1::::TAG { let public_key = seq_next_element!(seq, ExplicitContextTag1, ECPrivateKey, "BitStringAsn1"); ec_private_key.public_key = Optional(ExplicitContextTag1(Some(public_key.0))); } } Ok(ec_private_key) } } deserializer.deserialize_seq(Visitor) } } #[cfg(feature = "zeroize")] impl Drop for ECPrivateKey { fn drop(&mut self) { self.private_key.zeroize(); } } #[cfg(test)] mod tests { use super::*; use base64::{engine::general_purpose, Engine as _}; use picky_asn1::bit_string::BitString; #[test] fn pkcs_8_private_key() { let encoded = general_purpose::STANDARD .decode( "MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAq7BFUpkGp3+LQmlQ\ Yx2eqzDV+xeG8kx/sQFV18S5JhzGeIJNA72wSeukEPojtqUyX2J0CciPBh7eqclQ\ 2zpAswIDAQABAkAgisq4+zRdrzkwH1ITV1vpytnkO/NiHcnePQiOW0VUybPyHoGM\ /jf75C5xET7ZQpBe5kx5VHsPZj0CBb3b+wSRAiEA2mPWCBytosIU/ODRfq6EiV04\ lt6waE7I2uSPqIC20LcCIQDJQYIHQII+3YaPqyhGgqMexuuuGx+lDKD6/Fu/JwPb\ 5QIhAKthiYcYKlL9h8bjDsQhZDUACPasjzdsDEdq8inDyLOFAiEAmCr/tZwA3qeA\ ZoBzI10DGPIuoKXBd3nk/eBxPkaxlEECIQCNymjsoI7GldtujVnr1qT+3yedLfHK\ srDVjIT3LsvTqw==", ) .expect("invalid base64"); let modulus = IntegerAsn1::from(encoded[35..100].to_vec()); let public_exponent = IntegerAsn1::from(encoded[102..105].to_vec()); let private_exponent = IntegerAsn1::from(encoded[107..171].to_vec()); let prime_1 = IntegerAsn1::from(encoded[173..206].to_vec()); let prime_2 = IntegerAsn1::from(encoded[208..241].to_vec()); let exponent_1 = IntegerAsn1::from(encoded[243..276].to_vec()); let exponent_2 = IntegerAsn1::from(encoded[278..311].to_vec()); let coefficient = IntegerAsn1::from(encoded[313..346].to_vec()); let private_key = PrivateKeyInfo::new_rsa_encryption( modulus, public_exponent, private_exponent, (prime_1, prime_2), (exponent_1, exponent_2), coefficient, ); check_serde!(private_key: PrivateKeyInfo in encoded); } #[test] #[cfg(feature = "legacy")] fn old_broken_key_legacy_support() { // Version previous to picky-asn1-x509 6.0.0 could generate weird keys with negative values // https://github.com/Devolutions/picky-rs/issues/53 // We want to support these for now. let encoded = general_purpose::STANDARD .decode( "MIIDMAIBADANBgkqhkiG9w0BAQEFAASCAxowggMWAgEAAoIBAOB9jOJvCkMHOc98Q\ GPFikxAvBKANkme5f/nNuNnEnbefoKDFkS6ElfqASAAkIHxUREnRvBTTa6b+qba/0\ DhBuXsYGCl8VF0pUE4JGujv1HIi5aRCar0WmY66s7DJ4uR3Nk9Jy0WeRiH4yyzEIG\ 8+6QDu4d/U6slWTmE8eZtQEE7rz4FGpQU9OhrGM3xJOIIbLX/xU2SFt83Xs3JREEt\ bfrXQpSxAHmtwvlBKpeZacrcobm6eQKsoI2MIg3LFvoHs0+40dadm14ngpgwx4qqk\ bG34jvWH13OhHRweFGNkQpcg99rlzZYkCM13e9EcmirQ9XYHuB5pHS31eznolZKbx\ cCAwEAAQKCAQCrPFlopxaGxk48jCR5dkbln0NWQWInigMazf06PHcDIPgTCXbE+cH\ gOWieRo/z7mTN1s3vpztMA0KQX9/wVzVx0Ho7fpiyb21WcEKnsIHRGk4PjZZ4Rmdm\ L27IRGg3uA1jz5fAdrHsGksY34Wp0MOJ+ibjViY2GAkVLOlvwMoQds6eNIGO88T5O\ fcmvutjK43ObU1vgx2ptTaLNAVczEE5VHqcLx4GZPv6k71afOQfIDQerIpsGb4gvr\ 1JdwYKb4z02z2SaNIA3Vly0q5s4r8uU36eg9z65utu93M7zI7f8/MX2byZ2Jz4b3T\ nH10FURmbPoNQH/O2T0TbtT4M1y0xAoGA72JW0IcFxze7j7PPaP6cQN1IXvFDZUFF\ dZHqFI8+4VPcv3EKTs+iQflM7pqtRuEWtwonIn3f7CGOx317uKwpVsZvfnDhXCUPJ\ Q3pns7KgaROGXyruFFQ9gl6XsXGK02Wop9nX0/iRK3ruwZ4uJwDioEYcvGw+ocqAc\ yOdodNnpUCgYDwEo/sPJNaPOzc7fpaQr2PUUJ3ksL0ncGRO2h1JGYgDtWe5u1srSI\ DlpM2NdYSZyT04ebOF2SqNUBwY3LB1tPOFnjYwCutp4c75OYhOor7TodZHlzt3GeQ\ ntUw6XbHX0ohTgs4u2NXwOTq5yKeW4VYzuevN5ksF8GoW2noalpn+w==", ) .unwrap(); picky_asn1_der::from_bytes::(&encoded).unwrap(); } #[test] fn decode_ec_key() { let decoded = general_purpose::STANDARD .decode( "\ MIHcAgEBBEIBhqphIGu2PmlcEb6xADhhSCpgPUulB0s4L2qOgolRgaBx4fNgINFE\ mBsSyHJncsWG8WFEuUzAYy/YKz2lP0Qx6Z2gBwYFK4EEACOhgYkDgYYABABwBevJ\ w/+Xh6I98ruzoTX3MNTsbgnc+glenJRCbEJkjbJrObFhbfgqP52r1lAy2RxuShGi\ NYJJzNPT6vR1abS32QFtvTH7YbYa6OWk9dtGNY/cYxgx1nQyhUuofdW7qbbfu/Ww\ TP2oFsPXRAavZCh4AbWUn8bAHmzNRyuJonQBKlQlVQ==", ) .unwrap(); let ec_key = ECPrivateKey { version: IntegerAsn1([1].into()), private_key: OctetStringAsn1::from(decoded[8..74].to_vec()), parameters: ExplicitContextTag0(Some(EcParameters::NamedCurve(oids::secp521r1().into()))).into(), public_key: Optional(ExplicitContextTag1(Some( BitString::with_bytes(decoded[90..].to_vec()).into(), ))), }; check_serde!(ec_key: ECPrivateKey in decoded); } #[test] fn decode_pkcs8_ec_key() { let decoded = general_purpose::STANDARD.decode("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKZqrmOg/cDZ4tPCn\ 4LROs145nxx+ssufvflL8cROxFmhRANCAARmU90fCSTsncefY7hVeKw1WIg/YQmT\ 4DGJ7nJPZ+WXAd/xxp4c0bHGlIOju/U95ITPN9dAmro7OUTDJpz+rzGW").unwrap(); let expected_pkcs8_ec_key = PrivateKeyInfo { version: 0, private_key_algorithm: AlgorithmIdentifier::new_elliptic_curve(EcParameters::NamedCurve( oids::secp256r1().into(), )), private_key: PrivateKeyValue::EC(OctetStringAsn1Container(ECPrivateKey { version: IntegerAsn1([1].into()), private_key: OctetStringAsn1(decoded[36..68].to_vec()), parameters: Optional(Default::default()), public_key: Optional(ExplicitContextTag1(Some( BitString::with_bytes(decoded[73..].to_vec()).into(), ))), })), public_key: None, }; check_serde!(expected_pkcs8_ec_key: PrivateKeyInfo in decoded); } } picky-asn1-x509-0.10.0/src/signature.rs000064400000000000000000000006250072674642500155550ustar 00000000000000use picky_asn1::wrapper::IntegerAsn1; use serde::{Deserialize, Serialize}; /// Defined in [RFC 3279](https://tools.ietf.org/html/rfc3279#section-2.2.3) /// /// ```not_rust /// Ecdsa-Sig-Value ::= SEQUENCE { /// r INTEGER, /// s INTEGER /// } /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct EcdsaSignatureValue { pub r: IntegerAsn1, pub s: IntegerAsn1, } picky-asn1-x509-0.10.0/src/subject_public_key_info.rs000064400000000000000000000177010072674642500204370ustar 00000000000000use crate::{oids, AlgorithmIdentifier, AlgorithmIdentifierParameters}; use oid::ObjectIdentifier; use picky_asn1::{ bit_string::BitString, wrapper::{BitStringAsn1, BitStringAsn1Container, IntegerAsn1, OctetStringAsn1}, }; use serde::{de, ser, Deserialize, Serialize}; use std::fmt; #[derive(Debug, PartialEq, Eq, Clone)] pub enum PublicKey { Rsa(EncapsulatedRsaPublicKey), Ec(EncapsulatedEcPoint), /// Used For Ed25519, Ed448, X25519, and X448 keys Ed(EncapsulatedEcPoint), } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct RsaPublicKey { pub modulus: IntegerAsn1, // n pub public_exponent: IntegerAsn1, // e } pub type EncapsulatedRsaPublicKey = BitStringAsn1Container; pub type EcPoint = OctetStringAsn1; pub type EncapsulatedEcPoint = BitStringAsn1; #[derive(Debug, PartialEq, Eq, Clone)] pub struct SubjectPublicKeyInfo { pub algorithm: AlgorithmIdentifier, pub subject_public_key: PublicKey, } impl SubjectPublicKeyInfo { pub fn new_rsa_key(modulus: IntegerAsn1, public_exponent: IntegerAsn1) -> Self { Self { algorithm: AlgorithmIdentifier::new_rsa_encryption(), subject_public_key: PublicKey::Rsa( RsaPublicKey { modulus, public_exponent, } .into(), ), } } pub fn new_ec_key(curve: ObjectIdentifier, point: BitString) -> Self { Self { algorithm: AlgorithmIdentifier::new_elliptic_curve(curve.into()), subject_public_key: PublicKey::Ec(point.into()), } } pub fn new_ed_key(curve: ObjectIdentifier, point: BitString) -> Self { Self { algorithm: AlgorithmIdentifier::new_unchecked(curve, AlgorithmIdentifierParameters::None), subject_public_key: PublicKey::Ed(point.into()), } } } impl ser::Serialize for SubjectPublicKeyInfo { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { use ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(2))?; seq.serialize_element(&self.algorithm)?; match &self.subject_public_key { PublicKey::Rsa(key) => seq.serialize_element(key)?, PublicKey::Ec(key) => seq.serialize_element(key)?, PublicKey::Ed(key) => seq.serialize_element(key)?, } seq.end() } } impl<'de> de::Deserialize<'de> for SubjectPublicKeyInfo { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = SubjectPublicKeyInfo; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded subject public key info") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let algorithm: AlgorithmIdentifier = seq_next_element!(seq, AlgorithmIdentifier, "algorithm oid"); let subject_public_key = match Into::::into(algorithm.oid()).as_str() { oids::RSA_ENCRYPTION => PublicKey::Rsa(seq_next_element!(seq, SubjectPublicKeyInfo, "rsa key")), oids::EC_PUBLIC_KEY => { PublicKey::Ec(seq_next_element!(seq, SubjectPublicKeyInfo, "elliptic curves key")) } oids::ED25519 | oids::X25519 => { PublicKey::Ed(seq_next_element!(seq, SubjectPublicKeyInfo, "curve25519 key")) } oids::ED448 | oids::X448 => { PublicKey::Ed(seq_next_element!(seq, SubjectPublicKeyInfo, "curve448 key")) } _ => { return Err(serde_invalid_value!( SubjectPublicKeyInfo, "unsupported algorithm (unknown oid)", "a supported algorithm" )); } }; Ok(SubjectPublicKeyInfo { algorithm, subject_public_key, }) } } deserializer.deserialize_seq(Visitor) } } #[cfg(test)] mod tests { use super::*; use base64::{engine::general_purpose, Engine as _}; use num_bigint_dig::BigInt; #[test] fn rsa_subject_public_key_info() { let encoded = general_purpose::STANDARD .decode( "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsiLoIx\ mXaZAFRBKtHYZhiF8m+pYR+xGIpupvsdDEvKO92D6fIccgVLIW6p6sSNk\ oXx5J6KDSMbA/chy5M6pRvJkaCXCI4zlCPMYvPhI8OxN3RYPfdQTLpgPy\ wrlfdn2CAum7o4D8nR4NJacB3NfPnS9tsJ2L3p5iHviuTB4xm03IKmPPq\ saJy+nXUFC1XS9E/PseVHRuNvKa7WmlwSZngQzKAVSIwqpgCc+oP1pKEe\ J0M3LHFo8ao5SuzhfXUIGrPnkUKEE3m7B0b8xXZfP1N6ELoonWDK+RMgY\ IBaZdgBhPfHxF8KfTHvSzcUzWZojuR+ynaFL9AJK+8RiXnB4CJwIDAQAB", ) .expect("invalid base64"); // RSA algorithm identifier let algorithm = AlgorithmIdentifier::new_rsa_encryption(); check_serde!(algorithm: AlgorithmIdentifier in encoded[4..19]); // RSA modulus and public exponent let modulus = IntegerAsn1::from_bytes_be_signed(vec![ 0x00, 0xb2, 0x22, 0xe8, 0x23, 0x19, 0x97, 0x69, 0x90, 0x5, 0x44, 0x12, 0xad, 0x1d, 0x86, 0x61, 0x88, 0x5f, 0x26, 0xfa, 0x96, 0x11, 0xfb, 0x11, 0x88, 0xa6, 0xea, 0x6f, 0xb1, 0xd0, 0xc4, 0xbc, 0xa3, 0xbd, 0xd8, 0x3e, 0x9f, 0x21, 0xc7, 0x20, 0x54, 0xb2, 0x16, 0xea, 0x9e, 0xac, 0x48, 0xd9, 0x28, 0x5f, 0x1e, 0x49, 0xe8, 0xa0, 0xd2, 0x31, 0xb0, 0x3f, 0x72, 0x1c, 0xb9, 0x33, 0xaa, 0x51, 0xbc, 0x99, 0x1a, 0x9, 0x70, 0x88, 0xe3, 0x39, 0x42, 0x3c, 0xc6, 0x2f, 0x3e, 0x12, 0x3c, 0x3b, 0x13, 0x77, 0x45, 0x83, 0xdf, 0x75, 0x4, 0xcb, 0xa6, 0x3, 0xf2, 0xc2, 0xb9, 0x5f, 0x76, 0x7d, 0x82, 0x2, 0xe9, 0xbb, 0xa3, 0x80, 0xfc, 0x9d, 0x1e, 0xd, 0x25, 0xa7, 0x1, 0xdc, 0xd7, 0xcf, 0x9d, 0x2f, 0x6d, 0xb0, 0x9d, 0x8b, 0xde, 0x9e, 0x62, 0x1e, 0xf8, 0xae, 0x4c, 0x1e, 0x31, 0x9b, 0x4d, 0xc8, 0x2a, 0x63, 0xcf, 0xaa, 0xc6, 0x89, 0xcb, 0xe9, 0xd7, 0x50, 0x50, 0xb5, 0x5d, 0x2f, 0x44, 0xfc, 0xfb, 0x1e, 0x54, 0x74, 0x6e, 0x36, 0xf2, 0x9a, 0xed, 0x69, 0xa5, 0xc1, 0x26, 0x67, 0x81, 0xc, 0xca, 0x1, 0x54, 0x88, 0xc2, 0xaa, 0x60, 0x9, 0xcf, 0xa8, 0x3f, 0x5a, 0x4a, 0x11, 0xe2, 0x74, 0x33, 0x72, 0xc7, 0x16, 0x8f, 0x1a, 0xa3, 0x94, 0xae, 0xce, 0x17, 0xd7, 0x50, 0x81, 0xab, 0x3e, 0x79, 0x14, 0x28, 0x41, 0x37, 0x9b, 0xb0, 0x74, 0x6f, 0xcc, 0x57, 0x65, 0xf3, 0xf5, 0x37, 0xa1, 0xb, 0xa2, 0x89, 0xd6, 0xc, 0xaf, 0x91, 0x32, 0x6, 0x8, 0x5, 0xa6, 0x5d, 0x80, 0x18, 0x4f, 0x7c, 0x7c, 0x45, 0xf0, 0xa7, 0xd3, 0x1e, 0xf4, 0xb3, 0x71, 0x4c, 0xd6, 0x66, 0x88, 0xee, 0x47, 0xec, 0xa7, 0x68, 0x52, 0xfd, 0x0, 0x92, 0xbe, 0xf1, 0x18, 0x97, 0x9c, 0x1e, 0x2, 0x27, ]); check_serde!(modulus: IntegerAsn1 in encoded[28..289]); let public_exponent: IntegerAsn1 = BigInt::from(65537).to_signed_bytes_be().into(); check_serde!(public_exponent: IntegerAsn1 in encoded[289..294]); // RSA public key let subject_public_key: EncapsulatedRsaPublicKey = RsaPublicKey { modulus, public_exponent, } .into(); check_serde!(subject_public_key: EncapsulatedRsaPublicKey in encoded[19..294]); // full encode / decode let info = SubjectPublicKeyInfo { algorithm, subject_public_key: PublicKey::Rsa(subject_public_key), }; check_serde!(info: SubjectPublicKeyInfo in encoded); } } picky-asn1-x509-0.10.0/src/validity.rs000064400000000000000000000050550072674642500154030ustar 00000000000000use picky_asn1::date::{GeneralizedTime, UTCTime}; use picky_asn1::tag::TagPeeker; use picky_asn1::wrapper::{GeneralizedTimeAsn1, UTCTimeAsn1}; use picky_asn1::Asn1Type; use serde::{de, ser, Deserialize, Serialize}; use std::fmt; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Validity { pub not_before: Time, pub not_after: Time, } #[derive(Debug, PartialEq, Eq, Clone)] pub enum Time { Utc(UTCTimeAsn1), Generalized(GeneralizedTimeAsn1), } impl From for Time { fn from(time: UTCTimeAsn1) -> Self { Self::Utc(time) } } impl From for Time { fn from(time: UTCTime) -> Self { Self::Utc(time.into()) } } impl From for Time { fn from(time: GeneralizedTimeAsn1) -> Self { Self::Generalized(time) } } impl From for Time { fn from(time: GeneralizedTime) -> Self { Self::Generalized(time.into()) } } impl ser::Serialize for Time { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: ser::Serializer, { match &self { Time::Utc(time) => time.serialize(serializer), Time::Generalized(time) => time.serialize(serializer), } } } impl<'de> de::Deserialize<'de> for Time { fn deserialize(deserializer: D) -> Result>::Error> where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Time; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid DER-encoded Time") } fn visit_seq(self, mut seq: A) -> Result where A: de::SeqAccess<'de>, { let tag_peeker: TagPeeker = seq_next_element!(seq, Time, "choice tag"); match tag_peeker.next_tag { UTCTimeAsn1::TAG => Ok(Time::Utc(seq_next_element!(seq, Time, "UTCTime"))), GeneralizedTimeAsn1::TAG => Ok(Time::Generalized(seq_next_element!(seq, Time, "GeneralizedTime"))), _ => Err(serde_invalid_value!( Time, "invalid variant", "either UTCTime or GeneralizedTime" )), } } } deserializer.deserialize_enum("Time", &["UTC", "Generalized"], Visitor) } } picky-asn1-x509-0.10.0/src/version.rs000064400000000000000000000070320072674642500152400ustar 00000000000000use serde::{de, ser, Deserialize, Serialize}; use std::fmt; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[repr(u8)] pub enum Version { V1 = 0x00, V2 = 0x01, V3 = 0x02, } impl Default for Version { fn default() -> Self { Self::V1 } } impl Version { pub fn from_u8(v: u8) -> Option { match v { 0x00 => Some(Self::V1), 0x01 => Some(Self::V2), 0x02 => Some(Self::V3), _ => None, } } } impl Serialize for Version { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { serializer.serialize_u8(*self as u8) } } impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Version; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a valid version number") } fn visit_u8(self, v: u8) -> Result where E: de::Error, { let version = Version::from_u8(v).ok_or_else(|| { E::invalid_value( de::Unexpected::Other("invalid version number"), &"a valid integer representing a supported version number (0, 1 or 2)", ) })?; Ok(version) } } deserializer.deserialize_u8(Visitor) } } #[cfg(test)] mod tests { use super::*; use base64::{engine::general_purpose, Engine as _}; use picky_asn1::wrapper::{ExplicitContextTag9, Optional}; use picky_asn1_der::Asn1DerError; #[derive(Serialize, Deserialize, Debug, PartialEq)] struct OptionalVersionTestStruct { #[serde(skip_serializing_if = "version_is_default")] version: Optional>, other_non_optional_integer: u8, } fn version_is_default(version: &Optional>) -> bool { (version.0).0 == Version::V1 } #[test] fn optional_version() { let buffer_with_version: [u8; 10] = [0x30, 0x08, 0xA9, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x6E]; let non_default = OptionalVersionTestStruct { version: ExplicitContextTag9(Version::V3).into(), other_non_optional_integer: 0x6E, }; check_serde!(non_default: OptionalVersionTestStruct in buffer_with_version); let buffer_without_version: [u8; 5] = [0x30, 0x03, 0x02, 0x01, 0x6E]; let default = OptionalVersionTestStruct { version: ExplicitContextTag9(Version::default()).into(), other_non_optional_integer: 0x6E, }; check_serde!(default: OptionalVersionTestStruct in buffer_without_version); } #[test] fn unsupported_version() { let buffer: [u8; 3] = [0x02, 0x01, 0x0F]; let version: picky_asn1_der::Result = picky_asn1_der::from_bytes(&buffer); match version { Err(Asn1DerError::Message(msg)) => assert_eq!( msg, "invalid value: invalid version number, expected a valid integer \ representing a supported version number (0, 1 or 2)" ), Err(err) => panic!("invalid error: {}", err), Ok(_) => panic!("parsing should have failed but did not"), } } }