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

pkcs5/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
7)]
8#![forbid(unsafe_code)]
9
10//! # Usage
11//!
12//! The main API for this crate is the [`EncryptionScheme`] enum, which impls
13//! the [`Decode`] and [`Encode`] traits from the [`der`] crate, and can be
14//! used for decoding/encoding PKCS#5 `AlgorithmIdentifier` fields.
15//!
16//! The [`pbes2::Parameters`] struct can be used to generate new encryption
17//! parameters when encrypting new keys.
18//!
19//! [RFC 8018]: https://tools.ietf.org/html/rfc8018
20
21#[cfg(all(feature = "alloc", feature = "pbes2"))]
22extern crate alloc;
23
24mod error;
25
26pub mod pbes1;
27pub mod pbes2;
28
29pub use crate::error::{Error, Result};
30pub use der::{self, asn1::ObjectIdentifier};
31pub use spki::AlgorithmIdentifierRef;
32
33use der::{
34    Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag, Writer,
35};
36
37#[cfg(feature = "pbes2")]
38pub use scrypt;
39
40#[cfg(all(feature = "alloc", feature = "pbes2"))]
41use alloc::vec::Vec;
42
43/// Configuration for supported PKCS#5 password-based encryption schemes.
44///
45/// <div class="warning">
46/// <strong>Security Warning</strong>
47///
48/// This type should not be used to encrypt multiple plaintexts under the same IV/salt values.
49///
50/// Instead, new values should be randomly generated for every usage.
51/// </div>
52#[derive(Clone, Debug, Eq, PartialEq)]
53#[non_exhaustive]
54#[allow(clippy::large_enum_variant)]
55pub enum EncryptionScheme {
56    /// Password-Based Encryption Scheme 1 as defined in [RFC 8018 Section 6.1].
57    ///
58    /// [RFC 8018 Section 6.1]: https://tools.ietf.org/html/rfc8018#section-6.1
59    Pbes1(pbes1::Algorithm),
60
61    /// Password-Based Encryption Scheme 2 as defined in [RFC 8018 Section 6.2].
62    ///
63    /// [RFC 8018 Section 6.2]: https://tools.ietf.org/html/rfc8018#section-6.2
64    Pbes2(pbes2::Parameters),
65}
66
67impl EncryptionScheme {
68    /// Generate PBES2 parameters using recommended algorithm settings and parameters (salt/IV)
69    /// generated using the system's secure random number generator.
70    ///
71    /// # Panics
72    /// In the event the system's secure random generator experiences an internal failure.
73    #[cfg(all(feature = "pbes2", feature = "getrandom"))]
74    #[must_use]
75    #[track_caller]
76    pub fn generate() -> Self {
77        Self::Pbes2(pbes2::Parameters::generate())
78    }
79
80    /// Attempt to decrypt the given ciphertext, allocating and returning a byte vector containing
81    /// the plaintext.
82    ///
83    /// # Errors
84    /// Returns an error if the algorithm specified in this scheme's parameters is unsupported
85    /// (e.g. PBES1 is completely unsupported), or if the ciphertext is malformed (e.g. ciphertext
86    /// length is not a multiple of a block mode's padding).
87    #[cfg(all(feature = "alloc", feature = "pbes2"))]
88    pub fn decrypt(&self, password: impl AsRef<[u8]>, ciphertext: &[u8]) -> Result<Vec<u8>> {
89        match self {
90            Self::Pbes2(params) => params.decrypt(password, ciphertext),
91            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
92        }
93    }
94
95    /// Attempt to decrypt the given ciphertext in-place using a key derived from the provided
96    /// password and this scheme's parameters.
97    ///
98    /// # Errors
99    /// Returns an error if the algorithm specified in this scheme's parameters is unsupported
100    /// (e.g. PBES1 is completely unsupported), or if the ciphertext is malformed (e.g. not a
101    /// multiple of a block mode's padding).
102    #[cfg(feature = "pbes2")]
103    pub fn decrypt_in_place<'a>(
104        &self,
105        password: impl AsRef<[u8]>,
106        buffer: &'a mut [u8],
107    ) -> Result<&'a [u8]> {
108        match self {
109            Self::Pbes2(params) => params.decrypt_in_place(password, buffer),
110            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
111        }
112    }
113
114    /// Encrypt the given plaintext, allocating and returning a vector containing the ciphertext.
115    ///
116    /// # Errors
117    /// - For PBES1, simply returns [`Error::NoPbes1CryptSupport`] unconditionally.
118    /// - Returns [`Error::UnsupportedAlgorithm`] if support for the requested algorithm has not
119    ///   been enabled in this crate's features.
120    #[cfg(all(feature = "alloc", feature = "pbes2"))]
121    pub fn encrypt(&self, password: impl AsRef<[u8]>, plaintext: &[u8]) -> Result<Vec<u8>> {
122        match self {
123            Self::Pbes2(params) => params.encrypt(password, plaintext),
124            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
125        }
126    }
127
128    /// Encrypt the given ciphertext in-place using a key derived from the provided password and
129    /// this scheme's parameters.
130    ///
131    /// # Errors
132    /// - For PBES1, simply returns [`Error::NoPbes1CryptSupport`] unconditionally.
133    /// - Returns [`Error::UnsupportedAlgorithm`] if support for the requested algorithm has not
134    ///   been enabled in this crate's features.
135    #[cfg(feature = "pbes2")]
136    pub fn encrypt_in_place<'a>(
137        &self,
138        password: impl AsRef<[u8]>,
139        buffer: &'a mut [u8],
140        pos: usize,
141    ) -> Result<&'a [u8]> {
142        match self {
143            Self::Pbes2(params) => params.encrypt_in_place(password, buffer, pos),
144            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
145        }
146    }
147
148    /// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
149    #[must_use]
150    pub fn oid(&self) -> ObjectIdentifier {
151        match self {
152            Self::Pbes1(params) => params.oid(),
153            Self::Pbes2(_) => pbes2::PBES2_OID,
154        }
155    }
156
157    /// Get [`pbes1::Parameters`] if it is the selected algorithm.
158    #[must_use]
159    pub fn pbes1(&self) -> Option<&pbes1::Algorithm> {
160        match self {
161            Self::Pbes1(alg) => Some(alg),
162            _ => None,
163        }
164    }
165
166    /// Get [`pbes2::Parameters`] if it is the selected algorithm.
167    #[must_use]
168    pub fn pbes2(&self) -> Option<&pbes2::Parameters> {
169        match self {
170            Self::Pbes2(params) => Some(params),
171            _ => None,
172        }
173    }
174}
175
176impl<'a> DecodeValue<'a> for EncryptionScheme {
177    type Error = der::Error;
178
179    fn decode_value<R: Reader<'a>>(decoder: &mut R, header: Header) -> der::Result<Self> {
180        AlgorithmIdentifierRef::decode_value(decoder, header)?.try_into()
181    }
182}
183
184impl EncodeValue for EncryptionScheme {
185    fn value_len(&self) -> der::Result<Length> {
186        match self {
187            Self::Pbes1(pbes1) => pbes1.oid().encoded_len()? + pbes1.parameters.encoded_len()?,
188            Self::Pbes2(pbes2) => pbes2::PBES2_OID.encoded_len()? + pbes2.encoded_len()?,
189        }
190    }
191
192    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
193        match self {
194            Self::Pbes1(pbes1) => {
195                pbes1.oid().encode(writer)?;
196                pbes1.parameters.encode(writer)?;
197            }
198            Self::Pbes2(pbes2) => {
199                pbes2::PBES2_OID.encode(writer)?;
200                pbes2.encode(writer)?;
201            }
202        }
203
204        Ok(())
205    }
206}
207
208impl Sequence<'_> for EncryptionScheme {}
209
210impl From<pbes1::Algorithm> for EncryptionScheme {
211    fn from(alg: pbes1::Algorithm) -> EncryptionScheme {
212        Self::Pbes1(alg)
213    }
214}
215
216impl From<pbes2::Parameters> for EncryptionScheme {
217    fn from(params: pbes2::Parameters) -> EncryptionScheme {
218        Self::Pbes2(params)
219    }
220}
221
222impl TryFrom<AlgorithmIdentifierRef<'_>> for EncryptionScheme {
223    type Error = der::Error;
224
225    fn try_from(alg: AlgorithmIdentifierRef<'_>) -> der::Result<EncryptionScheme> {
226        if alg.oid == pbes2::PBES2_OID {
227            match alg.parameters {
228                Some(params) => pbes2::Parameters::try_from(params).map(Into::into),
229                None => Err(Tag::OctetString.value_error().into()),
230            }
231        } else {
232            pbes1::Algorithm::try_from(alg).map(Into::into)
233        }
234    }
235}
236
237impl TryFrom<&[u8]> for EncryptionScheme {
238    type Error = der::Error;
239
240    fn try_from(bytes: &[u8]) -> der::Result<EncryptionScheme> {
241        AlgorithmIdentifierRef::from_der(bytes)?.try_into()
242    }
243}