From a592dfc1eb89e7ee94e4fa80dadf5944b9016ae5 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Thu, 3 Mar 2022 13:23:50 -0500 Subject: [PATCH 1/4] add support for CRLs, OCSP and certs-only SignedData messages. - The ocsp crate is new and features encoders/decoders for the OCSP protocol. - the pkcs7 crate now features a new module that provides a partial implementation of SignedData support. This is intended to provided parsing certs-only PKCS7 messages in support of dynamic path building in the new certval library. This adds a new dependency on the x509 crate. ASN.1 definitions of structures that were not implemented are included as comments. - The spki crate was modified because the signer_infos field in SignedData generated the need to add PartialOrd to AlgorithmIdentifier. - The x509 crate received a new crl module that features encoders/decoders for CRLs. The Rust version in Cargo.toml was modified to match README and sibling modules. A couple of escape characters were added to keyusage.rs to quiet warnings from cargo doc. Typos were fixed in oids.rs and new changed to new_unwrap. Certificate tests were modified to feature new_unwrap. Only a simple CRL parsing test is included here. The certval library more thoroughly exercises CRLs via executing PKITS for validation and revocation status determination. --- Cargo.lock | 11 + Cargo.toml | 1 + ocsp/Cargo.toml | 29 ++ ocsp/README.md | 60 +++ ocsp/src/lib.rs | 5 + ocsp/src/ocsp.rs | 463 ++++++++++++++++++ ocsp/tests/ocsp.rs | 296 +++++++++++ pkcs7/Cargo.toml | 1 + pkcs7/src/cryptographic_message_syntax2004.rs | 378 ++++++++++++++ pkcs7/src/lib.rs | 3 + .../tests/cryptographic_message_syntax2004.rs | 23 + .../DODJITCINTEROPERABILITYROOTCA2_IT.p7c | Bin 0 -> 1935 bytes pkcs7/tests/examples/DODROOTCA3_IB.p7c | Bin 0 -> 31371 bytes .../tests/examples/caCertsIssuedTofbcag4.p7c | Bin 0 -> 11536 bytes spki/src/algorithm.rs | 2 +- x509/Cargo.toml | 2 +- x509/src/crl.rs | 89 ++++ x509/src/ext/pkix/keyusage.rs | 4 +- x509/src/ext/pkix/oids.rs | 24 +- x509/src/lib.rs | 1 + x509/tests/certificate.rs | 5 +- x509/tests/crl.rs | 12 + 22 files changed, 1390 insertions(+), 19 deletions(-) create mode 100644 ocsp/Cargo.toml create mode 100644 ocsp/README.md create mode 100644 ocsp/src/lib.rs create mode 100644 ocsp/src/ocsp.rs create mode 100644 ocsp/tests/ocsp.rs create mode 100644 pkcs7/src/cryptographic_message_syntax2004.rs create mode 100644 pkcs7/tests/cryptographic_message_syntax2004.rs create mode 100644 pkcs7/tests/examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c create mode 100644 pkcs7/tests/examples/DODROOTCA3_IB.p7c create mode 100644 pkcs7/tests/examples/caCertsIssuedTofbcag4.p7c create mode 100644 x509/src/crl.rs create mode 100644 x509/tests/crl.rs diff --git a/Cargo.lock b/Cargo.lock index 6120dd7d4..10dc6075a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -518,6 +518,16 @@ dependencies = [ "libc", ] +[[package]] +name = "ocsp" +version = "0.0.1" +dependencies = [ + "der", + "hex-literal", + "spki", + "x509", +] + [[package]] name = "oorandom" version = "11.1.3" @@ -574,6 +584,7 @@ dependencies = [ "der", "hex-literal", "spki", + "x509", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fd3318139..1cd59bbac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "const-oid", "der", "der/derive", + "ocsp", "pem-rfc7468", "pkcs1", "pkcs5", diff --git a/ocsp/Cargo.toml b/ocsp/Cargo.toml new file mode 100644 index 000000000..029ea5646 --- /dev/null +++ b/ocsp/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ocsp" +version = "0.0.1" +description = """ +Pure Rust implementation of the X.509 Internet Public Key Infrastructure +Online Certificate Status Protocol - OCSP formats as described in RFC 6960 +""" +authors = ["RustCrypto Developers"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/RustCrypto/formats/tree/master/x509" +categories = ["cryptography", "data-structures", "encoding", "no-std"] +keywords = ["crypto", "x.509"] +readme = "README.md" +edition = "2021" +rust-version = "1.57" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +der = { version = "0.6.0-pre.1", features = ["oid", "derive", "alloc"], path = "../der" } +x509 = { version = "0.0.1", path = "../x509" } +spki = { version = "=0.6.0-pre.0", path = "../spki" } + +[dev-dependencies] +hex-literal = "0.3" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/ocsp/README.md b/ocsp/README.md new file mode 100644 index 000000000..ff65e5799 --- /dev/null +++ b/ocsp/README.md @@ -0,0 +1,60 @@ +# [RustCrypto]: X.509 + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +[![Build Status][build-image]][build-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] + +Pure Rust implementation of the X.509 Internet Public Key Infrastructure +Online Certificate Status Protocol - OCSP formats as described in [RFC 6960]. + +[Documentation][docs-link] + +## Status + +tl;dr: not ready to use. + +This is a work-in-progress implementation which is at an early stage of +development. + +## Minimum Supported Rust Version + +This crate requires **Rust 1.57** at a minimum. + +We may change the MSRV in the future, but it will be accompanied by a minor +version bump. + +## License + +Licensed under either of: + +- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +- [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/x509.svg +[crate-link]: https://crates.io/crates/x509 +[docs-image]: https://docs.rs/x509/badge.svg +[docs-link]: https://docs.rs/x509/ +[build-image]: https://github.com/RustCrypto/formats/actions/workflows/x509.yml/badge.svg +[build-link]: https://github.com/RustCrypto/formats/actions/workflows/x509.yml +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.57+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats + +[//]: # (links) + +[RustCrypto]: https://github.com/rustcrypto +[RFC 6960]: https://datatracker.ietf.org/doc/html/rfc6960 diff --git a/ocsp/src/lib.rs b/ocsp/src/lib.rs new file mode 100644 index 000000000..f7cc3d707 --- /dev/null +++ b/ocsp/src/lib.rs @@ -0,0 +1,5 @@ +//! The ocsp module features encoders and decoders for the structures defined in [RFC 6960](https://datatracker.ietf.org/doc/html/rfc6960). + +pub mod ocsp; + +extern crate alloc; diff --git a/ocsp/src/ocsp.rs b/ocsp/src/ocsp.rs new file mode 100644 index 000000000..dffe14cc5 --- /dev/null +++ b/ocsp/src/ocsp.rs @@ -0,0 +1,463 @@ +use der::asn1::{BitString, Ia5String, ObjectIdentifier, OctetString, UIntBytes}; +use der::asn1::{GeneralizedTime, Null}; +use der::{Any, Choice, Enumerated, Sequence}; +use spki::AlgorithmIdentifier; +use x509::ext::pkix::name::GeneralName; +use x509::ext::pkix::{AuthorityInfoAccessSyntax, CrlReason}; +use x509::ext::Extensions; +use x509::name::Name; +use x509::Certificate; + +/// ```text +/// OCSPRequest ::= SEQUENCE { +/// tbsRequest TBSRequest, +/// optionalSignature [0] EXPLICIT Signature OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct OcspRequest<'a> { + pub tbs_request: TbsRequest<'a>, + + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub optional_signature: Option>, +} + +/// ```text +/// TBSRequest ::= SEQUENCE { +/// version [0] EXPLICIT Version DEFAULT v1, +/// requestorName [1] EXPLICIT GeneralName OPTIONAL, +/// requestList SEQUENCE OF Request, +/// requestExtensions [2] EXPLICIT Extensions OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct TbsRequest<'a> { + /// version \[0\] EXPLICIT Version DEFAULT v1, + #[asn1( + context_specific = "0", + default = "Default::default", + tag_mode = "EXPLICIT" + )] + pub version: Version, + + /// requestorName \[1\] EXPLICIT GeneralName OPTIONAL, + #[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")] + pub requestor_name: Option>, + + /// requestList SEQUENCE OF Request, + pub request_list: alloc::vec::Vec>, + + /// requestExtensions \[2\] EXPLICIT Extensions OPTIONAL } + #[asn1(context_specific = "2", optional = "true", tag_mode = "EXPLICIT")] + pub request_extensions: Option>, +} + +/// ```text +/// Signature ::= SEQUENCE { +/// signatureAlgorithm AlgorithmIdentifier, +/// signature BIT STRING, +/// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct Signature<'a> { + /// signatureAlgorithm AlgorithmIdentifier, + pub signature_algorithm: AlgorithmIdentifier<'a>, + + /// signature BIT STRING, + pub signature: BitString<'a>, + + /// certs \[0\] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub certs: Option>>, +} + +/// OCSP `Version` as defined in [RFC 6960 Section 4.1]. +/// +/// ```text +/// Version ::= INTEGER { v1(0) } +/// ``` +/// +/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1 +#[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)] +#[asn1(type = "INTEGER")] +#[repr(u8)] +pub enum Version { + /// Version 1 (default) + V1 = 0, +} + +impl Default for Version { + fn default() -> Self { + Self::V1 + } +} + +/// ```text +/// Request ::= SEQUENCE { +/// reqCert CertID, +/// singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct Request<'a> { + /// reqCert CertID, + pub req_cert: CertId<'a>, + + /// singleRequestExtensions \[0\] EXPLICIT Extensions OPTIONAL } + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub single_request_extensions: Option>, +} + +/// ```text +/// CertID ::= SEQUENCE { +/// hashAlgorithm AlgorithmIdentifier, +/// issuerNameHash OCTET STRING, -- Hash of issuer's DN +/// issuerKeyHash OCTET STRING, -- Hash of issuer's public key +/// serialNumber CertificateSerialNumber } +/// ``` +/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct CertId<'a> { + /// hashAlgorithm AlgorithmIdentifier, + pub hash_algorithm: AlgorithmIdentifier<'a>, + + /// issuerNameHash OCTET STRING, -- Hash of issuer's DN + pub issuer_name_hash: OctetString<'a>, + + /// issuerKeyHash OCTET STRING, -- Hash of issuer's public key + pub issuer_key_hash: OctetString<'a>, + + /// serialNumber CertificateSerialNumber } + pub serial_number: UIntBytes<'a>, +} + +/// ```text +/// OCSPResponse ::= SEQUENCE { +/// responseStatus OCSPResponseStatus, +/// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct OcspResponse<'a> { + pub response_status: OcspResponseStatus, + + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub response_bytes: Option>, +} + +/// ```text +/// OCSPResponseStatus ::= ENUMERATED { +/// successful (0), -- Response has valid confirmations +/// malformedRequest (1), -- Illegal confirmation request +/// internalError (2), -- Internal error in issuer +/// tryLater (3), -- Try again later +/// -- (4) is not used +/// sigRequired (5), -- Must sign the request +/// unauthorized (6) -- Request unauthorized +/// } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Enumerated, Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum OcspResponseStatus { + /// successful (0), -- Response has valid confirmations + Successful = 0, + + /// malformedRequest (1), -- Illegal confirmation request + MalformedRequest = 1, + + /// internalError (2), -- Internal error in issuer + InternalError = 2, + + /// tryLater (3), -- Try again later + TryLater = 3, + + // -- (4) is not used + /// sigRequired (5), -- Must sign the request + SigRequired = 5, + + /// unauthorized (6) -- Request unauthorized + Unauthorized = 6, +} + +/// ```text +/// ResponseBytes ::= SEQUENCE { +/// responseType OBJECT IDENTIFIER, +/// response OCTET STRING } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct ResponseBytes<'a> { + /// responseType OBJECT IDENTIFIER, + pub response_type: ObjectIdentifier, + + /// response OCTET STRING } + pub response: OctetString<'a>, +} + +/// ```text +/// BasicOCSPResponse ::= SEQUENCE { +/// tbsResponseData ResponseData, +/// signatureAlgorithm AlgorithmIdentifier, +/// signature BIT STRING, +/// certs \[0\] EXPLICIT SEQUENCE OF Certificate OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct BasicOcspResponse<'a> { + /// tbsResponseData ResponseData, + pub tbs_response_data: ResponseData<'a>, + + /// signatureAlgorithm AlgorithmIdentifier, + pub signature_algorithm: AlgorithmIdentifier<'a>, + + /// signature BIT STRING, + pub signature: BitString<'a>, + + /// certs \[0\] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub certs: Option>>, +} + +/// ```text +// ResponseData ::= SEQUENCE { +/// version [0] EXPLICIT Version DEFAULT v1, +/// responderID ResponderID, +/// producedAt GeneralizedTime, +/// responses SEQUENCE OF SingleResponse, +/// responseExtensions [1] EXPLICIT Extensions OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct ResponseData<'a> { + /// version \[0\] EXPLICIT Version DEFAULT v1, + #[asn1( + context_specific = "0", + default = "Default::default", + tag_mode = "EXPLICIT" + )] + pub version: Version, + + /// responderID ResponderID, + pub responder_id: ResponderId<'a>, + + /// producedAt GeneralizedTime, + pub produced_at: GeneralizedTime, + + /// responses SEQUENCE OF SingleResponse, + pub responses: Vec>, + + /// responseExtensions \[1\] EXPLICIT Extensions OPTIONAL } + #[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")] + pub response_extensions: Option>, +} + +/// ```text +// ResponderID ::= CHOICE { +/// byName [1] Name, +/// byKey [2] KeyHash } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Choice)] +pub enum ResponderId<'a> { + /// byName \[1\] Name, + #[asn1(context_specific = "1", tag_mode = "EXPLICIT", constructed = "true")] + ByName(Name<'a>), + + /// byKey \[2\] KeyHash } + #[asn1(context_specific = "2", tag_mode = "EXPLICIT", constructed = "true")] + ByKey(KeyHash<'a>), +} + +/// ```text +/// KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key +/// -- (i.e., the SHA-1 hash of the value of the +/// -- BIT STRING subjectPublicKey [excluding +/// -- the tag, length, and number of unused +/// -- bits] in the responder's certificate) +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +pub type KeyHash<'a> = OctetString<'a>; + +/// ```text +// SingleResponse ::= SEQUENCE { +/// certID CertID, +/// certStatus CertStatus, +/// thisUpdate GeneralizedTime, +/// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, +/// singleExtensions [1] EXPLICIT Extensions OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct SingleResponse<'a> { + /// certID CertID, + pub cert_id: CertId<'a>, + + /// certStatus CertStatus, + pub cert_status: CertStatus, + + /// thisUpdate GeneralizedTime, + pub this_update: GeneralizedTime, + + /// nextUpdate \[0\] EXPLICIT GeneralizedTime OPTIONAL, + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub next_update: Option, + + /// singleExtensions \[1\] EXPLICIT Extensions OPTIONAL } + #[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")] + pub single_request_extensions: Option>, +} + +/// ```text +/// CertStatus ::= CHOICE { +/// good [0] IMPLICIT NULL, +/// revoked [1] IMPLICIT RevokedInfo, +/// unknown [2] IMPLICIT UnknownInfo } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Choice)] +pub enum CertStatus { + /// good \[0\] IMPLICIT NULL, + #[asn1(context_specific = "0", tag_mode = "IMPLICIT")] + Good(Null), + + /// revoked \[1\] IMPLICIT RevokedInfo, + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", constructed = "true")] + Revoked(RevokedInfo), + + /// unknown \[2\] IMPLICIT UnknownInfo } + #[asn1(context_specific = "2", tag_mode = "IMPLICIT")] + Unknown(UnknownInfo), +} + +/// ```text +// RevokedInfo ::= SEQUENCE { +/// revocationTime GeneralizedTime, +/// revocationReason [0] EXPLICIT CRLReason OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct RevokedInfo { + /// revocationTime GeneralizedTime, + pub revocation_time: GeneralizedTime, + + /// revocationReason \[0\] EXPLICIT CRLReason OPTIONAL } + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub revocation_reason: Option, +} + +/// ```text +/// UnknownInfo ::= NULL +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +pub type UnknownInfo = Null; + +/// ```text +// ArchiveCutoff ::= GeneralizedTime +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +pub type ArchiveCutoff = GeneralizedTime; + +/// ```text +// AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +pub type AcceptableResponses = Vec; + +/// ```text +/// ServiceLocator ::= SEQUENCE { +/// issuer Name, +/// locator AuthorityInfoAccessSyntax } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct ServiceLocator<'a> { + /// issuer Name, + pub issuer: Name<'a>, + + /// locator AuthorityInfoAccessSyntax } + pub locator: AuthorityInfoAccessSyntax<'a>, +} + +/// ```text +/// CrlID ::= SEQUENCE { +/// crlUrl [0] EXPLICIT IA5String OPTIONAL, +/// crlNum [1] EXPLICIT INTEGER OPTIONAL, +/// crlTime [2] EXPLICIT GeneralizedTime OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct CrlId<'a> { + #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")] + pub crl_url: Option>, + + #[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")] + pub crl_num: Option>, + + #[asn1(context_specific = "2", optional = "true", tag_mode = "EXPLICIT")] + pub crl_time: Option, +} + +/// ```text +/// PreferredSignatureAlgorithms ::= SEQUENCE OF PreferredSignatureAlgorithm +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +pub type PreferredSignatureAlgorithms<'a> = Vec>; + +/// ```text +/// PreferredSignatureAlgorithm ::= SEQUENCE { +/// sigIdentifier AlgorithmIdentifier, +/// certIdentifier AlgorithmIdentifier OPTIONAL } +/// ``` +/// [RFC 6960 Section 4.2.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct PreferredSignatureAlgorithm<'a> { + pub sig_identifier: AlgorithmIdentifier<'a>, + pub cert_identifier: Option>, +} + +// Object Identifiers +// id-pkix OBJECT IDENTIFIER ::= +// { iso(1) identified-organization(3) dod(6) internet(1) +// security(5) mechanisms(5) pkix(7) } +// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } + +/// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } +pub const KP_OCSP_SIGNING: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.3.9"); + +// id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } +// id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } + +/// id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp } +pub const OCSP: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1"); + +/// id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 } +pub const OCSP_BASIC: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.1"); + +/// id-pkix-ocsp-nonce OBJECT IDENTIFIER ::= { id-pkix-ocsp 2 } +pub const OCSP_NONCE: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.2"); + +/// id-pkix-ocsp-crl OBJECT IDENTIFIER ::= { id-pkix-ocsp 3 } +pub const OCSP_CRL: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.3"); + +/// id-pkix-ocsp-response OBJECT IDENTIFIER ::= { id-pkix-ocsp 4 } +pub const OCSP_RESPONSE: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.4"); + +/// id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 } +pub const OCSP_NOCHECK: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.5"); + +/// id-pkix-ocsp-archive-cutoff OBJECT IDENTIFIER ::= { id-pkix-ocsp 6 } +pub const KP_OCSP_ARCHIVE_CUTOFF: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.6"); + +/// id-pkix-ocsp-service-locator OBJECT IDENTIFIER ::= { id-pkix-ocsp 7 } +pub const OCSP_SERVICE_LOCATOR: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.7"); + +/// id-pkix-ocsp-pref-sig-algs OBJECT IDENTIFIER ::= { id-pkix-ocsp 8 } +pub const OCSP_PREF_SIG_ALGS: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.8"); + +/// id-pkix-ocsp-extended-revoke OBJECT IDENTIFIER ::= { id-pkix-ocsp 9 } +pub const OCSP_EXTENDED_REVOKE: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.9"); diff --git a/ocsp/tests/ocsp.rs b/ocsp/tests/ocsp.rs new file mode 100644 index 000000000..063c37d4d --- /dev/null +++ b/ocsp/tests/ocsp.rs @@ -0,0 +1,296 @@ +use der::asn1::{Null, ObjectIdentifier}; +use der::{Decodable, Encodable}; +use hex_literal::hex; +use ocsp::ocsp::Version::V1; +use ocsp::ocsp::*; +use x509::ext::pkix::CrlReason; + +#[test] +fn decode_ocsp_req_ca_signed() { + // request generated using openssl via this command: + // openssl ocsp -noverify -no_nonce -respout ~/Desktop/ocspdigicert.resp + // -reqout ~/Desktop/ocspdigicert.req -issuer ~/Desktop/DigiCertGlobalCAG2.crt + // -cert ~/Desktop/amazon.der -url http://ocsp.digicert.com -header "HOST" "ocsp.digicert.com" -text + + pub const PKIXALG_SHA1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26"); + + let ocsp_req = + OcspRequest::from_der(&hex!("3051304F304D304B3049300906052B0E03021A05000414A87E303106E4E88565CFE952598FA6DA7C00532F0414246E2B2DD06A925151256901AA9A47A689E7402002100E4239AB85E2E6A27C52C6DE9B9078D9")[..]).unwrap(); + assert_eq!(ocsp_req.tbs_request.version, V1); + //assert!(ocsp_req.tbs_request.requestor_name.is_none()); + assert_eq!(ocsp_req.tbs_request.request_list.len(), 1); + let req = &ocsp_req.tbs_request.request_list[0]; + assert_eq!(req.req_cert.hash_algorithm.oid, PKIXALG_SHA1); + assert!(req.req_cert.hash_algorithm.parameters.is_some()); + assert_eq!( + req.req_cert + .hash_algorithm + .parameters + .to_vec() + .unwrap() + .as_slice(), + Null.to_vec().unwrap().as_slice() + ); + assert_eq!( + req.req_cert.issuer_name_hash.as_bytes(), + &hex!("A87E303106E4E88565CFE952598FA6DA7C00532F") + ); + assert_eq!( + req.req_cert.issuer_key_hash.as_bytes(), + &hex!("246E2B2DD06A925151256901AA9A47A689E74020") + ); + assert_eq!( + req.req_cert.serial_number.as_bytes(), + &hex!("0E4239AB85E2E6A27C52C6DE9B9078D9") + ); + + let reenc = ocsp_req.to_vec().unwrap(); + assert_eq!(reenc, &hex!("3051304F304D304B3049300906052B0E03021A05000414A87E303106E4E88565CFE952598FA6DA7C00532F0414246E2B2DD06A925151256901AA9A47A689E7402002100E4239AB85E2E6A27C52C6DE9B9078D9")[..]) +} + +#[test] +fn decode_ocsp_resp_ca_signed() { + // response generated using openssl via this command: + // openssl ocsp -noverify -no_nonce -respout ~/Desktop/ocspdigicert.resp + // -reqout ~/Desktop/ocspdigicert.req -issuer ~/Desktop/DigiCertGlobalCAG2.crt + // -cert ~/Desktop/amazon.der -url http://ocsp.digicert.com -header "HOST" "ocsp.digicert.com" -text + + // 308201D30A0100A08201CC308201C806092B0601050507300101048201B9308201B530819EA2160414246E2B2DD06A925151256901AA9A47A689E74020180F32303232303230323034313232355A307330713049300906052B0E03021A05000414A87E303106E4E88565CFE952598FA6DA7C00532F0414246E2B2DD06A925151256901AA9A47A689E7402002100E4239AB85E2E6A27C52C6DE9B9078D98000180F32303232303230323033353730315AA011180F32303232303230393033313230315A300D06092A864886F70D01010B0500038201010089DC0913BD91DB172C314AE8F78E6574950353618FFBBF2F12B1F134F21EC20063E66C60AC463BDB926A9D5B699EB39689032659D43C441863BAC9D8B7CC84137B558503D2631348EDA79AEC9FD4B69AE988F68A752470D3D4BA46F41B9FD41A974A5311BAA191AFBA370F0B051F035297FA76A2133B98FF471BDA4BFBFDA275B4FA1CF8377EEB2B2141F9C998ACE27C60F11E221D6732A5D538FEDF9A01D776E1877BCF70A8C953630B2C66752A66DF087C075794F5A2C0023C964CD73476EA2556F091F6C191B30B74E2523F668F32CE10AFB3D51CE053D4D41E62214B60171093AB915DBBA497F09B3C279127E1D25C7AA6A7048BBEA41A23F4F827BD108F + let ocsp_resp = + OcspResponse::from_der(&hex!("308201D30A0100A08201CC308201C806092B0601050507300101048201B9308201B530819EA2160414246E2B2DD06A925151256901AA9A47A689E74020180F32303232303230323034313232355A307330713049300906052B0E03021A05000414A87E303106E4E88565CFE952598FA6DA7C00532F0414246E2B2DD06A925151256901AA9A47A689E7402002100E4239AB85E2E6A27C52C6DE9B9078D98000180F32303232303230323033353730315AA011180F32303232303230393033313230315A300D06092A864886F70D01010B0500038201010089DC0913BD91DB172C314AE8F78E6574950353618FFBBF2F12B1F134F21EC20063E66C60AC463BDB926A9D5B699EB39689032659D43C441863BAC9D8B7CC84137B558503D2631348EDA79AEC9FD4B69AE988F68A752470D3D4BA46F41B9FD41A974A5311BAA191AFBA370F0B051F035297FA76A2133B98FF471BDA4BFBFDA275B4FA1CF8377EEB2B2141F9C998ACE27C60F11E221D6732A5D538FEDF9A01D776E1877BCF70A8C953630B2C66752A66DF087C075794F5A2C0023C964CD73476EA2556F091F6C191B30B74E2523F668F32CE10AFB3D51CE053D4D41E62214B60171093AB915DBBA497F09B3C279127E1D25C7AA6A7048BBEA41A23F4F827BD108F")[..]).unwrap(); + + assert_eq!(ocsp_resp.response_status, OcspResponseStatus::Successful); + assert!(ocsp_resp.response_bytes.is_some()); + let response_bytes = ocsp_resp.response_bytes.as_ref().unwrap(); + assert_eq!(response_bytes.response_type, OCSP_BASIC); + + pub const PKIXALG_SHA256_WITH_RSA_ENCRYPTION: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.11"); + pub const PKIXALG_SHA1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26"); + + let bor = BasicOcspResponse::from_der(response_bytes.response.as_bytes()).unwrap(); + assert_eq!( + bor.signature_algorithm.oid, + PKIXALG_SHA256_WITH_RSA_ENCRYPTION + ); + assert!(bor.signature_algorithm.parameters.is_some()); + assert_eq!( + bor.signature_algorithm + .parameters + .to_vec() + .unwrap() + .as_slice(), + Null.to_vec().unwrap().as_slice() + ); + + assert_eq!(bor.tbs_response_data.version, V1); + + match bor.tbs_response_data.responder_id { + ResponderId::ByKey(kh) => { + assert_eq!( + kh.as_bytes(), + &hex!("246E2B2DD06A925151256901AA9A47A689E74020") + ); + } + _ => { + panic!("Expected ByKey and got something else") + } + } + + assert_eq!( + bor.tbs_response_data + .produced_at + .to_unix_duration() + .as_secs(), + 1643775145 + ); + assert_eq!(bor.tbs_response_data.responses.len(), 1); + let sr = &bor.tbs_response_data.responses[0]; + assert_eq!(sr.cert_id.hash_algorithm.oid, PKIXALG_SHA1); + assert!(sr.cert_id.hash_algorithm.parameters.is_some()); + assert_eq!( + sr.cert_id + .hash_algorithm + .parameters + .to_vec() + .unwrap() + .as_slice(), + Null.to_vec().unwrap().as_slice() + ); + assert_eq!( + sr.cert_id.issuer_name_hash.as_bytes(), + &hex!("A87E303106E4E88565CFE952598FA6DA7C00532F") + ); + assert_eq!( + sr.cert_id.issuer_key_hash.as_bytes(), + &hex!("246E2B2DD06A925151256901AA9A47A689E74020") + ); + assert_eq!( + sr.cert_id.serial_number.as_bytes(), + &hex!("0E4239AB85E2E6A27C52C6DE9B9078D9") + ); + + match sr.cert_status { + CertStatus::Good(g) => { + assert_eq!( + g.to_vec().unwrap().as_slice(), + Null.to_vec().unwrap().as_slice() + ); + } + _ => { + panic!("Expected Good and got something other") + } + } + + assert_eq!(sr.this_update.to_unix_duration().as_secs(), 1643774221); + assert!(sr.next_update.is_some()); + assert_eq!( + sr.next_update.unwrap().to_unix_duration().as_secs(), + 1644376321 + ); + + let reenc = ocsp_resp.to_vec().unwrap(); + assert_eq!(reenc, &hex!("308201D30A0100A08201CC308201C806092B0601050507300101048201B9308201B530819EA2160414246E2B2DD06A925151256901AA9A47A689E74020180F32303232303230323034313232355A307330713049300906052B0E03021A05000414A87E303106E4E88565CFE952598FA6DA7C00532F0414246E2B2DD06A925151256901AA9A47A689E7402002100E4239AB85E2E6A27C52C6DE9B9078D98000180F32303232303230323033353730315AA011180F32303232303230393033313230315A300D06092A864886F70D01010B0500038201010089DC0913BD91DB172C314AE8F78E6574950353618FFBBF2F12B1F134F21EC20063E66C60AC463BDB926A9D5B699EB39689032659D43C441863BAC9D8B7CC84137B558503D2631348EDA79AEC9FD4B69AE988F68A752470D3D4BA46F41B9FD41A974A5311BAA191AFBA370F0B051F035297FA76A2133B98FF471BDA4BFBFDA275B4FA1CF8377EEB2B2141F9C998ACE27C60F11E221D6732A5D538FEDF9A01D776E1877BCF70A8C953630B2C66752A66DF087C075794F5A2C0023C964CD73476EA2556F091F6C191B30B74E2523F668F32CE10AFB3D51CE053D4D41E62214B60171093AB915DBBA497F09B3C279127E1D25C7AA6A7048BBEA41A23F4F827BD108F")[..]) +} + +#[test] +fn decode_ocsp_req_delegated() { + // request generated using openssl via this command: + // openssl ocsp -noverify -no_nonce -respout ~/Desktop/ocspdod.resp -reqout ~/Desktop/ocspdod.req + // -issuer ~/Desktop/email_ca_59.der -cert ~/Desktop/ee.der -url http://ocsp.disa.mil + // -header "HOST" "ocsp.disa.mil" -text + + pub const PKIXALG_SHA1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26"); + + let ocsp_req = + OcspRequest::from_der(&hex!("304530433041303F303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017467")[..]).unwrap(); + assert_eq!(ocsp_req.tbs_request.version, V1); + //assert!(ocsp_req.tbs_request.requestor_name.is_none()); + assert_eq!(ocsp_req.tbs_request.request_list.len(), 1); + let req = &ocsp_req.tbs_request.request_list[0]; + assert_eq!(req.req_cert.hash_algorithm.oid, PKIXALG_SHA1); + assert!(req.req_cert.hash_algorithm.parameters.is_some()); + assert_eq!( + req.req_cert + .hash_algorithm + .parameters + .to_vec() + .unwrap() + .as_slice(), + Null.to_vec().unwrap().as_slice() + ); + assert_eq!( + req.req_cert.issuer_name_hash.as_bytes(), + &hex!("0F0D5890F551D42ACF5431B7F42A321F7B74A473") + ); + assert_eq!( + req.req_cert.issuer_key_hash.as_bytes(), + &hex!("771441A65D9526D01DFF953B628CEAB7B55D3B92") + ); + assert_eq!(req.req_cert.serial_number.as_bytes(), &hex!("01017467")); + + let reenc = ocsp_req.to_vec().unwrap(); + assert_eq!(reenc, &hex!("304530433041303F303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017467")[..]) +} + +#[test] +fn decode_ocsp_resp_delegated() { + // response generated using openssl via this command: + // openssl ocsp -noverify -no_nonce -respout ~/Desktop/ocspdod.resp -reqout ~/Desktop/ocspdod.req + // -issuer ~/Desktop/email_ca_59.der -cert ~/Desktop/ee.der -url http://ocsp.disa.mil + // -header "HOST" "ocsp.disa.mil" -text + + // 308201D30A0100A08201CC308201C806092B0601050507300101048201B9308201B530819EA2160414246E2B2DD06A925151256901AA9A47A689E74020180F32303232303230323034313232355A307330713049300906052B0E03021A05000414A87E303106E4E88565CFE952598FA6DA7C00532F0414246E2B2DD06A925151256901AA9A47A689E7402002100E4239AB85E2E6A27C52C6DE9B9078D98000180F32303232303230323033353730315AA011180F32303232303230393033313230315A300D06092A864886F70D01010B0500038201010089DC0913BD91DB172C314AE8F78E6574950353618FFBBF2F12B1F134F21EC20063E66C60AC463BDB926A9D5B699EB39689032659D43C441863BAC9D8B7CC84137B558503D2631348EDA79AEC9FD4B69AE988F68A752470D3D4BA46F41B9FD41A974A5311BAA191AFBA370F0B051F035297FA76A2133B98FF471BDA4BFBFDA275B4FA1CF8377EEB2B2141F9C998ACE27C60F11E221D6732A5D538FEDF9A01D776E1877BCF70A8C953630B2C66752A66DF087C075794F5A2C0023C964CD73476EA2556F091F6C191B30B74E2523F668F32CE10AFB3D51CE053D4D41E62214B60171093AB915DBBA497F09B3C279127E1D25C7AA6A7048BBEA41A23F4F827BD108F + let ocsp_resp = + OcspResponse::from_der(&hex!("30820FA10A0100A0820F9A30820F9606092B060105050730010104820F8730820F83308209F1A2160414ADB0A9B2DDE9D444B4DF80F599598E84AC5EC687180F32303232303230333135303233365A308209C4307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017456A116180F32303231303532303138303635305AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017457A116180F32303231303730373139353830355AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017459A116180F32303231303532303138303634395AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745AA116180F32303231303132323136303832375AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745BA116180F32303231303731393138353734355AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745CA116180F32303231303132323136303832365AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745EA116180F32303231303432323132323332335AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017460A116180F32303231303731393138353734335AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017462A116180F32303231303932393136353734375AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017464A116180F32303231303432323132323332325AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017467A116180F32303231303932393136353734375AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017477A116180F32303231303730313138313934365AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017479A116180F32303232303130353138313532325AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747AA116180F32303231313130343131353932395AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747BA116180F32303231303730313138313934325AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747CA116180F32303232303130353138313532325AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747DA116180F32303231303231303136333032315AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747EA116180F32303231313130343131353932325AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017480A116180F32303231303231303136333032305AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017481A116180F32303231303132323133353031395AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A300D06092A864886F70D01010B050003820101008946FF8FF3009FC7265CB687D56960190379BEF6537FAC541A2306ED709B51AACD79B313331E297495AC4E26ABD94030CC407FDCE26CFC0B7A2BE1F226906182863FF1F140D628FA02B1D0BE1A1697E2F9C17AEF5FEC32AA4D120FAF0F58474AC2718964033076286DC358F5E96B08A5C83323CA4CA2B881D8FFA8E0EA3DB9A90863D378A3349EBF189030DA11891C6695950C5B93CBD440D998E67C0137655AC9DA40826B51211AD5985918336939131A4D1BF9E9A2A88CFB3C5059E3D1CE0D9B19F487EC018C60A0279E24270C7090006A4EC728E5C4A1F86829AFA236AF9979165DF1E7F0F5544217864F9C348ABF2B17C2FCD7DDE35838A99BC4A7256821A0820476308204723082046E30820356A0030201020203358762300D06092A864886F70D01010B0500305D310B300906035504061302555331183016060355040A130F552E532E20476F7665726E6D656E74310C300A060355040B1303446F44310C300A060355040B1303504B49311830160603550403130F444F4420454D41494C2043412D3539301E170D3232303132353030303035315A170D3232303331313030303035315A3064310B300906035504061302555331183016060355040A130F552E532E20476F7665726E6D656E74310C300A060355040B1303446F44310C300A060355040B1303504B49311F301D06035504031316444F44204E4950524E4554204F43535020313244203230820122300D06092A864886F70D01010105000382010F003082010A0282010100BF8A691DDB447562EFCDCB6C4DED76DDB200D1A188E01A3BA4EEEE1C42A7E5E78C0784C512F5CF64DD1A407A2EBC1F280761C1372351E03AED90C2DA0556066398E555EEE91C92988A62CDA1FD1F4E2E5F6EC6C022821C0D7C8B0E90E7CCF203CE8F102CBF4756D9B8CD09A2F16CF08F1E944311C8CD2120B98F6BCAEA1E60D1841A933815C8936BD20CC984FA1F5BF7AFFFFF2886DE53FDF3AE80E8394E09D441AE615693A78FE0051942B724147595DF049382897E26BBEA4E6902376C494F31141D1693005AA4E8AEBE4FEC342D10BFD90661935FB00D61A1FF7CB3B8FF45E473B3FFDD6D2C692FA8C0DE23373F1A6AD84109E7A9274008C5E94E28CE91850203010001A382012E3082012A301F0603551D23041830168014771441A65D9526D01DFF953B628CEAB7B55D3B92304606082B06010505070101043A3038303606082B06010505073002862A687474703A2F2F63726C2E646973612E6D696C2F7369676E2F444F44454D41494C43415F35392E63657230160603551D250101FF040C300A06082B06010505070309300E0603551D0F0101FF040403020780300F06092B060105050730010504020500301D0603551D0E04160414ADB0A9B2DDE9D444B4DF80F599598E84AC5EC68730670603551D200460305E300B0609608648016502010B24300B0609608648016502010B27300B0609608648016502010B2A300B0609608648016502010B3B300C060A6086480165030201030D300C060A60864801650302010311300C060A60864801650302010327300D06092A864886F70D01010B0500038201010047FE1DF6D439EF205C931F34B8C802B6E6D9C4EFEAFA7228FD07E56DFF17EEC18C250941EB46D5F416A02F2706A951A2A16435C589EE1059BB86EC38C7F07C2C6660C4B25C8918115FFD21A0D9798B860F878122B3781DCA004AFE869B4B88E0B8ABB4C3A255BC034B36ED82E4692AB765667E3036FC4BB2A53EF9B4654D5C8FDA62E1F6B9DFC7382FA9A3BEAC5EB35A105CB6FFD7B33D561DA77B886D17F58F9D6EDC722C9786E7C664598116F9ACB41282FB0C75EF6BA87BAE97249A42DF45FEC6AEB724C03D8EE5FF4FD7753AC0FE50CB6B746544D4A4DB977946D29183295B160538338FA2FB27F6BC84B5CBD658A431A8B87A9E20A57753BC8AB964B486")[..]).unwrap(); + + assert_eq!(ocsp_resp.response_status, OcspResponseStatus::Successful); + assert!(ocsp_resp.response_bytes.is_some()); + let response_bytes = ocsp_resp.response_bytes.as_ref().unwrap(); + assert_eq!(response_bytes.response_type, OCSP_BASIC); + + pub const PKIXALG_SHA256_WITH_RSA_ENCRYPTION: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.11"); + pub const PKIXALG_SHA1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26"); + + let bor = BasicOcspResponse::from_der(response_bytes.response.as_bytes()).unwrap(); + assert_eq!( + bor.signature_algorithm.oid, + PKIXALG_SHA256_WITH_RSA_ENCRYPTION + ); + assert!(bor.signature_algorithm.parameters.is_some()); + assert_eq!( + bor.signature_algorithm + .parameters + .to_vec() + .unwrap() + .as_slice(), + Null.to_vec().unwrap().as_slice() + ); + + assert_eq!(bor.tbs_response_data.version, V1); + + match bor.tbs_response_data.responder_id { + ResponderId::ByKey(kh) => { + assert_eq!( + kh.as_bytes(), + &hex!("ADB0A9B2DDE9D444B4DF80F599598E84AC5EC687") + ); + } + _ => { + panic!("Expected ByKey and got something else") + } + } + + assert_eq!( + bor.tbs_response_data + .produced_at + .to_unix_duration() + .as_secs(), + 1643900556 + ); + assert_eq!(bor.tbs_response_data.responses.len(), 20); + let sr = &bor.tbs_response_data.responses[10]; + + assert_eq!(sr.cert_id.hash_algorithm.oid, PKIXALG_SHA1); + assert!(sr.cert_id.hash_algorithm.parameters.is_some()); + assert_eq!( + sr.cert_id + .hash_algorithm + .parameters + .to_vec() + .unwrap() + .as_slice(), + Null.to_vec().unwrap().as_slice() + ); + assert_eq!( + sr.cert_id.issuer_name_hash.as_bytes(), + &hex!("0F0D5890F551D42ACF5431B7F42A321F7B74A473") + ); + assert_eq!( + sr.cert_id.issuer_key_hash.as_bytes(), + &hex!("771441A65D9526D01DFF953B628CEAB7B55D3B92") + ); + assert_eq!(sr.cert_id.serial_number.as_bytes(), &hex!("01017467")); + + match &sr.cert_status { + CertStatus::Revoked(ri) => { + assert!(ri.revocation_reason.is_some()); + assert_eq!(ri.revocation_reason.unwrap(), CrlReason::AffiliationChanged,); + assert_eq!(ri.revocation_time.to_unix_duration().as_secs(), 1632934667,); + } + _ => { + panic!("Expected Good and got something other") + } + } + + assert_eq!(sr.this_update.to_unix_duration().as_secs(), 1643848200); + assert!(sr.next_update.is_some()); + assert_eq!( + sr.next_update.unwrap().to_unix_duration().as_secs(), + 1644456600 + ); + + let reenc = ocsp_resp.to_vec().unwrap(); + assert_eq!(reenc, &hex!("30820FA10A0100A0820F9A30820F9606092B060105050730010104820F8730820F83308209F1A2160414ADB0A9B2DDE9D444B4DF80F599598E84AC5EC687180F32303232303230333135303233365A308209C4307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017456A116180F32303231303532303138303635305AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017457A116180F32303231303730373139353830355AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017459A116180F32303231303532303138303634395AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745AA116180F32303231303132323136303832375AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745BA116180F32303231303731393138353734355AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745CA116180F32303231303132323136303832365AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101745EA116180F32303231303432323132323332335AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017460A116180F32303231303731393138353734335AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017462A116180F32303231303932393136353734375AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017464A116180F32303231303432323132323332325AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017467A116180F32303231303932393136353734375AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017477A116180F32303231303730313138313934365AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017479A116180F32303232303130353138313532325AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747AA116180F32303231313130343131353932395AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747BA116180F32303231303730313138313934325AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747CA116180F32303232303130353138313532325AA0030A0103180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747DA116180F32303231303231303136333032315AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B9202040101747EA116180F32303231313130343131353932325AA0030A0101180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017480A116180F32303231303231303136333032305AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A307B303D300906052B0E03021A050004140F0D5890F551D42ACF5431B7F42A321F7B74A4730414771441A65D9526D01DFF953B628CEAB7B55D3B92020401017481A116180F32303231303132323133353031395AA0030A0104180F32303232303230333030333030305AA011180F32303232303231303031333030305A300D06092A864886F70D01010B050003820101008946FF8FF3009FC7265CB687D56960190379BEF6537FAC541A2306ED709B51AACD79B313331E297495AC4E26ABD94030CC407FDCE26CFC0B7A2BE1F226906182863FF1F140D628FA02B1D0BE1A1697E2F9C17AEF5FEC32AA4D120FAF0F58474AC2718964033076286DC358F5E96B08A5C83323CA4CA2B881D8FFA8E0EA3DB9A90863D378A3349EBF189030DA11891C6695950C5B93CBD440D998E67C0137655AC9DA40826B51211AD5985918336939131A4D1BF9E9A2A88CFB3C5059E3D1CE0D9B19F487EC018C60A0279E24270C7090006A4EC728E5C4A1F86829AFA236AF9979165DF1E7F0F5544217864F9C348ABF2B17C2FCD7DDE35838A99BC4A7256821A0820476308204723082046E30820356A0030201020203358762300D06092A864886F70D01010B0500305D310B300906035504061302555331183016060355040A130F552E532E20476F7665726E6D656E74310C300A060355040B1303446F44310C300A060355040B1303504B49311830160603550403130F444F4420454D41494C2043412D3539301E170D3232303132353030303035315A170D3232303331313030303035315A3064310B300906035504061302555331183016060355040A130F552E532E20476F7665726E6D656E74310C300A060355040B1303446F44310C300A060355040B1303504B49311F301D06035504031316444F44204E4950524E4554204F43535020313244203230820122300D06092A864886F70D01010105000382010F003082010A0282010100BF8A691DDB447562EFCDCB6C4DED76DDB200D1A188E01A3BA4EEEE1C42A7E5E78C0784C512F5CF64DD1A407A2EBC1F280761C1372351E03AED90C2DA0556066398E555EEE91C92988A62CDA1FD1F4E2E5F6EC6C022821C0D7C8B0E90E7CCF203CE8F102CBF4756D9B8CD09A2F16CF08F1E944311C8CD2120B98F6BCAEA1E60D1841A933815C8936BD20CC984FA1F5BF7AFFFFF2886DE53FDF3AE80E8394E09D441AE615693A78FE0051942B724147595DF049382897E26BBEA4E6902376C494F31141D1693005AA4E8AEBE4FEC342D10BFD90661935FB00D61A1FF7CB3B8FF45E473B3FFDD6D2C692FA8C0DE23373F1A6AD84109E7A9274008C5E94E28CE91850203010001A382012E3082012A301F0603551D23041830168014771441A65D9526D01DFF953B628CEAB7B55D3B92304606082B06010505070101043A3038303606082B06010505073002862A687474703A2F2F63726C2E646973612E6D696C2F7369676E2F444F44454D41494C43415F35392E63657230160603551D250101FF040C300A06082B06010505070309300E0603551D0F0101FF040403020780300F06092B060105050730010504020500301D0603551D0E04160414ADB0A9B2DDE9D444B4DF80F599598E84AC5EC68730670603551D200460305E300B0609608648016502010B24300B0609608648016502010B27300B0609608648016502010B2A300B0609608648016502010B3B300C060A6086480165030201030D300C060A60864801650302010311300C060A60864801650302010327300D06092A864886F70D01010B0500038201010047FE1DF6D439EF205C931F34B8C802B6E6D9C4EFEAFA7228FD07E56DFF17EEC18C250941EB46D5F416A02F2706A951A2A16435C589EE1059BB86EC38C7F07C2C6660C4B25C8918115FFD21A0D9798B860F878122B3781DCA004AFE869B4B88E0B8ABB4C3A255BC034B36ED82E4692AB765667E3036FC4BB2A53EF9B4654D5C8FDA62E1F6B9DFC7382FA9A3BEAC5EB35A105CB6FFD7B33D561DA77B886D17F58F9D6EDC722C9786E7C664598116F9ACB41282FB0C75EF6BA87BAE97249A42DF45FEC6AEB724C03D8EE5FF4FD7753AC0FE50CB6B746544D4A4DB977946D29183295B160538338FA2FB27F6BC84B5CBD658A431A8B87A9E20A57753BC8AB964B486")[..]) +} diff --git a/pkcs7/Cargo.toml b/pkcs7/Cargo.toml index 68f31549c..3917c7cbe 100644 --- a/pkcs7/Cargo.toml +++ b/pkcs7/Cargo.toml @@ -17,6 +17,7 @@ rust-version = "1.57" [dependencies] der = { version = "=0.6.0-pre.1", features = ["oid"], path = "../der" } spki = { version = "=0.6.0-pre.0", path = "../spki" } +x509 = { version = "=0.0.1", path = "../x509" } [dev-dependencies] hex-literal = "0.3" diff --git a/pkcs7/src/cryptographic_message_syntax2004.rs b/pkcs7/src/cryptographic_message_syntax2004.rs new file mode 100644 index 000000000..9f4a6efee --- /dev/null +++ b/pkcs7/src/cryptographic_message_syntax2004.rs @@ -0,0 +1,378 @@ +//! Selected structures from RFC5652 + +use core::cmp::Ordering; +use der::asn1::{BitString, OctetString, SetOf, SetOfVec, UIntBytes}; +use der::{Any, Choice, Decodable, Sequence, ValueOrd}; +use spki::{AlgorithmIdentifier, ObjectIdentifier}; +use x509::attr::AttributeTypeAndValue; +use x509::ext::pkix::SubjectKeyIdentifier; +use x509::name::Name; + +// Use 2004 suffix to distinguish from the enum in content_type.rs +/// ContentInfo ::= SEQUENCE { +/// contentType ContentType, +/// content \[0\] EXPLICIT ANY DEFINED BY contentType } +#[derive(Clone, Eq, PartialEq, Sequence)] +pub struct ContentInfo2004<'a> { + /// contentType ContentType, + pub content_type: ObjectIdentifier, + /// content \[0\] EXPLICIT ANY DEFINED BY contentType } + #[asn1(context_specific = "0", tag_mode = "EXPLICIT")] + pub content: Any<'a>, +} + +/// SignedData ::= SEQUENCE { +/// version CMSVersion, +/// digestAlgorithms DigestAlgorithmIdentifiers, +/// encapContentInfo EncapsulatedContentInfo, +/// certificates \[0\] IMPLICIT CertificateSet OPTIONAL, +/// crls \[1\] IMPLICIT RevocationInfoChoices OPTIONAL, +/// signerInfos SignerInfos } +#[derive(Clone, Eq, PartialEq, Sequence)] +pub struct SignedData<'a> { + /// version CMSVersion, + pub version: u8, + /// digestAlgorithms DigestAlgorithmIdentifiers, + pub digest_algorithms: DigestAlgorithmIdentifiers<'a>, + /// encapContentInfo EncapsulatedContentInfo, + pub encap_content_info: EncapsulatedContentInfo<'a>, + // Using Any as a means of deferring most of the decoding of the certificates (will still need + // to call to_vec on the resulting Any to restore tag and length values). + /// certificates \[0\] IMPLICIT CertificateSet OPTIONAL, + #[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")] + pub certificates: Option>>, + /// crls \[1\] IMPLICIT RevocationInfoChoices OPTIONAL, + #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] + pub crls: Option>>, + /// signerInfos SignerInfos } + pub signer_infos: SetOfVec>, +} + +/// DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier +pub type DigestAlgorithmIdentifiers<'a> = SetOfVec; + +/// EncapsulatedContentInfo ::= SEQUENCE { +/// eContentType ContentType, +/// eContent \[0\] EXPLICIT OCTET STRING OPTIONAL } +#[derive(Clone, Eq, PartialEq, Sequence)] +pub struct EncapsulatedContentInfo<'a> { + /// eContentType ContentType, + pub econtent_type: ObjectIdentifier, + /// eContent \[0\] EXPLICIT OCTET STRING OPTIONAL } + #[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")] + pub econtent: Option>, +} + +// ContentType ::= OBJECT IDENTIFIER + +/// SignerInfo ::= SEQUENCE { +/// version CMSVersion, +/// sid SignerIdentifier, +/// digestAlgorithm DigestAlgorithmIdentifier, +/// signedAttrs \[0\] IMPLICIT SignedAttributes OPTIONAL, +/// signatureAlgorithm SignatureAlgorithmIdentifier, +/// signature SignatureValue, +/// unsignedAttrs \[1\] IMPLICIT UnsignedAttributes OPTIONAL } +#[derive(Clone, Eq, PartialEq, PartialOrd, Sequence)] +pub struct SignerInfo<'a> { + /// version CMSVersion, + pub version: u8, + /// sid SignerIdentifier, + pub sid: SignerIdentifier<'a>, + /// digestAlgorithm DigestAlgorithmIdentifier, + pub digest_algorithm: AlgorithmIdentifier<'a>, + /// signedAttrs \[0\] IMPLICIT SignedAttributes OPTIONAL, + pub signed_attrs: SignedAttributes<'a>, + /// signatureAlgorithm SignatureAlgorithmIdentifier, + pub signature_algorithm: AlgorithmIdentifier<'a>, + /// signature SignatureValue, + pub signature: BitString<'a>, + /// unsignedAttrs \[1\] IMPLICIT UnsignedAttributes OPTIONAL } + pub unsigned_attrs: UnsignedAttributes<'a>, +} +impl ValueOrd for SignerInfo<'_> { + fn value_cmp(&self, _other: &Self) -> der::Result { + todo!() + } +} + +/// SignerIdentifier ::= CHOICE { +/// issuerAndSerialNumber IssuerAndSerialNumber, +/// subjectKeyIdentifier \[0\] SubjectKeyIdentifier } +#[derive(Clone, Eq, PartialEq, PartialOrd, Choice)] +pub enum SignerIdentifier<'a> { + /// issuerAndSerialNumber IssuerAndSerialNumber, + IssuerAndSerialNumber(IssuerAndSerialNumber<'a>), + /// subjectKeyIdentifier \[0\] SubjectKeyIdentifier } + #[asn1(context_specific = "0", tag_mode = "EXPLICIT")] + SubjectKeyIdentifier(SubjectKeyIdentifier<'a>), +} + +/// SignedAttributes ::= SET SIZE (1..MAX) OF Attribute +pub type SignedAttributes<'a> = SetOf, 10>; + +/// UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute +pub type UnsignedAttributes<'a> = SetOf, 10>; + +/* + Attribute ::= SEQUENCE { + attrType OBJECT IDENTIFIER, + attrValues SET OF AttributeValue } + + AttributeValue ::= ANY + + SignatureValue ::= OCTET STRING + + EnvelopedData ::= SEQUENCE { + version CMSVersion, + originatorInfo \[0\] IMPLICIT OriginatorInfo OPTIONAL, + recipientInfos RecipientInfos, + encryptedContentInfo EncryptedContentInfo, + unprotectedAttrs \[1\] IMPLICIT UnprotectedAttributes OPTIONAL } + + OriginatorInfo ::= SEQUENCE { + certs \[0\] IMPLICIT CertificateSet OPTIONAL, + crls \[1\] IMPLICIT RevocationInfoChoices OPTIONAL } + + RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo + + EncryptedContentInfo ::= SEQUENCE { + contentType ContentType, + contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + encryptedContent \[0\] IMPLICIT EncryptedContent OPTIONAL } + + EncryptedContent ::= OCTET STRING + + UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute + + RecipientInfo ::= CHOICE { + ktri KeyTransRecipientInfo, + kari \[1\] KeyAgreeRecipientInfo, + kekri \[2\] KEKRecipientInfo, + pwri \[3\] PasswordRecipientInfo, + ori \[4\] OtherRecipientInfo } + + EncryptedKey ::= OCTET STRING + + KeyTransRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 0 or 2 + rid RecipientIdentifier, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey EncryptedKey } + + RecipientIdentifier ::= CHOICE { + issuerAndSerialNumber IssuerAndSerialNumber, + subjectKeyIdentifier \[0\] SubjectKeyIdentifier } + + KeyAgreeRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 3 + originator \[0\] EXPLICIT OriginatorIdentifierOrKey, + ukm \[1\] EXPLICIT UserKeyingMaterial OPTIONAL, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + recipientEncryptedKeys RecipientEncryptedKeys } + + OriginatorIdentifierOrKey ::= CHOICE { + issuerAndSerialNumber IssuerAndSerialNumber, + subjectKeyIdentifier \[0\] SubjectKeyIdentifier, + originatorKey \[1\] OriginatorPublicKey } + + OriginatorPublicKey ::= SEQUENCE { + algorithm AlgorithmIdentifier, + publicKey BIT STRING } + + RecipientEncryptedKeys ::= SEQUENCE OF RecipientEncryptedKey + + RecipientEncryptedKey ::= SEQUENCE { + rid KeyAgreeRecipientIdentifier, + encryptedKey EncryptedKey } + + KeyAgreeRecipientIdentifier ::= CHOICE { + issuerAndSerialNumber IssuerAndSerialNumber, + rKeyId \[0\] IMPLICIT RecipientKeyIdentifier } + + RecipientKeyIdentifier ::= SEQUENCE { + subjectKeyIdentifier SubjectKeyIdentifier, + date GeneralizedTime OPTIONAL, + other OtherKeyAttribute OPTIONAL } + + SubjectKeyIdentifier ::= OCTET STRING + + KEKRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 4 + kekid KEKIdentifier, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey EncryptedKey } + + KEKIdentifier ::= SEQUENCE { + keyIdentifier OCTET STRING, + date GeneralizedTime OPTIONAL, + other OtherKeyAttribute OPTIONAL } + + PasswordRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 0 + keyDerivationAlgorithm \[0\] KeyDerivationAlgorithmIdentifier + OPTIONAL, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey EncryptedKey } + + OtherRecipientInfo ::= SEQUENCE { + oriType OBJECT IDENTIFIER, + oriValue ANY DEFINED BY oriType } + + DigestedData ::= SEQUENCE { + version CMSVersion, + digestAlgorithm DigestAlgorithmIdentifier, + encapContentInfo EncapsulatedContentInfo, + digest Digest } + + Digest ::= OCTET STRING + + EncryptedData ::= SEQUENCE { + version CMSVersion, + encryptedContentInfo EncryptedContentInfo, + unprotectedAttrs \[1\] IMPLICIT UnprotectedAttributes OPTIONAL } + + AuthenticatedData ::= SEQUENCE { + version CMSVersion, + originatorInfo \[0\] IMPLICIT OriginatorInfo OPTIONAL, + recipientInfos RecipientInfos, + macAlgorithm MessageAuthenticationCodeAlgorithm, + digestAlgorithm \[1\] DigestAlgorithmIdentifier OPTIONAL, + encapContentInfo EncapsulatedContentInfo, + authAttrs \[2\] IMPLICIT AuthAttributes OPTIONAL, + mac MessageAuthenticationCode, + unauthAttrs \[3\] IMPLICIT UnauthAttributes OPTIONAL } + + AuthAttributes ::= SET SIZE (1..MAX) OF Attribute + + UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute + + MessageAuthenticationCode ::= OCTET STRING + + DigestAlgorithmIdentifier ::= AlgorithmIdentifier + + SignatureAlgorithmIdentifier ::= AlgorithmIdentifier + + KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + + ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + + MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier + + KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier + + RevocationInfoChoices ::= SET OF RevocationInfoChoice + + RevocationInfoChoice ::= CHOICE { + crl CertificateList, + other \[1\] IMPLICIT OtherRevocationInfoFormat } + + OtherRevocationInfoFormat ::= SEQUENCE { + otherRevInfoFormat OBJECT IDENTIFIER, + otherRevInfo ANY DEFINED BY otherRevInfoFormat } + + CertificateChoices ::= CHOICE { + certificate Certificate, + extendedCertificate \[0\] IMPLICIT ExtendedCertificate, -- Obsolete + v1AttrCert \[1\] IMPLICIT AttributeCertificateV1, -- Obsolete + v2AttrCert \[2\] IMPLICIT AttributeCertificateV2, + other \[3\] IMPLICIT OtherCertificateFormat } + + AttributeCertificateV2 ::= AttributeCertificate + + OtherCertificateFormat ::= SEQUENCE { + otherCertFormat OBJECT IDENTIFIER, + otherCert ANY DEFINED BY otherCertFormat } + + CertificateSet ::= SET OF CertificateChoices +*/ + +/// IssuerAndSerialNumber ::= SEQUENCE { +/// issuer Name, +/// serialNumber CertificateSerialNumber } +#[derive(Clone, Eq, PartialEq, PartialOrd, Sequence)] +pub struct IssuerAndSerialNumber<'a> { + /// issuer Name, + pub issuer: Name<'a>, + /// serialNumber CertificateSerialNumber } + pub serial_number: UIntBytes<'a>, +} + +/* + CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) } + + UserKeyingMaterial ::= OCTET STRING + + OtherKeyAttribute ::= SEQUENCE { + keyAttrId OBJECT IDENTIFIER, + keyAttr ANY DEFINED BY keyAttrId OPTIONAL } + + -- Content Type Object Identifiers + + id-ct-contentInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-ct(1) 6 } + + id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 } + + id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 } + + id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 } + + id-digestedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 5 } + + id-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 } + + id-ct-authData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-ct(1) 2 } + + -- The CMS Attributes + + MessageDigest ::= OCTET STRING + + SigningTime ::= Time + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Countersignature ::= SignerInfo + + -- Attribute Object Identifiers + + id-contentType OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 3 } + + id-messageDigest OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 4 } + + id-signingTime OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 5 } + + id-countersignature OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 6 } + + -- Obsolete Extended Certificate syntax from PKCS #6 + + ExtendedCertificateOrCertificate ::= CHOICE { + certificate Certificate, + extendedCertificate \[0\] IMPLICIT ExtendedCertificate } + + ExtendedCertificate ::= SEQUENCE { + extendedCertificateInfo ExtendedCertificateInfo, + signatureAlgorithm SignatureAlgorithmIdentifier, + signature Signature } + + ExtendedCertificateInfo ::= SEQUENCE { + version CMSVersion, + certificate Certificate, + attributes UnauthAttributes } + + Signature ::= BIT STRING + + END -- of CryptographicMessageSyntax2004 +*/ diff --git a/pkcs7/src/lib.rs b/pkcs7/src/lib.rs index 6a080bbf6..e066700fd 100644 --- a/pkcs7/src/lib.rs +++ b/pkcs7/src/lib.rs @@ -15,12 +15,15 @@ mod content_type; pub use crate::{content_info::ContentInfo, content_type::ContentType}; +pub mod cryptographic_message_syntax2004; pub mod data_content; pub mod encrypted_data_content; pub mod enveloped_data_content; use der::asn1::ObjectIdentifier; +extern crate alloc; + /// `pkcs-7` Object Identifier (OID). pub const PKCS_7_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.7"); diff --git a/pkcs7/tests/cryptographic_message_syntax2004.rs b/pkcs7/tests/cryptographic_message_syntax2004.rs new file mode 100644 index 000000000..e5b02cb6f --- /dev/null +++ b/pkcs7/tests/cryptographic_message_syntax2004.rs @@ -0,0 +1,23 @@ +use der::{Decodable, Encodable}; +use pkcs7::cryptographic_message_syntax2004::*; + +#[test] +fn signed_data_parse_test1() { + let der_encoded_sd = include_bytes!("examples/caCertsIssuedTofbcag4.p7c"); + let ci = ContentInfo2004::from_der(der_encoded_sd).unwrap(); + let content = ci.content.to_vec().unwrap(); + let _sd = SignedData::from_der(content.as_slice()).unwrap(); + //assert_eq!(1, sd.certificates.unwrap().len()); + + let der_encoded_sd = include_bytes!("examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c"); + let ci = ContentInfo2004::from_der(der_encoded_sd).unwrap(); + let content = ci.content.to_vec().unwrap(); + let sd = SignedData::from_der(content.as_slice()).unwrap(); + assert_eq!(1, sd.certificates.unwrap().len()); + + let der_encoded_sd = include_bytes!("examples/DODROOTCA3_IB.p7c"); + let ci = ContentInfo2004::from_der(der_encoded_sd).unwrap(); + let content = ci.content.to_vec().unwrap(); + let sd = SignedData::from_der(content.as_slice()).unwrap(); + assert_eq!(26, sd.certificates.unwrap().len()); +} diff --git a/pkcs7/tests/examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c b/pkcs7/tests/examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c new file mode 100644 index 0000000000000000000000000000000000000000..a37ec8b7c8fce72e1a80c40d8f73597f89ddef1a GIT binary patch literal 1935 zcmbW2c~BE)7>Dz1HXA}hGL)2~Qy?NBawOk|Tfr$LAQ6Wk25KD?Ls%d~NJ5BKL760$ z6c3b&gJ7uz#YzRHUUd+!mRgHF>~Kk~9ksAUV{=lOH%YwghO<#zy=#ek(a7jiIWHi~&5*^6;+=t+UAhag+LSS*@vSVfrhrG(y$ zyF&qGxE?O4ndyp%g-OY{8yUdy2$d;RDR=-BQONZ0Q4!^4Q7TbDnAAE^vPoM|M2KWb zqWMwK&&y4U!$>>^kB0HlkxNOGIw353)i^xp`uuLD+#tuNiOhzF!_enJW|B^{K%tU} z6nZmZG8oA~`C6UUyhfB|Fqo-KQfLDK&P@R<2Ga%v4Dz`vVgrD2!CPK3Bds_b>wI$g zaU1sefz-mTjUVVzzscq=ACrhi@)n(|32zfExc5VobkESvi+3tcjv<}9ayai>A0NCS zOIvcK|J*}8BkDxR@n!uXr$Q&2MjPrUVg{qO$4RU9G)%c_2CfxOR_gv>xH6`?R^8FsYUFJh*`c=QEEfr<`0esVy%z?dvc^s^MoGa=P28}`D zuUSH1`|jDXYkbzm_3F{E$<{A=s)jmDPnTQE{RO!<6OT2-mium4@_TrX;!;{zA1LZR zRX^XwyngeIb%T}L7{NPspJ;42@^S9du`6D+#qxiLH8JCL$8|Wu1Prjp#=H$}%yH;T zcGj1VdZNOu*F(C#8tc`B+_^eF)jw-#=|2|-pg*N&qU0D^g!lVgs!vLP%jF&%ZuYPW zSJ!rJuZMivQX1*W@o>D#4Acf#Bmn5;92Mg@Vk3Y%izd)%5lR!9X%Ps$ zX{BCvfQQlyI(>@Aq8W{o;m}Nmlfh_4>15b6lj&q!XvR5#E6m4S!Z5&Mu>lv&fhw5w zf+5SET5L8O=SUfY#kq89(YZtUI7}dyDlW7~d8PqhD+LL7z4+_$KZF z-7#0rw%`r*cf$*Sk0+CC@9S1gktI3rMDmY<+=+mZeoN|ox_xWs%&Mk@eZf<_o5c5d zqo!8gwOh-AOCs?@g|+)?iW@6RtBemuY8RcUs!q=qc8q$}xsT*D51VI9-Hta_z9naT znf~qOA%wYdVYcPa*&nMf5E8av+0nqCY6d~aWRK)_Z~L4vt+Mif!E*FWz;)ejr{_9yHep9i|^ z!Mzb>0d>t=?ys<|9Rk+&2l+mSny=tqqBD_ m9&+BVE?d3<;9q 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Ý{MTF7uaOeUaI4WbGlAyvd#-!o{k_llN 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 literal 0 HcmV?d00001 diff --git a/spki/src/algorithm.rs b/spki/src/algorithm.rs index 8997c4a60..678199f9a 100644 --- a/spki/src/algorithm.rs +++ b/spki/src/algorithm.rs @@ -14,7 +14,7 @@ use der::{Decodable, DecodeValue, Decoder, DerOrd, Encodable, Header, Sequence, /// ``` /// /// [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2 -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)] pub struct AlgorithmIdentifier<'a> { /// Algorithm OID, i.e. the `algorithm` field in the `AlgorithmIdentifier` /// ASN.1 schema. diff --git a/x509/Cargo.toml b/x509/Cargo.toml index a18ec4746..20802651d 100644 --- a/x509/Cargo.toml +++ b/x509/Cargo.toml @@ -12,7 +12,7 @@ categories = ["cryptography", "data-structures", "encoding", "no-std"] keywords = ["crypto"] readme = "README.md" edition = "2021" -rust-version = "1.56" +rust-version = "1.57" [dependencies] der = { version = "=0.6.0-pre.1", features = ["derive", "alloc", "flagset"], path = "../der" } diff --git a/x509/src/crl.rs b/x509/src/crl.rs new file mode 100644 index 000000000..9d5cf0829 --- /dev/null +++ b/x509/src/crl.rs @@ -0,0 +1,89 @@ +//! CertificateList [`CertificateList`] and TBSCertList [`TbsCertList`] as defined in RFC 5280 + +use crate::ext::Extensions; +use crate::name::Name; +use crate::time::Time; +use crate::Version; +use der::asn1::{BitString, UIntBytes}; +use der::Sequence; +use spki::AlgorithmIdentifier; + +///```text +/// CertificateList ::= SEQUENCE { +/// tbsCertList TBSCertList, +/// signatureAlgorithm AlgorithmIdentifier, +/// signatureValue BIT STRING } +/// ``` +/// [RFC 5280 Section 5.1]: +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct CertificateList<'a> { + /// tbsCertList TBSCertList, + pub tbs_cert_list: TbsCertList<'a>, + /// signatureAlgorithm AlgorithmIdentifier, + pub signature_algorithm: AlgorithmIdentifier<'a>, + /// signature BIT STRING + pub signature: BitString<'a>, +} + +/// Structure fabricated from the revokedCertificates definition in TBSCertList +/// +///```text +/// RevokedCert ::= SEQUENCE { +/// userCertificate CertificateSerialNumber, +/// revocationDate Time, +/// crlEntryExtensions Extensions OPTIONAL +/// } +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct RevokedCert<'a> { + /// userCertificate CertificateSerialNumber, + pub serial_number: UIntBytes<'a>, + /// revocationDate Time, + pub revocation_date: Time, + /// crlEntryExtensions Extensions OPTIONAL + pub crl_entry_extensions: Extensions<'a>, +} + +/// Structure fabricated from the revokedCertificates definition in TBSCertList +/// ```text +/// RevokedCerts ::= SEQUENCE OF RevokedCert; +/// ``` +pub type RevokedCerts<'a> = alloc::vec::Vec>; + +///```text +/// 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(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct TbsCertList<'a> { + /// version Version OPTIONAL, + /// -- if present, MUST be v2 + pub version: Version, + /// signature AlgorithmIdentifier, + pub signature: AlgorithmIdentifier<'a>, + /// issuer Name, + pub issuer: Name<'a>, + /// thisUpdate Time, + pub this_update: Time, + /// nextUpdate Time OPTIONAL, + pub next_update: Option