Skip to content

Commit 63d62b9

Browse files
authored
pkcs5: initial scrypt params support (#434)
Support for parsing scrypt parameters from a PBES2 AlgorithmIdentifier. No support for actually deriving an `EncryptionKey` using scrypt is yet included, but that's the next logical step. Additionally adds a test vector generated using OpenSSL: $ openssl pkcs8 -v2 aes-256-cbc -scrypt -topk8 -inform der -in ed25519-priv.der -out ed25519-encpriv-aes256-scrypt.pem
1 parent 1a27fc0 commit 63d62b9

13 files changed

+433
-277
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkcs5/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ readme = "README.md"
1717
der = { version = "0.3", features = ["oid"], path = "../der" }
1818
spki = { version = "0.3", path = "../spki" }
1919

20-
aes = { version = "0.7", optional = true }
20+
aes = { version = "0.7.3", optional = true }
2121
block-modes = { version = "0.8", optional = true, default-features = false }
2222
hmac = { version = "0.11", optional = true, default-features = false }
2323
pbkdf2 = { version = "0.8", optional = true, default-features = false }

pkcs5/src/pbes2.rs

+9-261
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,20 @@
22
//!
33
//! [RFC 8018 Section 6.2]: https://tools.ietf.org/html/rfc8018#section-6.2
44
5+
mod kdf;
6+
57
#[cfg(feature = "pbes2")]
68
mod encryption;
79

10+
pub use self::kdf::*;
11+
812
use crate::{AlgorithmIdentifier, CryptoError, ObjectIdentifier};
913
use core::convert::{TryFrom, TryInto};
1014
use der::{Any, Decodable, Encodable, Encoder, Error, ErrorKind, Length, Message, OctetString};
1115

1216
#[cfg(all(feature = "alloc", feature = "pbes2"))]
1317
use alloc::vec::Vec;
1418

15-
/// Password-Based Encryption Scheme 2 (PBES2) OID.
16-
///
17-
/// <https://tools.ietf.org/html/rfc8018#section-6.2>
18-
pub const PBES2_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.5.13");
19-
20-
/// Password-Based Key Derivation Function (PBKDF2) OID.
21-
pub const PBKDF2_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.5.12");
22-
23-
/// HMAC-SHA1 (for use with PBKDF2)
24-
pub const HMAC_WITH_SHA1_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.2.7");
25-
26-
/// HMAC-SHA-256 (for use with PBKDF2)
27-
pub const HMAC_WITH_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.2.9");
28-
2919
/// 128-bit Advanced Encryption Standard (AES) algorithm with Cipher-Block
3020
/// Chaining (CBC) mode of operation.
3121
pub const AES_128_CBC_OID: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.1.2");
@@ -34,6 +24,11 @@ pub const AES_128_CBC_OID: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.
3424
/// Chaining (CBC) mode of operation.
3525
pub const AES_256_CBC_OID: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.1.42");
3626

27+
/// Password-Based Encryption Scheme 2 (PBES2) OID.
28+
///
29+
/// <https://tools.ietf.org/html/rfc8018#section-6.2>
30+
pub const PBES2_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.5.13");
31+
3732
/// AES cipher block size
3833
const AES_BLOCK_SIZE: usize = 16;
3934

@@ -175,253 +170,6 @@ impl<'a> Message<'a> for Parameters<'a> {
175170
}
176171
}
177172

