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

pkcs5/
pbes1.rs

1//! Password-Based Encryption Scheme 1 as defined in [RFC 8018 Section 6.1].
2//!
3//! [RFC 8018 Section 6.1]: https://tools.ietf.org/html/rfc8018#section-6.1
4
5use crate::AlgorithmIdentifierRef;
6use der::{
7    Decode, DecodeValue, Encode, EncodeValue, ErrorKind, Length, Reader, Sequence, Tag, Writer,
8    asn1::{AnyRef, ObjectIdentifier, OctetStringRef},
9};
10
11/// `pbeWithMD2AndDES-CBC` Object Identifier (OID).
12pub const PBE_WITH_MD2_AND_DES_CBC_OID: ObjectIdentifier =
13    ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.1");
14
15/// `pbeWithMD2AndRC2-CBC` Object Identifier (OID).
16pub const PBE_WITH_MD2_AND_RC2_CBC_OID: ObjectIdentifier =
17    ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.4");
18
19/// `pbeWithMD5AndDES-CBC` Object Identifier (OID).
20pub const PBE_WITH_MD5_AND_DES_CBC_OID: ObjectIdentifier =
21    ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.3");
22
23/// `pbeWithMD5AndRC2-CBC` Object Identifier (OID).
24pub const PBE_WITH_MD5_AND_RC2_CBC_OID: ObjectIdentifier =
25    ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.6");
26
27/// `pbeWithSHA1AndDES-CBC` Object Identifier (OID).
28pub const PBE_WITH_SHA1_AND_DES_CBC_OID: ObjectIdentifier =
29    ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.10");
30
31/// `pbeWithSHA1AndRC2-CBC` Object Identifier (OID).
32pub const PBE_WITH_SHA1_AND_RC2_CBC_OID: ObjectIdentifier =
33    ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.11");
34
35/// Length of a PBES1 salt (as defined in the `PBEParameter` ASN.1 message).
36pub const SALT_LENGTH: usize = 8;
37
38/// Password-Based Encryption Scheme 1 algorithms as defined in [RFC 8018 Appendix A.C].
39///
40/// ```text
41/// PBES1Algorithms ALGORITHM-IDENTIFIER ::= {
42///    {PBEParameter IDENTIFIED BY pbeWithMD2AndDES-CBC}  |
43///    {PBEParameter IDENTIFIED BY pbeWithMD2AndRC2-CBC}  |
44///    {PBEParameter IDENTIFIED BY pbeWithMD5AndDES-CBC}  |
45///    {PBEParameter IDENTIFIED BY pbeWithMD5AndRC2-CBC}  |
46///    {PBEParameter IDENTIFIED BY pbeWithSHA1AndDES-CBC} |
47///    {PBEParameter IDENTIFIED BY pbeWithSHA1AndRC2-CBC},
48///    ...
49/// }
50/// ```
51///
52/// [RFC 8018 Appendix A.C]: https://datatracker.ietf.org/doc/html/rfc8018#appendix-C
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub struct Algorithm {
55    /// Encryption scheme.
56    pub encryption: EncryptionScheme,
57
58    /// Scheme parameters.
59    pub parameters: Parameters,
60}
61
62impl Algorithm {
63    /// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
64    #[must_use]
65    pub fn oid(&self) -> ObjectIdentifier {
66        self.encryption.oid()
67    }
68}
69
70impl<'a> DecodeValue<'a> for Algorithm {
71    type Error = der::Error;
72
73    fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
74        AlgorithmIdentifierRef::decode_value(reader, header)?.try_into()
75    }
76}
77
78impl EncodeValue for Algorithm {
79    fn value_len(&self) -> der::Result<Length> {
80        self.encryption.encoded_len()? + self.parameters.encoded_len()?
81    }
82
83    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
84        self.encryption.encode(writer)?;
85        self.parameters.encode(writer)?;
86        Ok(())
87    }
88}
89
90impl Sequence<'_> for Algorithm {}
91
92impl<'a> TryFrom<AlgorithmIdentifierRef<'a>> for Algorithm {
93    type Error = der::Error;
94
95    fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result<Self> {
96        // Ensure that we have a supported PBES1 algorithm identifier
97        let encryption =
98            EncryptionScheme::try_from(alg.oid).map_err(|_| Tag::ObjectIdentifier.value_error())?;
99
100        let parameters = alg
101            .parameters
102            .ok_or_else(|| Tag::OctetString.value_error())?
103            .try_into()?;
104
105        Ok(Self {
106            encryption,
107            parameters,
108        })
109    }
110}
111
112/// Password-Based Encryption Scheme 1 parameters as defined in [RFC 8018 Appendix A.3].
113///
114/// ```text
115/// PBEParameter ::= SEQUENCE {
116///    salt OCTET STRING (SIZE(8)),
117///    iterationCount INTEGER }
118/// ```
119///
120/// [RFC 8018 Appendix A.3]: https://tools.ietf.org/html/rfc8018#appendix-A.3
121#[derive(Clone, Copy, Debug, Eq, PartialEq)]
122pub struct Parameters {
123    /// Salt value
124    pub salt: [u8; SALT_LENGTH],
125
126    /// Iteration count
127    pub iteration_count: u16,
128}
129
130impl<'a> DecodeValue<'a> for Parameters {
131    type Error = der::Error;
132
133    fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
134        AnyRef::decode_value(reader, header)?.try_into()
135    }
136}
137
138impl EncodeValue for Parameters {
139    fn value_len(&self) -> der::Result<Length> {
140        OctetStringRef::new(&self.salt)?.encoded_len()? + self.iteration_count.encoded_len()?
141    }
142
143    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
144        OctetStringRef::new(&self.salt)?.encode(writer)?;
145        self.iteration_count.encode(writer)?;
146        Ok(())
147    }
148}
149
150impl Sequence<'_> for Parameters {}
151
152impl TryFrom<AnyRef<'_>> for Parameters {
153    type Error = der::Error;
154
155    fn try_from(any: AnyRef<'_>) -> der::Result<Parameters> {
156        any.sequence(|reader| {
157            Ok(Parameters {
158                salt: <&OctetStringRef>::decode(reader)?
159                    .as_bytes()
160                    .try_into()
161                    .map_err(|_| Tag::OctetString.value_error())?,
162                iteration_count: reader.decode()?,
163            })
164        })
165    }
166}
167
168/// Password-Based Encryption Scheme 1 ciphersuites as defined in [RFC 8018 Appendix A.3].
169///
170/// [RFC 8018 Appendix A.3]: https://tools.ietf.org/html/rfc8018#appendix-A.3
171#[derive(Copy, Clone, Debug, Eq, PartialEq)]
172pub enum EncryptionScheme {
173    /// `pbeWithMD2AndDES-CBC`
174    PbeWithMd2AndDesCbc,
175
176    /// `pbeWithMD2AndRC2-CBC`
177    PbeWithMd2AndRc2Cbc,
178
179    /// `pbeWithMD5AndDES-CBC`
180    PbeWithMd5AndDesCbc,
181
182    /// `pbeWithMD5AndRC2-CBC`
183    PbeWithMd5AndRc2Cbc,
184
185    /// `pbeWithSHA1AndDES-CBC`
186    PbeWithSha1AndDesCbc,
187
188    /// `pbeWithSHA1AndRC2-CBC`
189    PbeWithSha1AndRc2Cbc,
190}
191
192impl TryFrom<ObjectIdentifier> for EncryptionScheme {
193    type Error = der::Error;
194
195    fn try_from(oid: ObjectIdentifier) -> der::Result<Self> {
196        match oid {
197            PBE_WITH_MD2_AND_DES_CBC_OID => Ok(Self::PbeWithMd2AndDesCbc),
198            PBE_WITH_MD2_AND_RC2_CBC_OID => Ok(Self::PbeWithMd2AndRc2Cbc),
199            PBE_WITH_MD5_AND_DES_CBC_OID => Ok(Self::PbeWithMd5AndDesCbc),
200            PBE_WITH_MD5_AND_RC2_CBC_OID => Ok(Self::PbeWithMd5AndRc2Cbc),
201            PBE_WITH_SHA1_AND_DES_CBC_OID => Ok(Self::PbeWithSha1AndDesCbc),
202            PBE_WITH_SHA1_AND_RC2_CBC_OID => Ok(Self::PbeWithSha1AndRc2Cbc),
203            _ => Err(ErrorKind::OidUnknown { oid }.into()),
204        }
205    }
206}
207
208impl EncryptionScheme {
209    /// Get the [`SymmetricCipher`] to be used.
210    #[must_use]
211    pub fn cipher(self) -> SymmetricCipher {
212        match self {
213            Self::PbeWithMd2AndDesCbc => SymmetricCipher::DesCbc,
214            Self::PbeWithMd2AndRc2Cbc => SymmetricCipher::Rc2Cbc,
215            Self::PbeWithMd5AndDesCbc => SymmetricCipher::DesCbc,
216            Self::PbeWithMd5AndRc2Cbc => SymmetricCipher::Rc2Cbc,
217            Self::PbeWithSha1AndDesCbc => SymmetricCipher::DesCbc,
218            Self::PbeWithSha1AndRc2Cbc => SymmetricCipher::Rc2Cbc,
219        }
220    }
221
222    /// Get the [`DigestAlgorithm`] to be used.
223    #[must_use]
224    pub fn digest(self) -> DigestAlgorithm {
225        match self {
226            Self::PbeWithMd2AndDesCbc => DigestAlgorithm::Md2,
227            Self::PbeWithMd2AndRc2Cbc => DigestAlgorithm::Md2,
228            Self::PbeWithMd5AndDesCbc => DigestAlgorithm::Md5,
229            Self::PbeWithMd5AndRc2Cbc => DigestAlgorithm::Md5,
230            Self::PbeWithSha1AndDesCbc => DigestAlgorithm::Sha1,
231            Self::PbeWithSha1AndRc2Cbc => DigestAlgorithm::Sha1,
232        }
233    }
234
235    /// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
236    #[must_use]
237    pub fn oid(self) -> ObjectIdentifier {
238        match self {
239            Self::PbeWithMd2AndDesCbc => PBE_WITH_MD2_AND_DES_CBC_OID,
240            Self::PbeWithMd2AndRc2Cbc => PBE_WITH_MD2_AND_RC2_CBC_OID,
241            Self::PbeWithMd5AndDesCbc => PBE_WITH_MD5_AND_DES_CBC_OID,
242            Self::PbeWithMd5AndRc2Cbc => PBE_WITH_MD5_AND_RC2_CBC_OID,
243            Self::PbeWithSha1AndDesCbc => PBE_WITH_SHA1_AND_DES_CBC_OID,
244            Self::PbeWithSha1AndRc2Cbc => PBE_WITH_SHA1_AND_RC2_CBC_OID,
245        }
246    }
247}
248
249impl Encode for EncryptionScheme {
250    fn encoded_len(&self) -> der::Result<Length> {
251        self.oid().encoded_len()
252    }
253
254    fn encode(&self, writer: &mut impl Writer) -> der::Result<()> {
255        self.oid().encode(writer)
256    }
257}
258
259/// Digest algorithms supported by PBES1.
260#[derive(Copy, Clone, Debug, Eq, PartialEq)]
261pub enum DigestAlgorithm {
262    /// MD2
263    Md2,
264
265    /// MD5
266    Md5,
267
268    /// SHA-1
269    Sha1,
270}
271
272/// Symmetric encryption ciphers supported by PBES1.
273#[derive(Copy, Clone, Debug, Eq, PartialEq)]
274pub enum SymmetricCipher {
275    /// DES in CBC mode
276    DesCbc,
277
278    /// RC2 in CBC mode
279    Rc2Cbc,
280}