• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! PKCS#8 `EncryptedPrivateKeyInfo`
2 
3 use crate::{Error, Result};
4 use core::fmt;
5 use der::{asn1::OctetStringRef, Decode, DecodeValue, Encode, Header, Reader, Sequence};
6 use pkcs5::EncryptionScheme;
7 
8 #[cfg(feature = "alloc")]
9 use der::SecretDocument;
10 
11 #[cfg(feature = "encryption")]
12 use {
13     pkcs5::pbes2,
14     rand_core::{CryptoRng, RngCore},
15 };
16 
17 #[cfg(feature = "pem")]
18 use 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 #[cfg_attr(docsrs, doc(cfg(feature = "pkcs5")))]
40 #[derive(Clone, Eq, PartialEq)]
41 pub struct EncryptedPrivateKeyInfo<'a> {
42     /// Algorithm identifier describing a password-based symmetric encryption
43     /// scheme used to encrypt the `encrypted_data` field.
44     pub encryption_algorithm: EncryptionScheme<'a>,
45 
46     /// Private key data
47     pub encrypted_data: &'a [u8],
48 }
49 
50 impl<'a> EncryptedPrivateKeyInfo<'a> {
51     /// Attempt to decrypt this encrypted private key using the provided
52     /// password to derive an encryption key.
53     #[cfg(feature = "encryption")]
54     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
decrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument>55     pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument> {
56         Ok(self
57             .encryption_algorithm
58             .decrypt(password, self.encrypted_data)?
59             .try_into()?)
60     }
61 
62     /// Encrypt the given ASN.1 DER document using a symmetric encryption key
63     /// derived from the provided password.
64     #[cfg(feature = "encryption")]
65     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
encrypt( mut rng: impl CryptoRng + RngCore, password: impl AsRef<[u8]>, doc: &[u8], ) -> Result<SecretDocument>66     pub(crate) fn encrypt(
67         mut rng: impl CryptoRng + RngCore,
68         password: impl AsRef<[u8]>,
69         doc: &[u8],
70     ) -> Result<SecretDocument> {
71         let mut salt = [0u8; 16];
72         rng.fill_bytes(&mut salt);
73 
74         let mut iv = [0u8; 16];
75         rng.fill_bytes(&mut iv);
76 
77         let pbes2_params = pbes2::Parameters::scrypt_aes256cbc(Default::default(), &salt, &iv)?;
78         EncryptedPrivateKeyInfo::encrypt_with(pbes2_params, password, doc)
79     }
80 
81     /// Encrypt this private key using a symmetric encryption key derived
82     /// from the provided password and [`pbes2::Parameters`].
83     #[cfg(feature = "encryption")]
84     #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
encrypt_with( pbes2_params: pbes2::Parameters<'a>, password: impl AsRef<[u8]>, doc: &[u8], ) -> Result<SecretDocument>85     pub(crate) fn encrypt_with(
86         pbes2_params: pbes2::Parameters<'a>,
87         password: impl AsRef<[u8]>,
88         doc: &[u8],
89     ) -> Result<SecretDocument> {
90         let encrypted_data = pbes2_params.encrypt(password, doc)?;
91 
92         EncryptedPrivateKeyInfo {
93             encryption_algorithm: pbes2_params.into(),
94             encrypted_data: &encrypted_data,
95         }
96         .try_into()
97     }
98 }
99 
100 impl<'a> DecodeValue<'a> for EncryptedPrivateKeyInfo<'a> {
decode_value<R: Reader<'a>>( reader: &mut R, header: Header, ) -> der::Result<EncryptedPrivateKeyInfo<'a>>101     fn decode_value<R: Reader<'a>>(
102         reader: &mut R,
103         header: Header,
104     ) -> der::Result<EncryptedPrivateKeyInfo<'a>> {
105         reader.read_nested(header.length, |reader| {
106             Ok(Self {
107                 encryption_algorithm: reader.decode()?,
108                 encrypted_data: OctetStringRef::decode(reader)?.as_bytes(),
109             })
110         })
111     }
112 }
113 
114 impl<'a> Sequence<'a> for EncryptedPrivateKeyInfo<'a> {
fields<F, T>(&self, f: F) -> der::Result<T> where F: FnOnce(&[&dyn Encode]) -> der::Result<T>,115     fn fields<F, T>(&self, f: F) -> der::Result<T>
116     where
117         F: FnOnce(&[&dyn Encode]) -> der::Result<T>,
118     {
119         f(&[
120             &self.encryption_algorithm,
121             &OctetStringRef::new(self.encrypted_data)?,
122         ])
123     }
124 }
125 
126 impl<'a> TryFrom<&'a [u8]> for EncryptedPrivateKeyInfo<'a> {
127     type Error = Error;
128 
try_from(bytes: &'a [u8]) -> Result<Self>129     fn try_from(bytes: &'a [u8]) -> Result<Self> {
130         Ok(Self::from_der(bytes)?)
131     }
132 }
133 
134 impl<'a> fmt::Debug for EncryptedPrivateKeyInfo<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result135     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136         f.debug_struct("EncryptedPrivateKeyInfo")
137             .field("encryption_algorithm", &self.encryption_algorithm)
138             .finish_non_exhaustive()
139     }
140 }
141 
142 #[cfg(feature = "alloc")]
143 #[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "pkcs5"))))]
144 impl TryFrom<EncryptedPrivateKeyInfo<'_>> for SecretDocument {
145     type Error = Error;
146 
try_from(encrypted_private_key: EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument>147     fn try_from(encrypted_private_key: EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument> {
148         SecretDocument::try_from(&encrypted_private_key)
149     }
150 }
151 
152 #[cfg(feature = "alloc")]
153 #[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "pkcs5"))))]
154 impl TryFrom<&EncryptedPrivateKeyInfo<'_>> for SecretDocument {
155     type Error = Error;
156 
try_from(encrypted_private_key: &EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument>157     fn try_from(encrypted_private_key: &EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument> {
158         Ok(Self::encode_msg(encrypted_private_key)?)
159     }
160 }
161 
162 #[cfg(feature = "pem")]
163 #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
164 impl PemLabel for EncryptedPrivateKeyInfo<'_> {
165     const PEM_LABEL: &'static str = "ENCRYPTED PRIVATE KEY";
166 }
167