pkcs5/
pbes2.rs

1//! Password-Based Encryption Scheme 2 as defined in [RFC 8018 Section 6.2].
2//!
3//! [RFC 8018 Section 6.2]: https://tools.ietf.org/html/rfc8018#section-6.2
4
5mod kdf;
6
7#[cfg(feature = "pbes2")]
8mod encryption;
9
10pub use self::kdf::{
11    Kdf, Pbkdf2Params, Pbkdf2Prf, ScryptParams, HMAC_WITH_SHA1_OID, HMAC_WITH_SHA256_OID,
12    PBKDF2_OID, SCRYPT_OID,
13};
14
15use crate::{AlgorithmIdentifierRef, Error, Result};
16use der::{
17    asn1::{AnyRef, ObjectIdentifier, OctetStringRef},
18    Decode, DecodeValue, Encode, EncodeValue, ErrorKind, Length, Reader, Sequence, Tag, Writer,
19};
20
21#[cfg(all(feature = "alloc", feature = "pbes2"))]
22use alloc::vec::Vec;
23
24/// 128-bit Advanced Encryption Standard (AES) algorithm with Cipher-Block
25/// Chaining (CBC) mode of operation.
26pub const AES_128_CBC_OID: ObjectIdentifier =
27    ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.2");
28
29/// 192-bit Advanced Encryption Standard (AES) algorithm with Cipher-Block
30/// Chaining (CBC) mode of operation.
31pub const AES_192_CBC_OID: ObjectIdentifier =
32    ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.22");
33
34/// 256-bit Advanced Encryption Standard (AES) algorithm with Cipher-Block
35/// Chaining (CBC) mode of operation.
36pub const AES_256_CBC_OID: ObjectIdentifier =
37    ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.42");
38
39/// DES operating in CBC mode
40#[cfg(feature = "des-insecure")]
41pub const DES_CBC_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.7");
42
43/// Triple DES operating in CBC mode
44#[cfg(feature = "3des")]
45pub const DES_EDE3_CBC_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.3.7");
46
47/// Password-Based Encryption Scheme 2 (PBES2) OID.
48///
49/// <https://tools.ietf.org/html/rfc8018#section-6.2>
50pub const PBES2_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.13");
51
52/// AES cipher block size
53const AES_BLOCK_SIZE: usize = 16;
54
55/// DES / Triple DES block size
56#[cfg(any(feature = "3des", feature = "des-insecure"))]
57const DES_BLOCK_SIZE: usize = 8;
58
59/// Password-Based Encryption Scheme 2 parameters as defined in [RFC 8018 Appendix A.4].
60///
61/// ```text
62///  PBES2-params ::= SEQUENCE {
63///       keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
64///       encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
65/// ```
66///
67/// [RFC 8018 Appendix A.4]: https://tools.ietf.org/html/rfc8018#appendix-A.4
68#[derive(Clone, Debug, Eq, PartialEq)]
69pub struct Parameters<'a> {
70    /// Key derivation function
71    pub kdf: Kdf<'a>,
72
73    /// Encryption scheme
74    pub encryption: EncryptionScheme<'a>,
75}
76
77impl<'a> Parameters<'a> {
78    /// Initialize PBES2 parameters using PBKDF2-SHA256 as the password-based
79    /// key derivation function and AES-128-CBC as the symmetric cipher.
80    pub fn pbkdf2_sha256_aes128cbc(
81        pbkdf2_iterations: u32,
82        pbkdf2_salt: &'a [u8],
83        aes_iv: &'a [u8; AES_BLOCK_SIZE],
84    ) -> Result<Self> {
85        let kdf = Pbkdf2Params::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
86        let encryption = EncryptionScheme::Aes128Cbc { iv: aes_iv };
87        Ok(Self { kdf, encryption })
88    }
89
90    /// Initialize PBES2 parameters using PBKDF2-SHA256 as the password-based
91    /// key derivation function and AES-256-CBC as the symmetric cipher.
92    pub fn pbkdf2_sha256_aes256cbc(
93        pbkdf2_iterations: u32,
94        pbkdf2_salt: &'a [u8],
95        aes_iv: &'a [u8; AES_BLOCK_SIZE],
96    ) -> Result<Self> {
97        let kdf = Pbkdf2Params::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
98        let encryption = EncryptionScheme::Aes256Cbc { iv: aes_iv };
99        Ok(Self { kdf, encryption })
100    }
101
102    /// Initialize PBES2 parameters using scrypt as the password-based
103    /// key derivation function and AES-128-CBC as the symmetric cipher.
104    ///
105    /// For more information on scrypt parameters, see documentation for the
106    /// [`scrypt::Params`] struct.
107    // TODO(tarcieri): encapsulate `scrypt::Params`?
108    #[cfg(feature = "pbes2")]
109    pub fn scrypt_aes128cbc(
110        params: scrypt::Params,
111        salt: &'a [u8],
112        aes_iv: &'a [u8; AES_BLOCK_SIZE],
113    ) -> Result<Self> {
114        let kdf = ScryptParams::from_params_and_salt(params, salt)?.into();
115        let encryption = EncryptionScheme::Aes128Cbc { iv: aes_iv };
116        Ok(Self { kdf, encryption })
117    }
118
119    /// Initialize PBES2 parameters using scrypt as the password-based
120    /// key derivation function and AES-256-CBC as the symmetric cipher.
121    ///
122    /// For more information on scrypt parameters, see documentation for the
123    /// [`scrypt::Params`] struct.
124    ///
125    /// When in doubt, use `Default::default()` as the [`scrypt::Params`].
126    /// This also avoids the need to import the type from the `scrypt` crate.
127    // TODO(tarcieri): encapsulate `scrypt::Params`?
128    #[cfg(feature = "pbes2")]
129    pub fn scrypt_aes256cbc(
130        params: scrypt::Params,
131        salt: &'a [u8],
132        aes_iv: &'a [u8; AES_BLOCK_SIZE],
133    ) -> Result<Self> {
134        let kdf = ScryptParams::from_params_and_salt(params, salt)?.into();
135        let encryption = EncryptionScheme::Aes256Cbc { iv: aes_iv };
136        Ok(Self { kdf, encryption })
137    }
138
139    /// Attempt to decrypt the given ciphertext, allocating and returning a
140    /// byte vector containing the plaintext.
141    #[cfg(all(feature = "alloc", feature = "pbes2"))]
142    pub fn decrypt(&self, password: impl AsRef<[u8]>, ciphertext: &[u8]) -> Result<Vec<u8>> {
143        let mut buffer = ciphertext.to_vec();
144        let pt_len = self.decrypt_in_place(password, &mut buffer)?.len();
145        buffer.truncate(pt_len);
146        Ok(buffer)
147    }
148
149    /// Attempt to decrypt the given ciphertext in-place using a key derived
150    /// from the provided password and this scheme's parameters.
151    ///
152    /// Returns an error if the algorithm specified in this scheme's parameters
153    /// is unsupported, or if the ciphertext is malformed (e.g. not a multiple
154    /// of a block mode's padding)
155    #[cfg(feature = "pbes2")]
156    pub fn decrypt_in_place<'b>(
157        &self,
158        password: impl AsRef<[u8]>,
159        buffer: &'b mut [u8],
160    ) -> Result<&'b [u8]> {
161        encryption::decrypt_in_place(self, password, buffer)
162    }
163
164    /// Encrypt the given plaintext, allocating and returning a vector
165    /// containing the ciphertext.
166    #[cfg(all(feature = "alloc", feature = "pbes2"))]
167    pub fn encrypt(&self, password: impl AsRef<[u8]>, plaintext: &[u8]) -> Result<Vec<u8>> {
168        // TODO(tarcieri): support non-AES ciphers?
169        let mut buffer = Vec::with_capacity(plaintext.len() + AES_BLOCK_SIZE);
170        buffer.extend_from_slice(plaintext);
171        buffer.extend_from_slice(&[0u8; AES_BLOCK_SIZE]);
172
173        let ct_len = self
174            .encrypt_in_place(password, &mut buffer, plaintext.len())?
175            .len();
176
177        buffer.truncate(ct_len);
178        Ok(buffer)
179    }
180
181    /// Encrypt the given plaintext in-place using a key derived from the
182    /// provided password and this scheme's parameters, writing the ciphertext
183    /// into the same buffer.
184    #[cfg(feature = "pbes2")]
185    pub fn encrypt_in_place<'b>(
186        &self,
187        password: impl AsRef<[u8]>,
188        buffer: &'b mut [u8],
189        pos: usize,
190    ) -> Result<&'b [u8]> {
191        encryption::encrypt_in_place(self, password, buffer, pos)
192    }
193}
194
195impl<'a> DecodeValue<'a> for Parameters<'a> {
196    fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
197        AnyRef::decode_value(reader, header)?.try_into()
198    }
199}
200
201impl EncodeValue for Parameters<'_> {
202    fn value_len(&self) -> der::Result<Length> {
203        self.kdf.encoded_len()? + self.encryption.encoded_len()?
204    }
205
206    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
207        self.kdf.encode(writer)?;
208        self.encryption.encode(writer)?;
209        Ok(())
210    }
211}
212
213impl<'a> Sequence<'a> for Parameters<'a> {}
214
215impl<'a> TryFrom<AnyRef<'a>> for Parameters<'a> {
216    type Error = der::Error;
217
218    fn try_from(any: AnyRef<'a>) -> der::Result<Self> {
219        any.sequence(|params| {
220            let kdf = AlgorithmIdentifierRef::decode(params)?;
221            let encryption = AlgorithmIdentifierRef::decode(params)?;
222
223            Ok(Self {
224                kdf: kdf.try_into()?,
225                encryption: encryption.try_into()?,
226            })
227        })
228    }
229}
230
231/// Symmetric encryption scheme used by PBES2.
232#[derive(Copy, Clone, Debug, Eq, PartialEq)]
233#[non_exhaustive]
234pub enum EncryptionScheme<'a> {
235    /// AES-128 in CBC mode
236    Aes128Cbc {
237        /// Initialization vector
238        iv: &'a [u8; AES_BLOCK_SIZE],
239    },
240
241    /// AES-192 in CBC mode
242    Aes192Cbc {
243        /// Initialization vector
244        iv: &'a [u8; AES_BLOCK_SIZE],
245    },
246
247    /// AES-256 in CBC mode
248    Aes256Cbc {
249        /// Initialization vector
250        iv: &'a [u8; AES_BLOCK_SIZE],
251    },
252
253    /// 3-Key Triple DES in CBC mode
254    #[cfg(feature = "3des")]
255    DesEde3Cbc {
256        /// Initialisation vector
257        iv: &'a [u8; DES_BLOCK_SIZE],
258    },
259
260    /// DES in CBC mode
261    #[cfg(feature = "des-insecure")]
262    DesCbc {
263        /// Initialisation vector
264        iv: &'a [u8; DES_BLOCK_SIZE],
265    },
266}
267
268impl<'a> EncryptionScheme<'a> {
269    /// Get the size of a key used by this algorithm in bytes.
270    pub fn key_size(&self) -> usize {
271        match self {
272            Self::Aes128Cbc { .. } => 16,
273            Self::Aes192Cbc { .. } => 24,
274            Self::Aes256Cbc { .. } => 32,
275            #[cfg(feature = "des-insecure")]
276            Self::DesCbc { .. } => 8,
277            #[cfg(feature = "3des")]
278            Self::DesEde3Cbc { .. } => 24,
279        }
280    }
281
282    /// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
283    pub fn oid(&self) -> ObjectIdentifier {
284        match self {
285            Self::Aes128Cbc { .. } => AES_128_CBC_OID,
286            Self::Aes192Cbc { .. } => AES_192_CBC_OID,
287            Self::Aes256Cbc { .. } => AES_256_CBC_OID,
288            #[cfg(feature = "des-insecure")]
289            Self::DesCbc { .. } => DES_CBC_OID,
290            #[cfg(feature = "3des")]
291            Self::DesEde3Cbc { .. } => DES_EDE3_CBC_OID,
292        }
293    }
294
295    /// Convenience function to turn the OID (see [`oid`](Self::oid))
296    /// of this [`EncryptionScheme`] into error case
297    /// [`Error::AlgorithmParametersInvalid`]
298    pub fn to_alg_params_invalid(&self) -> Error {
299        Error::AlgorithmParametersInvalid { oid: self.oid() }
300    }
301}
302
303impl<'a> Decode<'a> for EncryptionScheme<'a> {
304    fn decode<R: Reader<'a>>(reader: &mut R) -> der::Result<Self> {
305        AlgorithmIdentifierRef::decode(reader).and_then(TryInto::try_into)
306    }
307}
308
309impl<'a> TryFrom<AlgorithmIdentifierRef<'a>> for EncryptionScheme<'a> {
310    type Error = der::Error;
311
312    fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result<Self> {
313        // TODO(tarcieri): support for non-AES algorithms?
314        let iv = match alg.parameters {
315            Some(params) => params.decode_as::<OctetStringRef<'a>>()?.as_bytes(),
316            None => return Err(Tag::OctetString.value_error()),
317        };
318
319        match alg.oid {
320            AES_128_CBC_OID => Ok(Self::Aes128Cbc {
321                iv: iv
322                    .try_into()
323                    .map_err(|_| der::Tag::OctetString.value_error())?,
324            }),
325            AES_192_CBC_OID => Ok(Self::Aes192Cbc {
326                iv: iv
327                    .try_into()
328                    .map_err(|_| der::Tag::OctetString.value_error())?,
329            }),
330            AES_256_CBC_OID => Ok(Self::Aes256Cbc {
331                iv: iv
332                    .try_into()
333                    .map_err(|_| der::Tag::OctetString.value_error())?,
334            }),
335            #[cfg(feature = "des-insecure")]
336            DES_CBC_OID => Ok(Self::DesCbc {
337                iv: iv[0..DES_BLOCK_SIZE]
338                    .try_into()
339                    .map_err(|_| der::Tag::OctetString.value_error())?,
340            }),
341            #[cfg(feature = "3des")]
342            DES_EDE3_CBC_OID => Ok(Self::DesEde3Cbc {
343                iv: iv[0..DES_BLOCK_SIZE]
344                    .try_into()
345                    .map_err(|_| der::Tag::OctetString.value_error())?,
346            }),
347            oid => Err(ErrorKind::OidUnknown { oid }.into()),
348        }
349    }
350}
351
352impl<'a> TryFrom<EncryptionScheme<'a>> for AlgorithmIdentifierRef<'a> {
353    type Error = der::Error;
354
355    fn try_from(scheme: EncryptionScheme<'a>) -> der::Result<Self> {
356        let parameters = OctetStringRef::new(match scheme {
357            EncryptionScheme::Aes128Cbc { iv } => iv,
358            EncryptionScheme::Aes192Cbc { iv } => iv,
359            EncryptionScheme::Aes256Cbc { iv } => iv,
360            #[cfg(feature = "des-insecure")]
361            EncryptionScheme::DesCbc { iv } => iv,
362            #[cfg(feature = "3des")]
363            EncryptionScheme::DesEde3Cbc { iv } => iv,
364        })?;
365
366        Ok(AlgorithmIdentifierRef {
367            oid: scheme.oid(),
368            parameters: Some(parameters.into()),
369        })
370    }
371}
372
373impl<'a> Encode for EncryptionScheme<'a> {
374    fn encoded_len(&self) -> der::Result<Length> {
375        AlgorithmIdentifierRef::try_from(*self)?.encoded_len()
376    }
377
378    fn encode(&self, writer: &mut impl Writer) -> der::Result<()> {
379        AlgorithmIdentifierRef::try_from(*self)?.encode(writer)
380    }
381}