Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

pkcs8/
encrypted_private_key_info.rs

1//! PKCS#8 `EncryptedPrivateKeyInfo`
2
3use crate::{Error, Result};
4use core::fmt;
5use der::{
6    Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Sequence, Writer,
7    asn1::OctetStringRef,
8};
9use pkcs5::EncryptionScheme;
10
11#[cfg(feature = "alloc")]
12use der::{SecretDocument, asn1::OctetString};
13
14#[cfg(feature = "encryption")]
15use {pkcs5::pbes2, rand_core::TryCryptoRng};
16
17#[cfg(feature = "pem")]
18use der::pem::PemLabel;
19
20/// PKCS#8 `EncryptedPrivateKeyInfo`.
21///
22/// ASN.1 structure containing a PKCS#5 [`EncryptionScheme`] identifier for a
23/// password-based symmetric encryption scheme and encrypted private key data.
24///
25/// ## Schema
26/// Structure described in [RFC 5208 Section 6]:
27///
28/// ```text
29/// EncryptedPrivateKeyInfo ::= SEQUENCE {
30///   encryptionAlgorithm  EncryptionAlgorithmIdentifier,
31///   encryptedData        EncryptedData }
32///
33/// EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
34///
35/// EncryptedData ::= OCTET STRING
36/// ```
37///
38/// [RFC 5208 Section 6]: https://tools.ietf.org/html/rfc5208#section-6
39#[derive(Clone, Eq, PartialEq)]
40pub struct EncryptedPrivateKeyInfo<Data> {
41    /// Algorithm identifier describing a password-based symmetric encryption
42    /// scheme used to encrypt the `encrypted_data` field.
43    pub encryption_algorithm: EncryptionScheme,
44
45    /// Private key data
46    pub encrypted_data: Data,
47}
48
49impl<'a, Data> EncryptedPrivateKeyInfo<Data>
50where
51    Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
52    Data: AsRef<[u8]>,
53{
54    /// Attempt to decrypt this encrypted private key using the provided
55    /// password to derive an encryption key.
56    ///
57    /// # Errors
58    /// - Returns errors in the event the file could not be decrypted successfully.
59    /// - Returns errors if the file decrypted but the resulting plaintext failed to decode.
60    #[cfg(feature = "encryption")]
61    pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument> {
62        Ok(self
63            .encryption_algorithm
64            .decrypt(password, self.encrypted_data.as_ref())?
65            .try_into()?)
66    }
67
68    /// Encrypt the given ASN.1 DER document using a symmetric encryption key
69    /// derived from the provided password.
70    #[cfg(feature = "getrandom")]
71    pub(crate) fn encrypt(password: impl AsRef<[u8]>, doc: &[u8]) -> Result<SecretDocument> {
72        Self::encrypt_with_rng(&mut getrandom::SysRng, password, doc)
73    }
74
75    /// Encrypt the given ASN.1 DER document using a symmetric encryption key
76    /// derived from the provided password.
77    #[cfg(feature = "encryption")]
78    pub(crate) fn encrypt_with_rng<R: TryCryptoRng>(
79        rng: &mut R,
80        password: impl AsRef<[u8]>,
81        doc: &[u8],
82    ) -> Result<SecretDocument> {
83        let pbes2_params = pbes2::Parameters::generate_recommended(rng)?;
84        EncryptedPrivateKeyInfoOwned::encrypt_with_params(pbes2_params, password, doc)
85    }
86
87    /// Encrypt this private key using a symmetric encryption key derived
88    /// from the provided password and [`pbes2::Parameters`].
89    #[cfg(feature = "encryption")]
90    pub(crate) fn encrypt_with_params(
91        pbes2_params: pbes2::Parameters,
92        password: impl AsRef<[u8]>,
93        doc: &[u8],
94    ) -> Result<SecretDocument> {
95        let encrypted_data = pbes2_params.encrypt(password, doc)?;
96        let encrypted_data = OctetStringRef::new(&encrypted_data)?;
97
98        EncryptedPrivateKeyInfo {
99            encryption_algorithm: pbes2_params.into(),
100            encrypted_data,
101        }
102        .try_into()
103    }
104}
105
106impl<'a, Data> DecodeValue<'a> for EncryptedPrivateKeyInfo<Data>
107where
108    Data: DecodeValue<'a, Error = der::Error> + FixedTag + 'a,
109{
110    type Error = der::Error;
111
112    fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> der::Result<Self> {
113        Ok(Self {
114            encryption_algorithm: reader.decode()?,
115            encrypted_data: reader.decode()?,
116        })
117    }
118}
119
120impl<Data> EncodeValue for EncryptedPrivateKeyInfo<Data>
121where
122    Data: EncodeValue + FixedTag,
123{
124    fn value_len(&self) -> der::Result<Length> {
125        self.encryption_algorithm.encoded_len()? + self.encrypted_data.encoded_len()?
126    }
127
128    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
129        self.encryption_algorithm.encode(writer)?;
130        self.encrypted_data.encode(writer)?;
131        Ok(())
132    }
133}
134
135impl<'a, Data> Sequence<'a> for EncryptedPrivateKeyInfo<Data> where
136    Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a
137{
138}
139
140impl<'a, Data> TryFrom<&'a [u8]> for EncryptedPrivateKeyInfo<Data>
141where
142    Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
143{
144    type Error = Error;
145
146    fn try_from(bytes: &'a [u8]) -> Result<Self> {
147        Ok(Self::from_der(bytes)?)
148    }
149}
150
151impl<Data> fmt::Debug for EncryptedPrivateKeyInfo<Data> {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        f.debug_struct("EncryptedPrivateKeyInfo")
154            .field("encryption_algorithm", &self.encryption_algorithm)
155            .finish_non_exhaustive()
156    }
157}
158
159#[cfg(feature = "alloc")]
160impl<'a, Data> TryFrom<EncryptedPrivateKeyInfo<Data>> for SecretDocument
161where
162    Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
163{
164    type Error = Error;
165
166    fn try_from(encrypted_private_key: EncryptedPrivateKeyInfo<Data>) -> Result<SecretDocument> {
167        SecretDocument::try_from(&encrypted_private_key)
168    }
169}
170
171#[cfg(feature = "alloc")]
172impl<'a, Data> TryFrom<&EncryptedPrivateKeyInfo<Data>> for SecretDocument
173where
174    Data: DecodeValue<'a, Error = der::Error> + EncodeValue + FixedTag + 'a,
175{
176    type Error = Error;
177
178    fn try_from(encrypted_private_key: &EncryptedPrivateKeyInfo<Data>) -> Result<SecretDocument> {
179        Ok(Self::encode_msg(encrypted_private_key)?)
180    }
181}
182
183#[cfg(feature = "pem")]
184impl<Data> PemLabel for EncryptedPrivateKeyInfo<Data> {
185    const PEM_LABEL: &'static str = "ENCRYPTED PRIVATE KEY";
186}
187
188/// [`EncryptedPrivateKeyInfo`] with [`OctetStringRef`] encrypted data.
189pub type EncryptedPrivateKeyInfoRef<'a> = EncryptedPrivateKeyInfo<&'a OctetStringRef>;
190
191#[cfg(feature = "alloc")]
192/// [`EncryptedPrivateKeyInfo`] with [`OctetString`] encrypted data.
193pub type EncryptedPrivateKeyInfoOwned = EncryptedPrivateKeyInfo<OctetString>;