1 //! PKCS#8 `EncryptedPrivateKeyInfo`
2 
3 use crate::{Error, Result};
4 use core::fmt;
5 use der::{
6     asn1::OctetStringRef, Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader,
7     Sequence, Writer,
8 };
9 use pkcs5::EncryptionScheme;
10 
11 #[cfg(feature = "alloc")]
12 use der::SecretDocument;
13 
14 #[cfg(feature = "encryption")]
15 use {
16     pkcs5::pbes2,
17     rand_core::{CryptoRng, RngCore},
18 };
19 
20 #[cfg(feature = "pem")]
21 use der::pem::PemLabel;
22 
23 /// PKCS#8 `EncryptedPrivateKeyInfo`.
24 ///
25 /// ASN.1 structure containing a PKCS#5 [`EncryptionScheme`] identifier for a
26 /// password-based symmetric encryption scheme and encrypted private key data.
27 ///
28 /// ## Schema
29 /// Structure described in [RFC 5208 Section 6]:
30 ///
31 /// ```text
32 /// EncryptedPrivateKeyInfo ::= SEQUENCE {
33 ///   encryptionAlgorithm  EncryptionAlgorithmIdentifier,
34 ///   encryptedData        EncryptedData }
35 ///
36 /// EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
37 ///
38 /// EncryptedData ::= OCTET STRING
39 /// ```
40 ///
41 /// [RFC 5208 Section 6]: https://tools.ietf.org/html/rfc5208#section-6
42 #[derive(Clone, Eq, PartialEq)]
43 pub struct EncryptedPrivateKeyInfo<'a> {
44     /// Algorithm identifier describing a password-based symmetric encryption
45     /// scheme used to encrypt the `encrypted_data` field.
46     pub encryption_algorithm: EncryptionScheme<'a>,
47 
48     /// Private key data
49     pub encrypted_data: &'a [u8],
50 }
51 
52 impl<'a> EncryptedPrivateKeyInfo<'a> {
53     /// Attempt to decrypt this encrypted private key using the provided
54     /// password to derive an encryption key.
55     #[cfg(feature = "encryption")]
decrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument>56     pub fn decrypt(&self, password: impl AsRef<[u8]>) -> Result<SecretDocument> {
57         Ok(self
58             .encryption_algorithm
59             .decrypt(password, self.encrypted_data)?
60             .try_into()?)
61     }
62 
63     /// Encrypt the given ASN.1 DER document using a symmetric encryption key
64     /// derived from the provided password.
65     #[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")]
encrypt_with( pbes2_params: pbes2::Parameters<'a>, password: impl AsRef<[u8]>, doc: &[u8], ) -> Result<SecretDocument>84     pub(crate) fn encrypt_with(
85         pbes2_params: pbes2::Parameters<'a>,
86         password: impl AsRef<[u8]>,
87         doc: &[u8],
88     ) -> Result<SecretDocument> {
89         let encrypted_data = pbes2_params.encrypt(password, doc)?;
90 
91         EncryptedPrivateKeyInfo {
92             encryption_algorithm: pbes2_params.into(),
93             encrypted_data: &encrypted_data,
94         }
95         .try_into()
96     }
97 }
98 
99 impl<'a> DecodeValue<'a> for EncryptedPrivateKeyInfo<'a> {
decode_value<R: Reader<'a>>( reader: &mut R, header: Header, ) -> der::Result<EncryptedPrivateKeyInfo<'a>>100     fn decode_value<R: Reader<'a>>(
101         reader: &mut R,
102         header: Header,
103     ) -> der::Result<EncryptedPrivateKeyInfo<'a>> {
104         reader.read_nested(header.length, |reader| {
105             Ok(Self {
106                 encryption_algorithm: reader.decode()?,
107                 encrypted_data: OctetStringRef::decode(reader)?.as_bytes(),
108             })
109         })
110     }
111 }
112 
113 impl EncodeValue for EncryptedPrivateKeyInfo<'_> {
value_len(&self) -> der::Result<Length>114     fn value_len(&self) -> der::Result<Length> {
115         self.encryption_algorithm.encoded_len()?
116             + OctetStringRef::new(self.encrypted_data)?.encoded_len()?
117     }
118 
encode_value(&self, writer: &mut impl Writer) -> der::Result<()>119     fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
120         self.encryption_algorithm.encode(writer)?;
121         OctetStringRef::new(self.encrypted_data)?.encode(writer)?;
122         Ok(())
123     }
124 }
125 
126 impl<'a> Sequence<'a> for EncryptedPrivateKeyInfo<'a> {}
127 
128 impl<'a> TryFrom<&'a [u8]> for EncryptedPrivateKeyInfo<'a> {
129     type Error = Error;
130 
try_from(bytes: &'a [u8]) -> Result<Self>131     fn try_from(bytes: &'a [u8]) -> Result<Self> {
132         Ok(Self::from_der(bytes)?)
133     }
134 }
135 
136 impl<'a> fmt::Debug for EncryptedPrivateKeyInfo<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result137     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138         f.debug_struct("EncryptedPrivateKeyInfo")
139             .field("encryption_algorithm", &self.encryption_algorithm)
140             .finish_non_exhaustive()
141     }
142 }
143 
144 #[cfg(feature = "alloc")]
145 impl TryFrom<EncryptedPrivateKeyInfo<'_>> for SecretDocument {
146     type Error = Error;
147 
try_from(encrypted_private_key: EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument>148     fn try_from(encrypted_private_key: EncryptedPrivateKeyInfo<'_>) -> Result<SecretDocument> {
149         SecretDocument::try_from(&encrypted_private_key)
150     }
151 }
152 
153 #[cfg(feature = "alloc")]
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 impl PemLabel for EncryptedPrivateKeyInfo<'_> {
164     const PEM_LABEL: &'static str = "ENCRYPTED PRIVATE KEY";
165 }
166