summaryrefslogtreecommitdiffstats
path: root/vendor/pkcs8/src/private_key_info.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/pkcs8/src/private_key_info.rs')
-rw-r--r--vendor/pkcs8/src/private_key_info.rs295
1 files changed, 295 insertions, 0 deletions
diff --git a/vendor/pkcs8/src/private_key_info.rs b/vendor/pkcs8/src/private_key_info.rs
new file mode 100644
index 0000000..ecae624
--- /dev/null
+++ b/vendor/pkcs8/src/private_key_info.rs
@@ -0,0 +1,295 @@
+//! PKCS#8 `PrivateKeyInfo`.
+
+use crate::{AlgorithmIdentifierRef, Error, Result, Version};
+use core::fmt;
+use der::{
+ asn1::{AnyRef, BitStringRef, ContextSpecific, OctetStringRef},
+ Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, TagMode, TagNumber,
+ Writer,
+};
+
+#[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: AlgorithmIdentifierRef<'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: AlgorithmIdentifierRef<'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")]
+ pub fn encrypt(
+ &self,
+ rng: impl CryptoRng + RngCore,
+ password: impl AsRef<[u8]>,
+ ) -> Result<SecretDocument> {
+ let der = Zeroizing::new(self.to_der()?);
+ 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")]
+ pub fn encrypt_with_params(
+ &self,
+ pbes2_params: pbes2::Parameters<'_>,
+ password: impl AsRef<[u8]>,
+ ) -> Result<SecretDocument> {
+ let der = Zeroizing::new(self.to_der()?);
+ EncryptedPrivateKeyInfo::encrypt_with(pbes2_params, password, der.as_ref())
+ }
+
+ /// Get a `BIT STRING` representation of the public key, if present.
+ fn public_key_bit_string(&self) -> der::Result<Option<ContextSpecific<BitStringRef<'a>>>> {
+ 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> DecodeValue<'a> for PrivateKeyInfo<'a> {
+ fn decode_value<R: Reader<'a>>(
+ reader: &mut R,
+ header: Header,
+ ) -> der::Result<PrivateKeyInfo<'a>> {
+ 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::<BitStringRef<'_>>(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::<ContextSpecific<AnyRef<'_>>>()?;
+ }
+
+ Ok(Self {
+ algorithm,
+ private_key,
+ public_key,
+ })
+ })
+ }
+}
+
+impl EncodeValue for PrivateKeyInfo<'_> {
+ fn value_len(&self) -> der::Result<Length> {
+ self.version().encoded_len()?
+ + self.algorithm.encoded_len()?
+ + OctetStringRef::new(self.private_key)?.encoded_len()?
+ + self.public_key_bit_string()?.encoded_len()?
+ }
+
+ fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
+ self.version().encode(writer)?;
+ self.algorithm.encode(writer)?;
+ OctetStringRef::new(self.private_key)?.encode(writer)?;
+ self.public_key_bit_string()?.encode(writer)?;
+ Ok(())
+ }
+}
+
+impl<'a> Sequence<'a> for PrivateKeyInfo<'a> {}
+
+impl<'a> TryFrom<&'a [u8]> for PrivateKeyInfo<'a> {
+ type Error = Error;
+
+ fn try_from(bytes: &'a [u8]) -> Result<Self> {
+ 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")]
+impl TryFrom<PrivateKeyInfo<'_>> for SecretDocument {
+ type Error = Error;
+
+ fn try_from(private_key: PrivateKeyInfo<'_>) -> Result<SecretDocument> {
+ SecretDocument::try_from(&private_key)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl TryFrom<&PrivateKeyInfo<'_>> for SecretDocument {
+ type Error = Error;
+
+ fn try_from(private_key: &PrivateKeyInfo<'_>) -> Result<SecretDocument> {
+ Ok(Self::encode_msg(private_key)?)
+ }
+}
+
+#[cfg(feature = "pem")]
+impl PemLabel for PrivateKeyInfo<'_> {
+ const PEM_LABEL: &'static str = "PRIVATE KEY";
+}
+
+#[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")]
+impl<'a> Eq for PrivateKeyInfo<'a> {}
+
+#[cfg(feature = "subtle")]
+impl<'a> PartialEq for PrivateKeyInfo<'a> {
+ fn eq(&self, other: &Self) -> bool {
+ self.ct_eq(other).into()
+ }
+}