pkcs5/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_auto_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#![warn(
10    clippy::mod_module_files,
11    clippy::unwrap_used,
12    missing_docs,
13    rust_2018_idioms,
14    unused_lifetimes,
15    unused_qualifications
16)]
17
18//! # Usage
19//!
20//! The main API for this crate is the [`EncryptionScheme`] enum, which impls
21//! the [`Decode`] and [`Encode`] traits from the [`der`] crate, and can be
22//! used for decoding/encoding PKCS#5 `AlgorithmIdentifier` fields.
23//!
24//! [RFC 8018]: https://tools.ietf.org/html/rfc8018
25
26#[cfg(all(feature = "alloc", feature = "pbes2"))]
27extern crate alloc;
28
29mod error;
30
31pub mod pbes1;
32pub mod pbes2;
33
34pub use crate::error::{Error, Result};
35pub use der::{self, asn1::ObjectIdentifier};
36pub use spki::AlgorithmIdentifierRef;
37
38use der::{
39    Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag, Writer,
40};
41
42#[cfg(feature = "pbes2")]
43pub use scrypt;
44
45#[cfg(all(feature = "alloc", feature = "pbes2"))]
46use alloc::vec::Vec;
47
48/// Supported PKCS#5 password-based encryption schemes.
49#[derive(Clone, Debug, Eq, PartialEq)]
50#[non_exhaustive]
51#[allow(clippy::large_enum_variant)]
52pub enum EncryptionScheme<'a> {
53    /// Password-Based Encryption Scheme 1 as defined in [RFC 8018 Section 6.1].
54    ///
55    /// [RFC 8018 Section 6.1]: https://tools.ietf.org/html/rfc8018#section-6.1
56    Pbes1(pbes1::Algorithm),
57
58    /// Password-Based Encryption Scheme 2 as defined in [RFC 8018 Section 6.2].
59    ///
60    /// [RFC 8018 Section 6.2]: https://tools.ietf.org/html/rfc8018#section-6.2
61    Pbes2(pbes2::Parameters<'a>),
62}
63
64impl<'a> EncryptionScheme<'a> {
65    /// Attempt to decrypt the given ciphertext, allocating and returning a
66    /// byte vector containing the plaintext.
67    #[cfg(all(feature = "alloc", feature = "pbes2"))]
68    pub fn decrypt(&self, password: impl AsRef<[u8]>, ciphertext: &[u8]) -> Result<Vec<u8>> {
69        match self {
70            Self::Pbes2(params) => params.decrypt(password, ciphertext),
71            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
72        }
73    }
74
75    /// Attempt to decrypt the given ciphertext in-place using a key derived
76    /// from the provided password and this scheme's parameters.
77    ///
78    /// Returns an error if the algorithm specified in this scheme's parameters
79    /// is unsupported, or if the ciphertext is malformed (e.g. not a multiple
80    /// of a block mode's padding)
81    #[cfg(feature = "pbes2")]
82    pub fn decrypt_in_place<'b>(
83        &self,
84        password: impl AsRef<[u8]>,
85        buffer: &'b mut [u8],
86    ) -> Result<&'b [u8]> {
87        match self {
88            Self::Pbes2(params) => params.decrypt_in_place(password, buffer),
89            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
90        }
91    }
92
93    /// Encrypt the given plaintext, allocating and returning a vector
94    /// containing the ciphertext.
95    #[cfg(all(feature = "alloc", feature = "pbes2"))]
96    pub fn encrypt(&self, password: impl AsRef<[u8]>, plaintext: &[u8]) -> Result<Vec<u8>> {
97        match self {
98            Self::Pbes2(params) => params.encrypt(password, plaintext),
99            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
100        }
101    }
102
103    /// Encrypt the given ciphertext in-place using a key derived from the
104    /// provided password and this scheme's parameters.
105    #[cfg(feature = "pbes2")]
106    pub fn encrypt_in_place<'b>(
107        &self,
108        password: impl AsRef<[u8]>,
109        buffer: &'b mut [u8],
110        pos: usize,
111    ) -> Result<&'b [u8]> {
112        match self {
113            Self::Pbes2(params) => params.encrypt_in_place(password, buffer, pos),
114            Self::Pbes1(_) => Err(Error::NoPbes1CryptSupport),
115        }
116    }
117
118    /// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
119    pub fn oid(&self) -> ObjectIdentifier {
120        match self {
121            Self::Pbes1(params) => params.oid(),
122            Self::Pbes2(_) => pbes2::PBES2_OID,
123        }
124    }
125
126    /// Get [`pbes1::Parameters`] if it is the selected algorithm.
127    pub fn pbes1(&self) -> Option<&pbes1::Algorithm> {
128        match self {
129            Self::Pbes1(alg) => Some(alg),
130            _ => None,
131        }
132    }
133
134    /// Get [`pbes2::Parameters`] if it is the selected algorithm.
135    pub fn pbes2(&self) -> Option<&pbes2::Parameters<'a>> {
136        match self {
137            Self::Pbes2(params) => Some(params),
138            _ => None,
139        }
140    }
141}
142
143impl<'a> DecodeValue<'a> for EncryptionScheme<'a> {
144    fn decode_value<R: Reader<'a>>(decoder: &mut R, header: Header) -> der::Result<Self> {
145        AlgorithmIdentifierRef::decode_value(decoder, header)?.try_into()
146    }
147}
148
149impl EncodeValue for EncryptionScheme<'_> {
150    fn value_len(&self) -> der::Result<Length> {
151        match self {
152            Self::Pbes1(pbes1) => pbes1.oid().encoded_len()? + pbes1.parameters.encoded_len()?,
153            Self::Pbes2(pbes2) => pbes2::PBES2_OID.encoded_len()? + pbes2.encoded_len()?,
154        }
155    }
156
157    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
158        match self {
159            Self::Pbes1(pbes1) => {
160                pbes1.oid().encode(writer)?;
161                pbes1.parameters.encode(writer)?;
162            }
163            Self::Pbes2(pbes2) => {
164                pbes2::PBES2_OID.encode(writer)?;
165                pbes2.encode(writer)?;
166            }
167        }
168
169        Ok(())
170    }
171}
172
173impl<'a> Sequence<'a> for EncryptionScheme<'a> {}
174
175impl<'a> From<pbes1::Algorithm> for EncryptionScheme<'a> {
176    fn from(alg: pbes1::Algorithm) -> EncryptionScheme<'a> {
177        Self::Pbes1(alg)
178    }
179}
180
181impl<'a> From<pbes2::Parameters<'a>> for EncryptionScheme<'a> {
182    fn from(params: pbes2::Parameters<'a>) -> EncryptionScheme<'a> {
183        Self::Pbes2(params)
184    }
185}
186
187impl<'a> TryFrom<AlgorithmIdentifierRef<'a>> for EncryptionScheme<'a> {
188    type Error = der::Error;
189
190    fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result<EncryptionScheme<'_>> {
191        if alg.oid == pbes2::PBES2_OID {
192            match alg.parameters {
193                Some(params) => pbes2::Parameters::try_from(params).map(Into::into),
194                None => Err(Tag::OctetString.value_error()),
195            }
196        } else {
197            pbes1::Algorithm::try_from(alg).map(Into::into)
198        }
199    }
200}
201
202impl<'a> TryFrom<&'a [u8]> for EncryptionScheme<'a> {
203    type Error = der::Error;
204
205    fn try_from(bytes: &'a [u8]) -> der::Result<EncryptionScheme<'a>> {
206        AlgorithmIdentifierRef::from_der(bytes)?.try_into()
207    }
208}