diff --git a/pkcs12/Cargo.toml b/pkcs12/Cargo.toml index 379fbde59..ded0edb69 100644 --- a/pkcs12/Cargo.toml +++ b/pkcs12/Cargo.toml @@ -17,3 +17,12 @@ rust-version = "1.65" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +der = { version = "=0.7.0-pre", features = ["oid", "pem"], path = "../der" } +spki = { version = "=0.7.0-pre", path = "../spki" } +x509-cert = { version = "=0.2.0-pre", path = "../x509-cert" } +pkcs7 = { version = "=0.4.0-pre.0", path = "../pkcs7" } +pkcs8 = { version = "=0.10.0-pre", features = ["pkcs5"], path = "../pkcs8" } +[dev-dependencies] +hex-literal = "0.3.3" \ No newline at end of file diff --git a/pkcs12/src/authenticated_safe.rs b/pkcs12/src/authenticated_safe.rs new file mode 100644 index 000000000..a5ce04331 --- /dev/null +++ b/pkcs12/src/authenticated_safe.rs @@ -0,0 +1,163 @@ +use alloc::vec::Vec; +use der::{ + asn1::{ContextSpecific, OctetStringRef}, + Decode, DecodeValue, Encode, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, + Sequence, Tag, TagMode, TagNumber, Writer, +}; +use pkcs7::encrypted_data_content::EncryptedDataContent; +use pkcs8::ObjectIdentifier; + +use crate::safe_bag::SafeContents; + +const CONTENT_TAG: TagNumber = TagNumber::new(0); + +/// sequence of AuthenticatedSafeItems +pub type AuthenticatedSafe<'a> = Vec>; + +/// TODO +#[derive(Clone, Debug)] +pub enum AuthenticatedSafeItem<'a> { + /// data safe + Data(Option>), + /// encrypted data safe + EncryptedData(Option>), + /// enveloped data safe + EnvelopedData(Option>), +} + +impl<'a> AuthenticatedSafeItem<'a> { + /// return content type of content info + pub fn content_type(&self) -> AuthenticatedSafeContentType { + match self { + Self::Data(_) => AuthenticatedSafeContentType::Data, + Self::EncryptedData(_) => AuthenticatedSafeContentType::EncryptedData, + Self::EnvelopedData(_) => AuthenticatedSafeContentType::EnvelopedData, + } + } +} + +impl<'a> DecodeValue<'a> for AuthenticatedSafeItem<'a> { + fn decode_value>( + reader: &mut R, + header: Header, + ) -> der::Result> { + reader.read_nested(header.length, |reader| { + let bag_type = reader.decode()?; + match bag_type { + AuthenticatedSafeContentType::Data => { + let inner = reader + .context_specific::>(CONTENT_TAG, TagMode::Explicit)?; + let contents = SafeContents::from_der(inner.unwrap().as_bytes())?; + Ok(AuthenticatedSafeItem::Data(Some(contents))) + } + AuthenticatedSafeContentType::EncryptedData => { + Ok(AuthenticatedSafeItem::EncryptedData( + reader.context_specific::>( + CONTENT_TAG, + TagMode::Explicit, + )?, + )) + } + AuthenticatedSafeContentType::EnvelopedData => { + Ok(AuthenticatedSafeItem::EnvelopedData( + reader + .context_specific::>(CONTENT_TAG, TagMode::Explicit)?, + )) + } + } + }) + } +} + +impl<'a> Sequence<'a> for AuthenticatedSafeItem<'a> { + fn fields(&self, f: F) -> der::Result + where + F: FnOnce(&[&dyn Encode]) -> der::Result, + { + match self { + AuthenticatedSafeItem::Data(data) => f(&[ + &self.content_type(), + &data.as_ref().map(|d| ContextSpecific { + tag_number: CONTENT_TAG, + tag_mode: TagMode::Explicit, + value: d.clone(), + }), + ]), + AuthenticatedSafeItem::EncryptedData(data) => f(&[ + &self.content_type(), + &data.as_ref().map(|d| ContextSpecific { + tag_number: CONTENT_TAG, + tag_mode: TagMode::Explicit, + value: d.clone(), + }), + ]), + AuthenticatedSafeItem::EnvelopedData(data) => f(&[ + &self.content_type(), + &data.as_ref().map(|d| ContextSpecific { + tag_number: CONTENT_TAG, + tag_mode: TagMode::Explicit, + value: d.clone(), + }), + ]), + } + } +} + +/// Indicates the type of content. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum AuthenticatedSafeContentType { + /// Plain data content type + Data, + + /// Enveloped-data content type + EnvelopedData, + + /// Encrypted-data content type + EncryptedData, +} + +impl<'a> DecodeValue<'a> for AuthenticatedSafeContentType { + fn decode_value>( + reader: &mut R, + header: Header, + ) -> der::Result { + ObjectIdentifier::decode_value(reader, header)?.try_into() + } +} + +impl EncodeValue for AuthenticatedSafeContentType { + fn value_len(&self) -> der::Result { + ObjectIdentifier::from(*self).value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> der::Result<()> { + ObjectIdentifier::from(*self).encode_value(writer) + } +} + +impl FixedTag for AuthenticatedSafeContentType { + const TAG: Tag = Tag::ObjectIdentifier; +} + +impl From for ObjectIdentifier { + fn from(content_type: AuthenticatedSafeContentType) -> ObjectIdentifier { + match content_type { + AuthenticatedSafeContentType::Data => pkcs7::PKCS_7_DATA_OID, + AuthenticatedSafeContentType::EnvelopedData => pkcs7::PKCS_7_ENVELOPED_DATA_OID, + AuthenticatedSafeContentType::EncryptedData => pkcs7::PKCS_7_ENCRYPTED_DATA_OID, + } + } +} + +impl TryFrom for AuthenticatedSafeContentType { + type Error = der::Error; + + fn try_from(oid: ObjectIdentifier) -> der::Result { + match oid { + pkcs7::PKCS_7_DATA_OID => Ok(Self::Data), + pkcs7::PKCS_7_ENVELOPED_DATA_OID => Ok(Self::EnvelopedData), + pkcs7::PKCS_7_ENCRYPTED_DATA_OID => Ok(Self::EncryptedData), + _ => Err(ErrorKind::OidUnknown { oid }.into()), + } + } +} diff --git a/pkcs12/src/bag_type.rs b/pkcs12/src/bag_type.rs new file mode 100644 index 000000000..f8454aea7 --- /dev/null +++ b/pkcs12/src/bag_type.rs @@ -0,0 +1,73 @@ +use der::asn1::ObjectIdentifier; +use der::{DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Tag, Writer}; + +/// Indicates the type of content. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum BagType { + /// Plain data content type + Key, + + /// Signed-data content type + Pkcs8, + + /// Enveloped-data content type + Cert, + + /// Signed-and-enveloped-data content type + Crl, + + /// Digested-data content type + Secret, + + /// Encrypted-data content type + SafeContents, +} + +impl<'a> DecodeValue<'a> for BagType { + fn decode_value>(reader: &mut R, header: Header) -> der::Result { + ObjectIdentifier::decode_value(reader, header)?.try_into() + } +} + +impl EncodeValue for BagType { + fn value_len(&self) -> der::Result { + ObjectIdentifier::from(*self).value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> der::Result<()> { + ObjectIdentifier::from(*self).encode_value(writer) + } +} + +impl FixedTag for BagType { + const TAG: Tag = Tag::ObjectIdentifier; +} + +impl From for ObjectIdentifier { + fn from(content_type: BagType) -> ObjectIdentifier { + match content_type { + BagType::Key => crate::PKCS_12_KEY_BAG_OID, + BagType::Pkcs8 => crate::PKCS_12_PKCS8_KEY_BAG_OID, + BagType::Cert => crate::PKCS_12_CERT_BAG_OID, + BagType::Crl => crate::PKCS_12_CRL_BAG_OID, + BagType::Secret => crate::PKCS_12_SECRET_BAG_OID, + BagType::SafeContents => crate::PKCS_12_SAFE_CONTENTS_BAG_OID, + } + } +} + +impl TryFrom for BagType { + type Error = der::Error; + + fn try_from(oid: ObjectIdentifier) -> der::Result { + match oid { + crate::PKCS_12_KEY_BAG_OID => Ok(Self::Key), + crate::PKCS_12_PKCS8_KEY_BAG_OID => Ok(Self::Pkcs8), + crate::PKCS_12_CERT_BAG_OID => Ok(Self::Cert), + crate::PKCS_12_CRL_BAG_OID => Ok(Self::Crl), + crate::PKCS_12_SECRET_BAG_OID => Ok(Self::Secret), + crate::PKCS_12_SAFE_CONTENTS_BAG_OID => Ok(Self::SafeContents), + _ => Err(ErrorKind::OidUnknown { oid }.into()), + } + } +} diff --git a/pkcs12/src/cert_bag_content.rs b/pkcs12/src/cert_bag_content.rs new file mode 100644 index 000000000..9ac32ba4c --- /dev/null +++ b/pkcs12/src/cert_bag_content.rs @@ -0,0 +1,19 @@ +use der::{asn1::OctetString, Sequence, ValueOrd}; + +use crate::cert_type::CertType; + +/// ```text +/// CertBag ::= SEQUENCE { +/// certId BAG-TYPE.&id ({CertTypes}), +/// certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) +/// } +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] +pub struct CertBagContent { + /// the cert id + pub id: CertType, + + /// the cert value + #[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")] + pub bytes: Option, +} diff --git a/pkcs12/src/cert_type.rs b/pkcs12/src/cert_type.rs new file mode 100644 index 000000000..56d117b89 --- /dev/null +++ b/pkcs12/src/cert_type.rs @@ -0,0 +1,63 @@ +use core::cmp::Ordering; + +use der::asn1::ObjectIdentifier; +use der::{ + DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Tag, ValueOrd, Writer, +}; + +/// Indicates the type of content. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub enum CertType { + /// Plain data content type + X509, + + /// Signed-data content type + Sdsi, +} + +impl<'a> DecodeValue<'a> for CertType { + fn decode_value>(reader: &mut R, header: Header) -> der::Result { + ObjectIdentifier::decode_value(reader, header)?.try_into() + } +} + +impl EncodeValue for CertType { + fn value_len(&self) -> der::Result { + ObjectIdentifier::from(*self).value_len() + } + + fn encode_value(&self, writer: &mut dyn Writer) -> der::Result<()> { + ObjectIdentifier::from(*self).encode_value(writer) + } +} + +impl FixedTag for CertType { + const TAG: Tag = Tag::ObjectIdentifier; +} + +impl From for ObjectIdentifier { + fn from(content_type: CertType) -> ObjectIdentifier { + match content_type { + CertType::X509 => crate::PKCS_12_X509_CERT_OID, + CertType::Sdsi => crate::PKCS_12_SDSI_CERT_OID, + } + } +} + +impl TryFrom for CertType { + type Error = der::Error; + + fn try_from(oid: ObjectIdentifier) -> der::Result { + match oid { + crate::PKCS_12_X509_CERT_OID => Ok(Self::X509), + crate::PKCS_12_SDSI_CERT_OID => Ok(Self::Sdsi), + _ => Err(ErrorKind::OidUnknown { oid }.into()), + } + } +} + +impl ValueOrd for CertType { + fn value_cmp(&self, other: &Self) -> der::Result { + Ok(self.cmp(other)) + } +} diff --git a/pkcs12/src/content_info.rs b/pkcs12/src/content_info.rs new file mode 100644 index 000000000..d01584e31 --- /dev/null +++ b/pkcs12/src/content_info.rs @@ -0,0 +1,103 @@ +use der::{ + asn1::OctetStringRef, Decode, DecodeValue, Encode, Header, Reader, Sequence, TagMode, TagNumber, +}; +use pkcs7::ContentType; + +use crate::authenticated_safe::AuthenticatedSafe; + +const CONTENT_TAG: TagNumber = TagNumber::new(0); + +/// TODO +#[derive(Clone, Debug)] +pub enum ContentInfo<'a> { + /// Content type `data` + Data(Option>), + + /// Content type `encrypted-data` + SignedData(Option>), +} + +impl<'a> ContentInfo<'a> { + /// return content type of content info + pub fn content_type(&self) -> ContentType { + match self { + Self::Data(_) => ContentType::Data, + Self::SignedData(_) => ContentType::SignedData, + // _ => panic!("unsupported"), + } + } +} + +impl<'a> ContentInfo<'a> { + /// new ContentInfo of `data` content type + // TODO + // pub fn new_data(content: &'a [u8]) -> Self { + // ContentInfo::Data(Some(content.into())) + // } + + /// new ContentInfo of given content type with empty content + pub fn new_empty(content_type: ContentType) -> Self { + match content_type { + ContentType::Data => ContentInfo::Data(None), + ContentType::SignedData => ContentInfo::SignedData(None), + _ => panic!("Not supported here"), + } + } + + // /// new Content info of given content type with given raw content + // pub fn new_raw(content_type: ContentType, content: &'a [u8]) -> der::Result { + // Ok(ContentInfo::Other(( + // content_type, + // Some(OctetStringRef::new(content)?), + // ))) + // } +} + +impl<'a> DecodeValue<'a> for ContentInfo<'a> { + fn decode_value>(reader: &mut R, header: Header) -> der::Result> { + reader.read_nested(header.length, |reader| { + let content_type = reader.decode()?; + match content_type { + ContentType::Data => { + let inner = reader + .context_specific::>(CONTENT_TAG, TagMode::Explicit)?; + let content = AuthenticatedSafe::from_der(inner.unwrap().as_bytes())?; + Ok(ContentInfo::Data(Some(content))) + } + ContentType::SignedData => Ok(ContentInfo::SignedData( + reader.context_specific(CONTENT_TAG, TagMode::Explicit)?, + )), + + _ => panic!("Not supported here"), + } + }) + } +} + +impl<'a> Sequence<'a> for ContentInfo<'a> { + fn fields(&self, f: F) -> der::Result + where + F: FnOnce(&[&dyn Encode]) -> der::Result, + { + match self { + Self::Data(data) => f(&[ + &self.content_type(), + // TODO + // &data.as_ref().map(|d| ContextSpecific { + // tag_number: CONTENT_TAG, + // tag_mode: TagMode::Explicit, + // value: *d, + // }), + ]), + Self::SignedData(data) => f(&[ + &self.content_type(), + // TODO + // &data.as_ref().map(|d| ContextSpecific { + // tag_number: CONTENT_TAG, + // tag_mode: TagMode::Explicit, + // value: *d, + // }), + ]), + } + } +} diff --git a/pkcs12/src/digest_info.rs b/pkcs12/src/digest_info.rs new file mode 100644 index 000000000..d04e0f4cf --- /dev/null +++ b/pkcs12/src/digest_info.rs @@ -0,0 +1,16 @@ +use der::{asn1::OctetString, Sequence, ValueOrd}; +use pkcs7::signed_data_content::DigestAlgorithmIdentifier; + +/// ```text +/// DigestInfo ::= SEQUENCE { +/// digestAlgorithm DigestAlgorithmIdentifier, +/// digest Digest } +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] +pub struct DigestInfo<'a> { + /// the algorithm. + pub algorithm: DigestAlgorithmIdentifier<'a>, + + /// the digest + pub digest: OctetString, +} diff --git a/pkcs12/src/lib.rs b/pkcs12/src/lib.rs index d443fd7a2..43c367e2c 100644 --- a/pkcs12/src/lib.rs +++ b/pkcs12/src/lib.rs @@ -15,3 +15,54 @@ )] //! TODO: PKCS#12 crate +//! +//! +// #[cfg(feature = "pem")] + +use spki::ObjectIdentifier; +extern crate alloc; + +pub mod authenticated_safe; +pub mod bag_type; +pub mod cert_bag_content; +pub mod cert_type; +pub mod content_info; +pub mod digest_info; +pub mod mac_data; +pub mod pfx; +pub mod safe_bag; +// bag types + +/// `pkcs-12 keyBag` Object Identifier (OID). +pub const PKCS_12_KEY_BAG_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.12.10.1.1"); + +/// `pkcs-12 pkcs8ShroudedKeyBag` Object Identifier (OID). +pub const PKCS_12_PKCS8_KEY_BAG_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.12.10.1.2"); + +/// `pkcs-12 certBag` Object Identifier (OID). +pub const PKCS_12_CERT_BAG_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.12.10.1.3"); + +/// `pkcs-12 crlBag` Object Identifier (OID). +pub const PKCS_12_CRL_BAG_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.12.10.1.4"); + +/// `pkcs-12 secretBag` Object Identifier (OID). +pub const PKCS_12_SECRET_BAG_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.12.10.1.5"); + +/// `pkcs-12 safeContentsBag` Object Identifier (OID). +pub const PKCS_12_SAFE_CONTENTS_BAG_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.12.10.1.6"); + +// cert types + +/// `pkcs-9 x509Certificate for pkcs-12` Object Identifier (OID). +pub const PKCS_12_X509_CERT_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.22.1"); + +/// `pkcs-9 sdsiCertificate for pkcs-12` Object Identifier (OID). +pub const PKCS_12_SDSI_CERT_OID: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.22.2"); diff --git a/pkcs12/src/mac_data.rs b/pkcs12/src/mac_data.rs new file mode 100644 index 000000000..f0b0bf9dc --- /dev/null +++ b/pkcs12/src/mac_data.rs @@ -0,0 +1,26 @@ +use crate::digest_info::DigestInfo; +use der::{ + asn1::{Int, OctetString}, + Sequence, ValueOrd, +}; + +/// ```text +/// MacData ::= SEQUENCE { +/// mac DigestInfo, +/// macSalt OCTET STRING, +/// iterations INTEGER DEFAULT 1 +/// -- Note: The default is for historical reasons and its +/// -- use is deprecated. +///} +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] +pub struct MacData<'a> { + /// the MAC digest info + pub mac: DigestInfo<'a>, + + /// the MAC salt + pub mac_salt: OctetString, + + /// the number of iterations + pub iterations: Int, +} diff --git a/pkcs12/src/pfx.rs b/pkcs12/src/pfx.rs new file mode 100644 index 000000000..3a3638543 --- /dev/null +++ b/pkcs12/src/pfx.rs @@ -0,0 +1,40 @@ +use core::cmp::Ordering; + +use der::{Enumerated, Sequence, ValueOrd}; + +use crate::{content_info::ContentInfo, mac_data::MacData}; + +/// just the version v3 +#[derive(Clone, Copy, Debug, Enumerated, Eq, PartialEq, PartialOrd, Ord)] +#[asn1(type = "INTEGER")] +#[repr(u8)] +pub enum Version { + /// syntax version 3 + V3 = 3, +} + +impl ValueOrd for Version { + fn value_cmp(&self, other: &Self) -> der::Result { + Ok(self.cmp(other)) + } +} + +/// ```text +/// PFX ::= SEQUENCE { +/// version INTEGER {v3(3)}(v3,...), +/// authSafe ContentInfo, +/// macData MacData OPTIONAL +/// } +/// +/// ``` +#[derive(Debug, Sequence)] +pub struct Pfx<'a> { + /// the syntax version number. + pub version: Version, + + /// the authenticated safe + pub auth_safe: ContentInfo<'a>, + + /// the message digest info + pub mac_data: MacData<'a>, +} diff --git a/pkcs12/src/safe_bag.rs b/pkcs12/src/safe_bag.rs new file mode 100644 index 000000000..7430379dc --- /dev/null +++ b/pkcs12/src/safe_bag.rs @@ -0,0 +1,128 @@ +use alloc::vec::Vec; +use der::{ + asn1::{ContextSpecific, OctetString}, + DecodeValue, Encode, Header, Reader, Sequence, TagMode, TagNumber, +}; +use pkcs8::{EncryptedPrivateKeyInfo, PrivateKeyInfo}; +use x509_cert::attr::Attributes; + +use crate::{bag_type::BagType, cert_bag_content::CertBagContent}; + +type Placeholder = OctetString; + +const CONTENT_TAG: TagNumber = TagNumber::new(0); + +/// Sequence of SafeBag +pub type SafeContents<'a> = Vec>; + +/// +/// ```text +/// SafeBag ::= SEQUENCE { +/// bagId BAG-TYPE.&id ({PKCS12BagSet}) +/// bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), +/// bagAttributes SET OF PKCS12Attribute OPTIONAL +/// } +/// PKCS12BagSet BAG-TYPE ::= { +/// keyBag | +/// pkcs8ShroudedKeyBag | +/// certBag | +/// crlBag | +/// secretBag | +/// safeContentsBag, +/// .. -- For future extensions +/// } +/// PKCS12Attribute ::= SEQUENCE { +/// attrId ATTRIBUTE.&id ({PKCS12AttrSet}), +/// attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) +/// } -- This type is compatible with the X.500 type 'Attribute' +/// ``` +#[derive(Clone, Debug)] +pub enum SafeBag<'a> { + /// key bag + KeyBag(Option>, Option), + /// pkcs8 bag + Pkcs8ShroudedKeyBag(Option>, Option), + /// cert bag + CertBag(Option, Option), + /// crl bag currently unimplemented + CrlBag(Option, Option), + /// secret bag currently unimplemented + SecretBag(Option, Option), + /// safeContents bag currently unimplemented + SafeContentsBag(Option, Option), +} + +impl<'a> SafeBag<'a> { + /// return content type of content info + pub fn content_type(&self) -> BagType { + match self { + Self::KeyBag(_, _) => BagType::Key, + _ => panic!("content type"), + } + } +} + +impl<'a> DecodeValue<'a> for SafeBag<'a> { + fn decode_value>(reader: &mut R, header: Header) -> der::Result> { + reader.read_nested(header.length, |reader| { + let bag_type = reader.decode()?; + match bag_type { + BagType::Key => Ok(SafeBag::KeyBag( + reader + .context_specific::>(CONTENT_TAG, TagMode::Explicit)?, + reader.decode()?, + )), + BagType::Pkcs8 => Ok(SafeBag::Pkcs8ShroudedKeyBag( + reader.context_specific::>( + CONTENT_TAG, + TagMode::Explicit, + )?, + reader.decode()?, + )), + BagType::Cert => Ok(SafeBag::CertBag( + reader.context_specific::(CONTENT_TAG, TagMode::Explicit)?, + reader.decode()?, + )), + _ => todo!("Encoding of other types is currently not supported"), + } + }) + } +} + +impl<'a> Sequence<'a> for SafeBag<'a> { + fn fields(&self, f: F) -> der::Result + where + F: FnOnce(&[&dyn Encode]) -> der::Result, + { + match self { + Self::KeyBag(data, attrs) => f(&[ + &self.content_type(), + &data.as_ref().map(|d| ContextSpecific { + tag_number: CONTENT_TAG, + tag_mode: TagMode::Explicit, + value: d.clone(), + }), + &attrs, + ]), + Self::Pkcs8ShroudedKeyBag(data, attrs) => f(&[ + &self.content_type(), + &data.as_ref().map(|d| ContextSpecific { + tag_number: CONTENT_TAG, + tag_mode: TagMode::Explicit, + value: d.clone(), + }), + &attrs, + ]), + Self::CertBag(data, attrs) => f(&[ + &self.content_type(), + &data.as_ref().map(|d| ContextSpecific { + tag_number: CONTENT_TAG, + tag_mode: TagMode::Explicit, + value: d.clone(), + }), + &attrs, + ]), + _ => todo!("Encoding of other types is currently not supported"), + } + } +} diff --git a/pkcs12/tests/cert_tests.rs b/pkcs12/tests/cert_tests.rs new file mode 100644 index 000000000..ed046be22 --- /dev/null +++ b/pkcs12/tests/cert_tests.rs @@ -0,0 +1,27 @@ +use der::Decode; + +use pkcs12::{content_info::ContentInfo, pfx::Pfx}; + +#[test] +fn decode_sample_pfx() -> der::Result<()> { + let bytes = include_bytes!("examples/example.pfx"); + + let content = Pfx::from_der(bytes).expect("expected valid data"); + + match content.auth_safe { + ContentInfo::Data(_) => Ok(()), + ContentInfo::SignedData(_) => todo!(), + } +} + +#[test] +fn decode_sample_pfx2() -> der::Result<()> { + let bytes = include_bytes!("examples/example2.pfx"); + + let content = Pfx::from_der(bytes).expect("expected valid data"); + + match content.auth_safe { + ContentInfo::Data(_) => Ok(()), + ContentInfo::SignedData(_) => todo!(), + } +} diff --git a/pkcs12/tests/examples/cert.pem b/pkcs12/tests/examples/cert.pem new file mode 100644 index 000000000..3f7664224 --- /dev/null +++ b/pkcs12/tests/examples/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICzDCCAjWgAwIBAgIUZMDrcllVoypmH9d8mGdPACkwWRswDQYJKoZIhvcNAQEL +BQAweDELMAkGA1UEBhMCREsxFDASBgNVBAgMC0hvdmVkc3RhZGVuMRUwEwYDVQQH +DAxLw4PCuGJlbmhhdm4xDDAKBgNVBAoMAy4uLjEMMAoGA1UECwwDLi4uMQwwCgYD +VQQDDAMuLi4xEjAQBgkqhkiG9w0BCQEWAy4uLjAeFw0yMzAxMDMxNzIwNTBaFw0y +NDAxMDMxNzIwNTBaMHgxCzAJBgNVBAYTAkRLMRQwEgYDVQQIDAtIb3ZlZHN0YWRl +bjEVMBMGA1UEBwwMS8ODwrhiZW5oYXZuMQwwCgYDVQQKDAMuLi4xDDAKBgNVBAsM +Ay4uLjEMMAoGA1UEAwwDLi4uMRIwEAYJKoZIhvcNAQkBFgMuLi4wgZ8wDQYJKoZI +hvcNAQEBBQADgY0AMIGJAoGBALFxLL2rx1ilbfXiI3iMIrjIenu3ucHysMYh0t1f +6mTyAqDsg7C5VEh5CESFbGEc10vu5ZrGfx3iREpFyuK/sMf4DJ+JHNw5nemMBcRy +edHccx2qLDqeTmtwRQCPaSJm6D1pGACRRjpDMl/s+FFkxPUBeGGerkJlbow+rC5A +nZS3AgMBAAGjUzBRMB0GA1UdDgQWBBR6gNs30Q3+JPqx0nTdyW1ODBWUQzAfBgNV +HSMEGDAWgBR6gNs30Q3+JPqx0nTdyW1ODBWUQzAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4GBAAMvY8V9Si+1ZjGRQCqSTtlkaakYOEfgnfqXOUbGNnO6 +7vsJbSX3S0CG0ciMbaIxyMG2TkRzSfJiODOblC5RnXg7EF0nU0oNBIYw3FY40lTD +rCgqQV4aEYScPCmuHS7pGM87VTrcxbMY+1vPq6iSadn4ijifOaBpj7Z5esWwVaUp +-----END CERTIFICATE----- diff --git a/pkcs12/tests/examples/example.pfx b/pkcs12/tests/examples/example.pfx new file mode 100644 index 000000000..d591096d8 Binary files /dev/null and b/pkcs12/tests/examples/example.pfx differ diff --git a/pkcs12/tests/examples/example2.pfx b/pkcs12/tests/examples/example2.pfx new file mode 100644 index 000000000..6ff4319f7 Binary files /dev/null and b/pkcs12/tests/examples/example2.pfx differ diff --git a/pkcs12/tests/examples/gen.sh b/pkcs12/tests/examples/gen.sh new file mode 100644 index 000000000..02b71df7d --- /dev/null +++ b/pkcs12/tests/examples/gen.sh @@ -0,0 +1,3 @@ +openssl req -x509 -newkey rsa:1024 -keyout key.pem -out cert.pem -sha256 -days 365 -noenc -subj "/C=DK/ST=Hovedstaden/L=København/O=.../OU=.../CN=.../emailAddress=..." +openssl pkcs12 -export -out example.pfx -inkey key.pem -in cert.pem -passout pass: +openssl pkcs12 -export -out example2.pfx -in cert.pem -inkey key.pem -certpbe NONE -passout pass:1234 \ No newline at end of file diff --git a/pkcs12/tests/examples/key.pem b/pkcs12/tests/examples/key.pem new file mode 100644 index 000000000..2e4e9f85f --- /dev/null +++ b/pkcs12/tests/examples/key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALFxLL2rx1ilbfXi +I3iMIrjIenu3ucHysMYh0t1f6mTyAqDsg7C5VEh5CESFbGEc10vu5ZrGfx3iREpF +yuK/sMf4DJ+JHNw5nemMBcRyedHccx2qLDqeTmtwRQCPaSJm6D1pGACRRjpDMl/s ++FFkxPUBeGGerkJlbow+rC5AnZS3AgMBAAECgYBoT+8MZbKgI0hcVx+hG0jCNmEC +4AQcx04ye+nZaCyEQV1YOxJDzv+ER1qb5Y/MG0daBUwHTA+ogr7ApvzZhfUm7qB/ +ISf57QmoJc+60x8u45VXxOSMPuTHXWAwY56kSLglFbDFyy0dA02vsXo3PZ/DLa8Q +sEaGmZUMJYgWUGBX+QJBAN1EQE7jiPs3tOizuCNrw8LIxkMPjrm7FU0BTA3CcmqA +K5FXh6WDTeMvp+fstjscpWrg9buSUTBWHR8uO0AUKBUCQQDNS846s57CtFbcldOL +UMfvEkZ73HuaXF6wPb8SoaUPR/9AhQaWCfQuBkzi4rW8ZGPigxl4hS6BA2zkqIk8 +SBCbAkEA0Na6W7smbvYFKh12jvgHrLETb/gfHe4WDLhMsC/3Dc4rUOLshKuJuAQi +1iP1W5WOC3KIfKF9P8IHeoaIJdLggQJBALQeuoZOahCyYTOQUNZ+vaxIAIdT3y6D +tKA0zJvwLv3FUXKuRCUH/rES3gqClqj/+5MVKxfO4gpXkwbbx+yX3dkCQQDaQrrB +HvJ3l5F5SY5jFOsQY27dfXqdhbTRTQtE+uqAd3lwOix0mjROAfiXXXJ6I95cXUFb +EtKC5DoanfxFWn49 +-----END PRIVATE KEY-----