178-
/// Password-based key derivation function.
179-
#[derive(Clone, Debug, Eq, PartialEq)]
180-
#[non_exhaustive]
181-
pub enum Kdf<'a> {
182-
/// Password-Based Key Derivation Function 2 (PBKDF2).
183-
Pbkdf2(Pbkdf2Params<'a>),
184-
}
185-
186-
impl<'a> Kdf<'a> {
187-
/// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
188-
pub fn oid(&self) -> ObjectIdentifier {
189-
match self {
190-
Self::Pbkdf2(_) => PBKDF2_OID,
191-
}
192-
}
193-
194-
/// Get [`Pbkdf2Params`] if it is the selected algorithm.
195-
pub fn pbkdf2(&self) -> Option<&Pbkdf2Params<'a>> {
196-
match self {
197-
Self::Pbkdf2(params) => Some(params),
198-
}
199-
}
200-
201-
/// Is the selected KDF PBKDF2?
202-
pub fn is_pbkdf2(&self) -> bool {
203-
self.pbkdf2().is_some()
204-
}
205-
}
206-
207-
impl<'a> From<Pbkdf2Params<'a>> for Kdf<'a> {
208-
fn from(params: Pbkdf2Params<'a>) -> Self {
209-
Kdf::Pbkdf2(params)
210-
}
211-
}
212-
213-
impl<'a> TryFrom<Any<'a>> for Kdf<'a> {
214-
type Error = Error;
215-
216-
fn try_from(any: Any<'a>) -> der::Result<Self> {
217-
AlgorithmIdentifier::try_from(any).and_then(TryInto::try_into)
218-
}
219-
}
220-
221-
impl<'a> TryFrom<AlgorithmIdentifier<'a>> for Kdf<'a> {
222-
type Error = Error;
223-
224-
fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result<Self> {
225-
match alg.oid {
226-
PBKDF2_OID => alg
227-
.parameters_any()
228-
.and_then(TryFrom::try_from)
229-
.map(Self::Pbkdf2),
230-
oid => Err(ErrorKind::UnknownOid { oid }.into()),
231-
}
232-
}
233-
}
234-
235-
impl<'a> Message<'a> for Kdf<'a> {
236-
fn fields<F, T>(&self, f: F) -> der::Result<T>
237-
where
238-
F: FnOnce(&[&dyn Encodable]) -> der::Result<T>,
239-
{
240-
match self {
241-
Self::Pbkdf2(params) => f(&[&self.oid(), params]),
242-
}
243-
}
244-
}
245-
246-
/// Password-Based Key Derivation Scheme 2 parameters as defined in
247-
/// [RFC 8018 Appendix A.2].
248-
///
249-
/// ```text
250-
/// PBKDF2-params ::= SEQUENCE {
251-
/// salt CHOICE {
252-
/// specified OCTET STRING,
253-
/// otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
254-
/// },
255-
/// iterationCount INTEGER (1..MAX),
256-
/// keyLength INTEGER (1..MAX) OPTIONAL,
257-
/// prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT
258-
/// algid-hmacWithSHA1 }
259-
/// ```
260-
///
261-
/// [RFC 8018 Appendix A.2]: https://tools.ietf.org/html/rfc8018#appendix-A.2
262-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
263-
pub struct Pbkdf2Params<'a> {
264-
/// PBKDF2 salt
265-
// TODO(tarcieri): support `CHOICE` with `otherSource`
266-
pub salt: &'a [u8],
267-
268-
/// PBKDF2 iteration count
269-
pub iteration_count: u16,
270-
271-
/// PBKDF2 output length
272-
// TODO(tarcieri): support this OPTIONAL field
273-
// Blocked on: https://github.com/RustCrypto/utils/issues/271
274-
pub key_length: Option<u16>,
275-
276-
/// Pseudo-random function to use with PBKDF2
277-
pub prf: Pbkdf2Prf,
278-
}
279-
280-
impl<'a> Pbkdf2Params<'a> {
281-
/// Initialize PBKDF2-SHA256 with the given iteration count and salt
282-
pub fn hmac_with_sha256(iteration_count: u16, salt: &'a [u8]) -> Result<Self, CryptoError> {
283-
Ok(Self {
284-
salt,
285-
iteration_count,
286-
key_length: None,
287-
prf: Pbkdf2Prf::HmacWithSha256,
288-
})
289-
}
290-
}
291-
292-
impl<'a> TryFrom<Any<'a>> for Pbkdf2Params<'a> {
293-
type Error = Error;
294-
295-
fn try_from(any: Any<'a>) -> der::Result<Self> {
296-
any.sequence(|params| {
297-
// TODO(tarcieri): support salt `CHOICE` w\ `AlgorithmIdentifier`
298-
let salt = params.octet_string()?;
299-
let iteration_count = params.decode()?;
300-
301-
// TODO(tarcieri): support OPTIONAL key length field
302-
// Blocked on: https://github.com/RustCrypto/utils/issues/271
303-
let key_length = None;
304-
let prf: Option<AlgorithmIdentifier<'_>> = params.optional()?;
305-
306-
Ok(Self {
307-
salt: salt.as_bytes(),
308-
iteration_count,
309-
key_length,
310-
prf: prf.map(TryInto::try_into).transpose()?.unwrap_or_default(),
311-
})
312-
})
313-
}
314-
}
315-
316-
impl<'a> Message<'a> for Pbkdf2Params<'a> {
317-
fn fields<F, T>(&self, f: F) -> der::Result<T>
318-
where
319-
F: FnOnce(&[&dyn Encodable]) -> der::Result<T>,
320-
{
321-
if self.prf == Pbkdf2Prf::default() {
322-
f(&[
323-
&OctetString::new(self.salt)?,
324-
&self.iteration_count,
325-
&self.key_length,
326-
])
327-
} else {
328-
f(&[
329-
&OctetString::new(self.salt)?,
330-
&self.iteration_count,
331-
&self.key_length,
332-
&self.prf,
333-
])
334-
}
335-
}
336-
}
337-
338-
/// Pseudo-random function used by PBKDF2.
339-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
340-
#[non_exhaustive]
341-
pub enum Pbkdf2Prf {
342-
/// HMAC with SHA1
343-
HmacWithSha1,
344-
345-
/// HMAC with SHA-256
346-
HmacWithSha256,
347-
}
348-
349-
impl Pbkdf2Prf {
350-
/// Get the [`ObjectIdentifier`] (a.k.a OID) for this algorithm.
351-
pub fn oid(self) -> ObjectIdentifier {
352-
match self {
353-
Self::HmacWithSha1 => HMAC_WITH_SHA1_OID,
354-
Self::HmacWithSha256 => HMAC_WITH_SHA256_OID,
355-
}
356-
}
357-
}
358-
359-
/// Default PRF as specified in RFC 8018 Appendix A.2:
360-
///
361-
/// ```text
362-
/// prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
363-
/// ```
364-
///
365-
/// Note that modern usage should avoid the use of SHA-1, despite it being
366-
/// the "default" here.
367-
impl Default for Pbkdf2Prf {
368-
fn default() -> Self {
369-
Self::HmacWithSha1
370-
}
371-
}
372-
373-
impl<'a> TryFrom<Any<'a>> for Pbkdf2Prf {
374-
type Error = Error;
375-
376-
fn try_from(any: Any<'a>) -> der::Result<Self> {
377-
AlgorithmIdentifier::try_from(any).and_then(TryInto::try_into)
378-
}
379-
}
380-
381-
impl<'a> TryFrom<AlgorithmIdentifier<'a>> for Pbkdf2Prf {
382-
type Error = Error;
383-
384-
fn try_from(alg: AlgorithmIdentifier<'a>) -> der::Result<Self> {
385-
if let Some(params) = alg.parameters {
386-
// TODO(tarcieri): support non-NULL parameters?
387-
if !params.is_null() {
388-
return Err(ErrorKind::Value { tag: params.tag() }.into());
389-
}
390-
} else {
391-
// TODO(tarcieri): support OPTIONAL parameters?
392-
return Err(ErrorKind::Truncated.into());
393-
}
394-
395-
match alg.oid {
396-
HMAC_WITH_SHA1_OID => Ok(Self::HmacWithSha1),
397-
HMAC_WITH_SHA256_OID => Ok(Self::HmacWithSha256),
398-
oid => Err(ErrorKind::UnknownOid { oid }.into()),
399-
}
400-
}
401-
}
402-
403-
impl<'a> From<Pbkdf2Prf> for AlgorithmIdentifier<'a> {
404-
fn from(prf: Pbkdf2Prf) -> Self {
405-
// TODO(tarcieri): support non-NULL parameters?
406-
let parameters = der::Null;
407-
408-
AlgorithmIdentifier {
409-
oid: prf.oid(),
410-
parameters: Some(parameters.into()),
411-
}
412-
}
413-
}
414-
415-
impl Encodable for Pbkdf2Prf {
416-
fn encoded_len(&self) -> der::Result<Length> {
417-
AlgorithmIdentifier::try_from(*self)?.encoded_len()
418-
}
419-
420-
fn encode(&self, encoder: &mut Encoder<'_>) -> der::Result<()> {
421-
AlgorithmIdentifier::try_from(*self)?.encode(encoder)
422-
}
423-
}
424-
425173
/// Symmetric encryption scheme used by PBES2.
426174
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
427175
#[non_exhaustive]

0 commit comments

Comments
 (0)