From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- vendor/pkcs8/src/encrypted_private_key_info.rs | 166 ++++++++++++++ vendor/pkcs8/src/error.rs | 93 ++++++++ vendor/pkcs8/src/lib.rs | 104 +++++++++ vendor/pkcs8/src/private_key_info.rs | 293 +++++++++++++++++++++++++ vendor/pkcs8/src/traits.rs | 145 ++++++++++++ vendor/pkcs8/src/version.rs | 63 ++++++ 6 files changed, 864 insertions(+) create mode 100644 vendor/pkcs8/src/encrypted_private_key_info.rs create mode 100644 vendor/pkcs8/src/error.rs create mode 100644 vendor/pkcs8/src/lib.rs create mode 100644 vendor/pkcs8/src/private_key_info.rs create mode 100644 vendor/pkcs8/src/traits.rs create mode 100644 vendor/pkcs8/src/version.rs (limited to 'vendor/pkcs8/src') diff --git a/vendor/pkcs8/src/encrypted_private_key_info.rs b/vendor/pkcs8/src/encrypted_private_key_info.rs new file mode 100644 index 000000000..460e3f6e3 --- /dev/null +++ b/vendor/pkcs8/src/encrypted_private_key_info.rs @@ -0,0 +1,166 @@ +//! PKCS#8 `EncryptedPrivateKeyInfo` + +use crate::{Error, Result}; +use core::fmt; +use der::{asn1::OctetStringRef, Decode, DecodeValue, Encode, Header, Reader, Sequence}; +use pkcs5::EncryptionScheme; + +#[cfg(feature = "alloc")] +use der::SecretDocument; + +#[cfg(feature = "encryption")] +use { + pkcs5::pbes2, + rand_core::{CryptoRng, RngCore}, +}; + +#[cfg(feature = "pem")] +use der::pem::PemLabel; + +/// PKCS#8 `EncryptedPrivateKeyInfo`. +/// +/// ASN.1 structure containing a PKCS#5 [`EncryptionScheme`] identifier for a +/// password-based symmetric encryption scheme and encrypted private key data. +/// +/// ## Schema +/// Structure described in [RFC 5208 Section 6]: +/// +/// ```text +/// EncryptedPrivateKeyInfo ::= SEQUENCE { +/// encryptionAlgorithm EncryptionAlgorithmIdentifier, +/// encryptedData EncryptedData } +/// +/// EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier +/// +/// EncryptedData ::= OCTET STRING +/// ``` +/// +/// [RFC 5208 Section 6]: https://tools.ietf.org/html/rfc5208#section-6 +#[cfg_attr(docsrs, doc(cfg(feature = "pkcs5")))] +#[derive(Clone, Eq, PartialEq)] +pub struct EncryptedPrivateKeyInfo<'a> { + /// Algorithm identifier describing a password-based symmetric encryption + /// scheme used to encrypt the `encrypted_data` field. + pub encryption_algorithm: EncryptionScheme<'a>, + + /// Private key data + pub encrypted_data: &'a [u8], +} + +impl<'a> EncryptedPrivateKeyInfo<'a> { + /// Attempt to decrypt this encrypted private key using the provided + /// password to derive an encryption key. + #[cfg(feature = "encryption")] + #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] + pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Result { + Ok(self + .encryption_algorithm + .decrypt(password, self.encrypted_data)? + .try_into()?) + } + + /// Encrypt the given ASN.1 DER document using a symmetric encryption key + /// derived from the provided password. + #[cfg(feature = "encryption")] + #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] + pub(crate) fn encrypt( + mut rng: impl CryptoRng + RngCore, + password: impl AsRef<[u8]>, + doc: &[u8], + ) -> Result { + let mut salt = [0u8; 16]; + rng.fill_bytes(&mut salt); + + let mut iv = [0u8; 16]; + rng.fill_bytes(&mut iv); + + let pbes2_params = pbes2::Parameters::scrypt_aes256cbc(Default::default(), &salt, &iv)?; + EncryptedPrivateKeyInfo::encrypt_with(pbes2_params, password, doc) + } + + /// Encrypt this private key using a symmetric encryption key derived + /// from the provided password and [`pbes2::Parameters`]. + #[cfg(feature = "encryption")] + #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] + pub(crate) fn encrypt_with( + pbes2_params: pbes2::Parameters<'a>, + password: impl AsRef<[u8]>, + doc: &[u8], + ) -> Result { + let encrypted_data = pbes2_params.encrypt(password, doc)?; + + EncryptedPrivateKeyInfo { + encryption_algorithm: pbes2_params.into(), + encrypted_data: &encrypted_data, + } + .try_into() + } +} + +impl<'a> DecodeValue<'a> for EncryptedPrivateKeyInfo<'a> { + fn decode_value>( + reader: &mut R, + header: Header, + ) -> der::Result> { + reader.read_nested(header.length, |reader| { + Ok(Self { + encryption_algorithm: reader.decode()?, + encrypted_data: OctetStringRef::decode(reader)?.as_bytes(), + }) + }) + } +} + +impl<'a> Sequence<'a> for EncryptedPrivateKeyInfo<'a> { + fn fields(&self, f: F) -> der::Result + where + F: FnOnce(&[&dyn Encode]) -> der::Result, + { + f(&[ + &self.encryption_algorithm, + &OctetStringRef::new(self.encrypted_data)?, + ]) + } +} + +impl<'a> TryFrom<&'a [u8]> for EncryptedPrivateKeyInfo<'a> { + type Error = Error; + + fn try_from(bytes: &'a [u8]) -> Result { + Ok(Self::from_der(bytes)?) + } +} + +impl<'a> fmt::Debug for EncryptedPrivateKeyInfo<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EncryptedPrivateKeyInfo") + .field("encryption_algorithm", &self.encryption_algorithm) + .finish_non_exhaustive() + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "pkcs5"))))] +impl TryFrom> for SecretDocument { + type Error = Error; + + fn try_from(encrypted_private_key: EncryptedPrivateKeyInfo<'_>) -> Result { + SecretDocument::try_from(&encrypted_private_key) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "pkcs5"))))] +impl TryFrom<&EncryptedPrivateKeyInfo<'_>> for SecretDocument { + type Error = Error; + + fn try_from(encrypted_private_key: &EncryptedPrivateKeyInfo<'_>) -> Result { + Ok(Self::encode_msg(encrypted_private_key)?) + } +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl PemLabel for EncryptedPrivateKeyInfo<'_> { + const PEM_LABEL: &'static str = "ENCRYPTED PRIVATE KEY"; +} diff --git a/vendor/pkcs8/src/error.rs b/vendor/pkcs8/src/error.rs new file mode 100644 index 000000000..bc4c2eafe --- /dev/null +++ b/vendor/pkcs8/src/error.rs @@ -0,0 +1,93 @@ +//! Error types + +use core::fmt; + +#[cfg(feature = "pem")] +use der::pem; + +/// Result type +pub type Result = core::result::Result; + +/// Error type +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum Error { + /// ASN.1 DER-related errors. + Asn1(der::Error), + + /// Errors relating to PKCS#5-encrypted keys. + #[cfg(feature = "pkcs5")] + EncryptedPrivateKey(pkcs5::Error), + + /// Malformed cryptographic key contained in a PKCS#8 document. + /// + /// This is intended for relaying errors related to the raw data contained + /// within [`PrivateKeyInfo::private_key`][`crate::PrivateKeyInfo::private_key`] + /// or [`SubjectPublicKeyInfo::subject_public_key`][`crate::SubjectPublicKeyInfo::subject_public_key`]. + KeyMalformed, + + /// [`AlgorithmIdentifier::parameters`][`crate::AlgorithmIdentifier::parameters`] + /// is malformed or otherwise encoded in an unexpected manner. + ParametersMalformed, + + /// Public key errors propagated from the [`spki::Error`] type. + PublicKey(spki::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Asn1(err) => write!(f, "PKCS#8 ASN.1 error: {}", err), + #[cfg(feature = "pkcs5")] + Error::EncryptedPrivateKey(err) => write!(f, "{}", err), + Error::KeyMalformed => f.write_str("PKCS#8 cryptographic key data malformed"), + Error::ParametersMalformed => f.write_str("PKCS#8 algorithm parameters malformed"), + Error::PublicKey(err) => write!(f, "public key error: {}", err), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +impl From for Error { + fn from(err: der::Error) -> Error { + Error::Asn1(err) + } +} + +impl From for Error { + fn from(err: der::ErrorKind) -> Error { + Error::Asn1(err.into()) + } +} + +#[cfg(feature = "pem")] +impl From for Error { + fn from(err: pem::Error) -> Error { + der::Error::from(err).into() + } +} + +#[cfg(feature = "pkcs5")] +impl From for Error { + fn from(err: pkcs5::Error) -> Error { + Error::EncryptedPrivateKey(err) + } +} + +impl From for Error { + fn from(err: spki::Error) -> Error { + Error::PublicKey(err) + } +} + +impl From for spki::Error { + fn from(err: Error) -> spki::Error { + match err { + Error::Asn1(e) => spki::Error::Asn1(e), + Error::PublicKey(e) => e, + _ => spki::Error::KeyMalformed, + } + } +} diff --git a/vendor/pkcs8/src/lib.rs b/vendor/pkcs8/src/lib.rs new file mode 100644 index 000000000..1d2dfa284 --- /dev/null +++ b/vendor/pkcs8/src/lib.rs @@ -0,0 +1,104 @@ +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", + html_root_url = "https://docs.rs/pkcs8/0.9.0-pre" +)] +#![forbid(unsafe_code, clippy::unwrap_used)] +#![warn(missing_docs, rust_2018_idioms, unused_qualifications)] + +//! ## About this crate +//! This library provides generalized PKCS#8 support designed to work with a +//! number of different algorithms. It supports `no_std` platforms including +//! ones without a heap (albeit with reduced functionality). +//! +//! It supports decoding/encoding the following types: +//! +//! - [`EncryptedPrivateKeyInfo`]: (with `pkcs5` feature) encrypted key. +//! - [`PrivateKeyInfo`]: algorithm identifier and data representing a private key. +//! Optionally also includes public key data for asymmetric keys. +//! - [`SubjectPublicKeyInfo`]: algorithm identifier and data representing a public key +//! (re-exported from the [`spki`] crate) +//! +//! When the `pem` feature is enabled, it also supports decoding/encoding +//! documents from "PEM encoding" format as defined in RFC 7468. +//! +//! ## Encrypted Private Key Support +//! [`EncryptedPrivateKeyInfo`] supports decoding/encoding encrypted PKCS#8 +//! private keys and is gated under the `pkcs5` feature. +//! +//! When the `encryption` feature of this crate is enabled, it provides +//! [`EncryptedPrivateKeyInfo::decrypt`] and [`PrivateKeyInfo::encrypt`] +//! functions which are able to decrypt/encrypt keys using the following +//! algorithms: +//! +//! - [PKCS#5v2 Password Based Encryption Scheme 2 (RFC 8018)] +//! - Key derivation functions: +//! - [scrypt] ([RFC 7914]) +//! - PBKDF2 ([RFC 8018](https://datatracker.ietf.org/doc/html/rfc8018#section-5.2)) +//! - SHA-2 based PRF with HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, or HMAC-SHA512 +//! - SHA-1 based PRF with HMAC-SHA1, when the `sha1` feature of this crate is enabled. +//! - Symmetric encryption: AES-128-CBC, AES-192-CBC, or AES-256-CBC +//! (best available options for PKCS#5v2) +//! +//! ## Legacy DES-CBC and DES-EDE3-CBC (3DES) support (optional) +//! When the `des-insecure` and/or `3des` features are enabled this crate provides support for +//! private keys encrypted with with DES-CBC and DES-EDE3-CBC (3DES or Triple DES) symmetric +//! encryption, respectively. +//! +//! ⚠️ WARNING ⚠️ +//! +//! DES support (gated behind the `des-insecure` feature) is implemented to +//! allow for decryption of legacy PKCS#8 files only. +//! +//! Such PKCS#8 documents should be considered *INSECURE* due to the short +//! 56-bit key size of DES. +//! +//! New keys should use AES instead. +//! +//! [RFC 5208]: https://tools.ietf.org/html/rfc5208 +//! [RFC 5958]: https://tools.ietf.org/html/rfc5958 +//! [RFC 7914]: https://datatracker.ietf.org/doc/html/rfc7914 +//! [PKCS#5v2 Password Based Encryption Scheme 2 (RFC 8018)]: https://tools.ietf.org/html/rfc8018#section-6.2 +//! [scrypt]: https://en.wikipedia.org/wiki/Scrypt + +#[cfg(feature = "pem")] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +mod error; +mod private_key_info; +mod traits; +mod version; + +#[cfg(feature = "pkcs5")] +pub(crate) mod encrypted_private_key_info; + +pub use crate::{ + error::{Error, Result}, + private_key_info::PrivateKeyInfo, + traits::DecodePrivateKey, + version::Version, +}; +pub use der::{self, asn1::ObjectIdentifier, oid::AssociatedOid}; +pub use spki::{self, AlgorithmIdentifier, DecodePublicKey, SubjectPublicKeyInfo}; + +#[cfg(feature = "alloc")] +pub use { + crate::traits::EncodePrivateKey, + der::{Document, SecretDocument}, + spki::EncodePublicKey, +}; + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +pub use der::pem::LineEnding; + +#[cfg(feature = "pkcs5")] +pub use {encrypted_private_key_info::EncryptedPrivateKeyInfo, pkcs5}; + +#[cfg(feature = "rand_core")] +pub use rand_core; diff --git a/vendor/pkcs8/src/private_key_info.rs b/vendor/pkcs8/src/private_key_info.rs new file mode 100644 index 000000000..52f0878d7 --- /dev/null +++ b/vendor/pkcs8/src/private_key_info.rs @@ -0,0 +1,293 @@ +//! PKCS#8 `PrivateKeyInfo`. + +use crate::{AlgorithmIdentifier, Error, Result, Version}; +use core::fmt; +use der::{ + asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef}, + Decode, DecodeValue, Encode, Header, Reader, Sequence, TagMode, TagNumber, +}; + +#[cfg(feature = "alloc")] +use der::SecretDocument; + +#[cfg(feature = "encryption")] +use { + crate::EncryptedPrivateKeyInfo, + der::zeroize::Zeroizing, + pkcs5::pbes2, + rand_core::{CryptoRng, RngCore}, +}; + +#[cfg(feature = "pem")] +use der::pem::PemLabel; + +#[cfg(feature = "subtle")] +use subtle::{Choice, ConstantTimeEq}; + +/// Context-specific tag number for the public key. +const PUBLIC_KEY_TAG: TagNumber = TagNumber::N1; + +/// PKCS#8 `PrivateKeyInfo`. +/// +/// ASN.1 structure containing an [`AlgorithmIdentifier`], private key +/// data in an algorithm specific format, and optional attributes +/// (ignored by this implementation). +/// +/// Supports PKCS#8 v1 as described in [RFC 5208] and PKCS#8 v2 as described +/// in [RFC 5958]. PKCS#8 v2 keys include an additional public key field. +/// +/// # PKCS#8 v1 `PrivateKeyInfo` +/// +/// Described in [RFC 5208 Section 5]: +/// +/// ```text +/// PrivateKeyInfo ::= SEQUENCE { +/// version Version, +/// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, +/// privateKey PrivateKey, +/// attributes [0] IMPLICIT Attributes OPTIONAL } +/// +/// Version ::= INTEGER +/// +/// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier +/// +/// PrivateKey ::= OCTET STRING +/// +/// Attributes ::= SET OF Attribute +/// ``` +/// +/// # PKCS#8 v2 `OneAsymmetricKey` +/// +/// PKCS#8 `OneAsymmetricKey` as described in [RFC 5958 Section 2]: +/// +/// ```text +/// PrivateKeyInfo ::= OneAsymmetricKey +/// +/// OneAsymmetricKey ::= SEQUENCE { +/// version Version, +/// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, +/// privateKey PrivateKey, +/// attributes [0] Attributes OPTIONAL, +/// ..., +/// [[2: publicKey [1] PublicKey OPTIONAL ]], +/// ... +/// } +/// +/// Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) +/// +/// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier +/// +/// PrivateKey ::= OCTET STRING +/// +/// Attributes ::= SET OF Attribute +/// +/// PublicKey ::= BIT STRING +/// ``` +/// +/// [RFC 5208]: https://tools.ietf.org/html/rfc5208 +/// [RFC 5958]: https://datatracker.ietf.org/doc/html/rfc5958 +/// [RFC 5208 Section 5]: https://tools.ietf.org/html/rfc5208#section-5 +/// [RFC 5958 Section 2]: https://datatracker.ietf.org/doc/html/rfc5958#section-2 +#[derive(Clone)] +pub struct PrivateKeyInfo<'a> { + /// X.509 [`AlgorithmIdentifier`] for the private key type. + pub algorithm: AlgorithmIdentifier<'a>, + + /// Private key data. + pub private_key: &'a [u8], + + /// Public key data, optionally available if version is V2. + pub public_key: Option<&'a [u8]>, +} + +impl<'a> PrivateKeyInfo<'a> { + /// Create a new PKCS#8 [`PrivateKeyInfo`] message. + /// + /// This is a helper method which initializes `attributes` and `public_key` + /// to `None`, helpful if you aren't using those. + pub fn new(algorithm: AlgorithmIdentifier<'a>, private_key: &'a [u8]) -> Self { + Self { + algorithm, + private_key, + public_key: None, + } + } + + /// Get the PKCS#8 [`Version`] for this structure. + /// + /// [`Version::V1`] if `public_key` is `None`, [`Version::V2`] if `Some`. + pub fn version(&self) -> Version { + if self.public_key.is_some() { + Version::V2 + } else { + Version::V1 + } + } + + /// Encrypt this private key using a symmetric encryption key derived + /// from the provided password. + /// + /// Uses the following algorithms for encryption: + /// - PBKDF: scrypt with default parameters: + /// - log₂(N): 15 + /// - r: 8 + /// - p: 1 + /// - Cipher: AES-256-CBC (best available option for PKCS#5 encryption) + #[cfg(feature = "encryption")] + #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] + pub fn encrypt( + &self, + rng: impl CryptoRng + RngCore, + password: impl AsRef<[u8]>, + ) -> Result { + let der = Zeroizing::new(self.to_vec()?); + EncryptedPrivateKeyInfo::encrypt(rng, password, der.as_ref()) + } + + /// Encrypt this private key using a symmetric encryption key derived + /// from the provided password and [`pbes2::Parameters`]. + #[cfg(feature = "encryption")] + #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] + pub fn encrypt_with_params( + &self, + pbes2_params: pbes2::Parameters<'_>, + password: impl AsRef<[u8]>, + ) -> Result { + let der = Zeroizing::new(self.to_vec()?); + EncryptedPrivateKeyInfo::encrypt_with(pbes2_params, password, der.as_ref()) + } +} + +impl<'a> DecodeValue<'a> for PrivateKeyInfo<'a> { + fn decode_value>( + reader: &mut R, + header: Header, + ) -> der::Result> { + reader.read_nested(header.length, |reader| { + // Parse and validate `version` INTEGER. + let version = Version::decode(reader)?; + let algorithm = reader.decode()?; + let private_key = OctetStringRef::decode(reader)?.into(); + let public_key = reader + .context_specific::>(PUBLIC_KEY_TAG, TagMode::Implicit)? + .map(|bs| { + bs.as_bytes() + .ok_or_else(|| der::Tag::BitString.value_error()) + }) + .transpose()?; + + if version.has_public_key() != public_key.is_some() { + return Err(reader.error( + der::Tag::ContextSpecific { + constructed: true, + number: PUBLIC_KEY_TAG, + } + .value_error() + .kind(), + )); + } + + // Ignore any remaining extension fields + while !reader.is_finished() { + reader.decode::>>()?; + } + + Ok(Self { + algorithm, + private_key, + public_key, + }) + }) + } +} + +impl<'a> Sequence<'a> for PrivateKeyInfo<'a> { + fn fields(&self, f: F) -> der::Result + where + F: FnOnce(&[&dyn Encode]) -> der::Result, + { + f(&[ + &u8::from(self.version()), + &self.algorithm, + &OctetStringRef::new(self.private_key)?, + &self + .public_key + .map(|pk| { + BitStringRef::from_bytes(pk).map(|value| ContextSpecific { + tag_number: PUBLIC_KEY_TAG, + tag_mode: TagMode::Implicit, + value, + }) + }) + .transpose()?, + ]) + } +} + +impl<'a> TryFrom<&'a [u8]> for PrivateKeyInfo<'a> { + type Error = Error; + + fn try_from(bytes: &'a [u8]) -> Result { + Ok(Self::from_der(bytes)?) + } +} + +impl<'a> fmt::Debug for PrivateKeyInfo<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PrivateKeyInfo") + .field("version", &self.version()) + .field("algorithm", &self.algorithm) + .field("public_key", &self.public_key) + .finish_non_exhaustive() + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +impl TryFrom> for SecretDocument { + type Error = Error; + + fn try_from(private_key: PrivateKeyInfo<'_>) -> Result { + SecretDocument::try_from(&private_key) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +impl TryFrom<&PrivateKeyInfo<'_>> for SecretDocument { + type Error = Error; + + fn try_from(private_key: &PrivateKeyInfo<'_>) -> Result { + Ok(Self::encode_msg(private_key)?) + } +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl PemLabel for PrivateKeyInfo<'_> { + const PEM_LABEL: &'static str = "PRIVATE KEY"; +} + +#[cfg(feature = "subtle")] +#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))] +impl<'a> ConstantTimeEq for PrivateKeyInfo<'a> { + fn ct_eq(&self, other: &Self) -> Choice { + // NOTE: public fields are not compared in constant time + let public_fields_eq = + self.algorithm == other.algorithm && self.public_key == other.public_key; + + self.private_key.ct_eq(other.private_key) & Choice::from(public_fields_eq as u8) + } +} + +#[cfg(feature = "subtle")] +#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))] +impl<'a> Eq for PrivateKeyInfo<'a> {} + +#[cfg(feature = "subtle")] +#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))] +impl<'a> PartialEq for PrivateKeyInfo<'a> { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} diff --git a/vendor/pkcs8/src/traits.rs b/vendor/pkcs8/src/traits.rs new file mode 100644 index 000000000..dd86b90ef --- /dev/null +++ b/vendor/pkcs8/src/traits.rs @@ -0,0 +1,145 @@ +//! Traits for parsing objects from PKCS#8 encoded documents + +use crate::{Error, PrivateKeyInfo, Result}; + +#[cfg(feature = "alloc")] +use der::SecretDocument; + +#[cfg(feature = "encryption")] +use { + crate::EncryptedPrivateKeyInfo, + rand_core::{CryptoRng, RngCore}, +}; + +#[cfg(feature = "pem")] +use {crate::LineEnding, alloc::string::String, der::zeroize::Zeroizing}; + +#[cfg(feature = "pem")] +use der::pem::PemLabel; + +#[cfg(feature = "std")] +use std::path::Path; + +/// Parse a private key object from a PKCS#8 encoded document. +pub trait DecodePrivateKey: for<'a> TryFrom, Error = Error> + Sized { + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data + /// (binary format). + fn from_pkcs8_der(bytes: &[u8]) -> Result { + Self::try_from(PrivateKeyInfo::try_from(bytes)?) + } + + /// Deserialize encrypted PKCS#8 private key from ASN.1 DER-encoded data + /// (binary format) and attempt to decrypt it using the provided password. + #[cfg(feature = "encryption")] + #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] + fn from_pkcs8_encrypted_der(bytes: &[u8], password: impl AsRef<[u8]>) -> Result { + let doc = EncryptedPrivateKeyInfo::try_from(bytes)?.decrypt(password)?; + Self::from_pkcs8_der(doc.as_bytes()) + } + + /// Deserialize PKCS#8-encoded private key from PEM. + /// + /// Keys in this format begin with the following delimiter: + /// + /// ```text + /// -----BEGIN PRIVATE KEY----- + /// ``` + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + fn from_pkcs8_pem(s: &str) -> Result { + let (label, doc) = SecretDocument::from_pem(s)?; + PrivateKeyInfo::validate_pem_label(label)?; + Self::from_pkcs8_der(doc.as_bytes()) + } + + /// Deserialize encrypted PKCS#8-encoded private key from PEM and attempt + /// to decrypt it using the provided password. + /// + /// Keys in this format begin with the following delimiter: + /// + /// ```text + /// -----BEGIN ENCRYPTED PRIVATE KEY----- + /// ``` + #[cfg(all(feature = "encryption", feature = "pem"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "encryption", feature = "pem"))))] + fn from_pkcs8_encrypted_pem(s: &str, password: impl AsRef<[u8]>) -> Result { + let (label, doc) = SecretDocument::from_pem(s)?; + EncryptedPrivateKeyInfo::validate_pem_label(label)?; + Self::from_pkcs8_encrypted_der(doc.as_bytes(), password) + } + + /// Load PKCS#8 private key from an ASN.1 DER-encoded file on the local + /// filesystem (binary format). + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn read_pkcs8_der_file(path: impl AsRef) -> Result { + Self::from_pkcs8_der(SecretDocument::read_der_file(path)?.as_bytes()) + } + + /// Load PKCS#8 private key from a PEM-encoded file on the local filesystem. + #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn read_pkcs8_pem_file(path: impl AsRef) -> Result { + let (label, doc) = SecretDocument::read_pem_file(path)?; + PrivateKeyInfo::validate_pem_label(&label)?; + Self::from_pkcs8_der(doc.as_bytes()) + } +} + +/// Serialize a private key object to a PKCS#8 encoded document. +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub trait EncodePrivateKey { + /// Serialize a [`SecretDocument`] containing a PKCS#8-encoded private key. + fn to_pkcs8_der(&self) -> Result; + + /// Create an [`SecretDocument`] containing the ciphertext of + /// a PKCS#8 encoded private key encrypted under the given `password`. + #[cfg(feature = "encryption")] + #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] + fn to_pkcs8_encrypted_der( + &self, + rng: impl CryptoRng + RngCore, + password: impl AsRef<[u8]>, + ) -> Result { + EncryptedPrivateKeyInfo::encrypt(rng, password, self.to_pkcs8_der()?.as_bytes()) + } + + /// Serialize this private key as PEM-encoded PKCS#8 with the given [`LineEnding`]. + #[cfg(feature = "pem")] + #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] + fn to_pkcs8_pem(&self, line_ending: LineEnding) -> Result> { + let doc = self.to_pkcs8_der()?; + Ok(doc.to_pem(PrivateKeyInfo::PEM_LABEL, line_ending)?) + } + + /// Serialize this private key as an encrypted PEM-encoded PKCS#8 private + /// key using the `provided` to derive an encryption key. + #[cfg(all(feature = "encryption", feature = "pem"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "encryption", feature = "pem"))))] + fn to_pkcs8_encrypted_pem( + &self, + rng: impl CryptoRng + RngCore, + password: impl AsRef<[u8]>, + line_ending: LineEnding, + ) -> Result> { + let doc = self.to_pkcs8_encrypted_der(rng, password)?; + Ok(doc.to_pem(EncryptedPrivateKeyInfo::PEM_LABEL, line_ending)?) + } + + /// Write ASN.1 DER-encoded PKCS#8 private key to the given path + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn write_pkcs8_der_file(&self, path: impl AsRef) -> Result<()> { + Ok(self.to_pkcs8_der()?.write_der_file(path)?) + } + + /// Write ASN.1 DER-encoded PKCS#8 private key to the given path + #[cfg(all(feature = "pem", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "std"))))] + fn write_pkcs8_pem_file(&self, path: impl AsRef, line_ending: LineEnding) -> Result<()> { + let doc = self.to_pkcs8_der()?; + Ok(doc.write_pem_file(path, PrivateKeyInfo::PEM_LABEL, line_ending)?) + } +} diff --git a/vendor/pkcs8/src/version.rs b/vendor/pkcs8/src/version.rs new file mode 100644 index 000000000..339368392 --- /dev/null +++ b/vendor/pkcs8/src/version.rs @@ -0,0 +1,63 @@ +//! PKCS#8 version identifier. + +use crate::Error; +use der::{Decode, Encode, FixedTag, Reader, Tag, Writer}; + +/// Version identifier for PKCS#8 documents. +/// +/// (RFC 5958 designates `0` and `1` as the only valid versions for PKCS#8 documents) +#[derive(Clone, Debug, Copy, PartialEq)] +pub enum Version { + /// Denotes PKCS#8 v1: no public key field. + V1 = 0, + + /// Denotes PKCS#8 v2: `OneAsymmetricKey` with public key field. + V2 = 1, +} + +impl Version { + /// Is this version expected to have a public key? + pub fn has_public_key(self) -> bool { + match self { + Version::V1 => false, + Version::V2 => true, + } + } +} + +impl<'a> Decode<'a> for Version { + fn decode>(decoder: &mut R) -> der::Result { + Version::try_from(u8::decode(decoder)?).map_err(|_| Self::TAG.value_error()) + } +} + +impl Encode for Version { + fn encoded_len(&self) -> der::Result { + der::Length::from(1u8).for_tlv() + } + + fn encode(&self, writer: &mut dyn Writer) -> der::Result<()> { + u8::from(*self).encode(writer) + } +} + +impl From for u8 { + fn from(version: Version) -> Self { + version as u8 + } +} + +impl TryFrom for Version { + type Error = Error; + fn try_from(byte: u8) -> Result { + match byte { + 0 => Ok(Version::V1), + 1 => Ok(Version::V2), + _ => Err(Self::TAG.value_error().into()), + } + } +} + +impl FixedTag for Version { + const TAG: Tag = Tag::Integer; +} -- cgit v1.2.3