From a44febb6a11ce01c32f9e7c138d1aaead2fd83c9 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Mon, 7 Aug 2023 23:03:43 +0000 Subject: [PATCH] pkcs5: Make storage generic Signed-off-by: Arthur Gautier --- der/src/referenced.rs | 19 ++++ pkcs5/src/lib.rs | 76 +++++++++++---- pkcs5/src/pbes2.rs | 173 ++++++++++++++++++++++------------ pkcs5/src/pbes2/encryption.rs | 164 +++++++++++++++++++++----------- pkcs5/src/pbes2/kdf.rs | 124 +++++++++++++++--------- 5 files changed, 383 insertions(+), 173 deletions(-) diff --git a/der/src/referenced.rs b/der/src/referenced.rs index b0c8f0325..d5685706b 100644 --- a/der/src/referenced.rs +++ b/der/src/referenced.rs @@ -46,6 +46,25 @@ where } } +impl<'a, T, const N: usize> RefToOwned<'a> for &'a [T; N] +where + T: Clone, +{ + type Owned = [T; N]; + + fn ref_to_owned(&self) -> Self::Owned { + self.clone().clone() + } +} + +impl OwnedToRef for [T; N] { + type Borrowed<'a> = &'a [T; N] where T:'a; + + fn owned_to_ref(&self) -> Self::Borrowed<'_> { + &self + } +} + #[cfg(feature = "alloc")] mod allocating { use super::{OwnedToRef, RefToOwned}; diff --git a/pkcs5/src/lib.rs b/pkcs5/src/lib.rs index 08993c245..45753babc 100644 --- a/pkcs5/src/lib.rs +++ b/pkcs5/src/lib.rs @@ -45,11 +45,17 @@ pub use scrypt; #[cfg(all(feature = "alloc", feature = "pbes2"))] use alloc::vec::Vec; +pub type EncryptionScheme<'a> = EncryptionSchemeInner< + &'a [u8; pbes2::AES_BLOCK_SIZE], + &'a [u8; pbes2::DES_BLOCK_SIZE], + &'a [u8], +>; + /// Supported PKCS#5 password-based encryption schemes. #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] #[allow(clippy::large_enum_variant)] -pub enum EncryptionScheme<'a> { +pub enum EncryptionSchemeInner { /// Password-Based Encryption Scheme 1 as defined in [RFC 8018 Section 6.1]. /// /// [RFC 8018 Section 6.1]: https://tools.ietf.org/html/rfc8018#section-6.1 @@ -58,10 +64,15 @@ pub enum EncryptionScheme<'a> { /// Password-Based Encryption Scheme 2 as defined in [RFC 8018 Section 6.2]. /// /// [RFC 8018 Section 6.2]: https://tools.ietf.org/html/rfc8018#section-6.2 - Pbes2(pbes2::Parameters<'a>), + Pbes2(pbes2::ParametersInner), } -impl<'a> EncryptionScheme<'a> { +impl EncryptionSchemeInner +where + Salt: AsRef<[u8]>, + AesBlock: AsRef<[u8]>, + DesBlock: AsRef<[u8]>, +{ /// Attempt to decrypt the given ciphertext, allocating and returning a /// byte vector containing the plaintext. #[cfg(all(feature = "alloc", feature = "pbes2"))] @@ -132,7 +143,7 @@ impl<'a> EncryptionScheme<'a> { } /// Get [`pbes2::Parameters`] if it is the selected algorithm. - pub fn pbes2(&self) -> Option<&pbes2::Parameters<'a>> { + pub fn pbes2(&self) -> Option<&pbes2::ParametersInner> { match self { Self::Pbes2(params) => Some(params), _ => None, @@ -140,13 +151,24 @@ impl<'a> EncryptionScheme<'a> { } } -impl<'a> DecodeValue<'a> for EncryptionScheme<'a> { +impl<'a, AesBlock, DesBlock, Salt> DecodeValue<'a> + for EncryptionSchemeInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ fn decode_value>(decoder: &mut R, header: Header) -> der::Result { AlgorithmIdentifierRef::decode_value(decoder, header)?.try_into() } } -impl EncodeValue for EncryptionScheme<'_> { +impl<'a, AesBlock, DesBlock, Salt> EncodeValue for EncryptionSchemeInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ fn value_len(&self) -> der::Result { match self { Self::Pbes1(pbes1) => pbes1.oid().encoded_len()? + pbes1.parameters.encoded_len()?, @@ -170,27 +192,43 @@ impl EncodeValue for EncryptionScheme<'_> { } } -impl<'a> Sequence<'a> for EncryptionScheme<'a> {} +impl<'a, AesBlock, DesBlock, Salt> Sequence<'a> for EncryptionSchemeInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ +} -impl<'a> From for EncryptionScheme<'a> { - fn from(alg: pbes1::Algorithm) -> EncryptionScheme<'a> { +impl From + for EncryptionSchemeInner +{ + fn from(alg: pbes1::Algorithm) -> Self { Self::Pbes1(alg) } } -impl<'a> From> for EncryptionScheme<'a> { - fn from(params: pbes2::Parameters<'a>) -> EncryptionScheme<'a> { +impl From> + for EncryptionSchemeInner +{ + fn from(params: pbes2::ParametersInner) -> Self { Self::Pbes2(params) } } -impl<'a> TryFrom> for EncryptionScheme<'a> { +impl<'a, AesBlock, DesBlock, Salt> TryFrom> + for EncryptionSchemeInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ type Error = der::Error; - fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result> { + fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result { if alg.oid == pbes2::PBES2_OID { match alg.parameters { - Some(params) => pbes2::Parameters::try_from(params).map(Into::into), + Some(params) => pbes2::ParametersInner::try_from(params).map(Into::into), None => Err(Tag::OctetString.value_error()), } } else { @@ -199,10 +237,16 @@ impl<'a> TryFrom> for EncryptionScheme<'a> { } } -impl<'a> TryFrom<&'a [u8]> for EncryptionScheme<'a> { +impl<'a, AesBlock, DesBlock, Salt> TryFrom<&'a [u8]> + for EncryptionSchemeInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ type Error = der::Error; - fn try_from(bytes: &'a [u8]) -> der::Result> { + fn try_from(bytes: &'a [u8]) -> der::Result { AlgorithmIdentifierRef::from_der(bytes)?.try_into() } } diff --git a/pkcs5/src/pbes2.rs b/pkcs5/src/pbes2.rs index 301105cc2..e59a6304d 100644 --- a/pkcs5/src/pbes2.rs +++ b/pkcs5/src/pbes2.rs @@ -8,8 +8,8 @@ mod kdf; mod encryption; pub use self::kdf::{ - Kdf, Pbkdf2Params, Pbkdf2Prf, ScryptParams, HMAC_WITH_SHA1_OID, HMAC_WITH_SHA256_OID, - PBKDF2_OID, SCRYPT_OID, + Kdf, KdfInner, Pbkdf2Params, Pbkdf2ParamsInner, Pbkdf2Prf, ScryptParams, ScryptParamsInner, + HMAC_WITH_SHA1_OID, HMAC_WITH_SHA256_OID, PBKDF2_OID, SCRYPT_OID, }; use crate::{AlgorithmIdentifierRef, Error, Result}; @@ -50,11 +50,11 @@ pub const DES_EDE3_CBC_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2 pub const PBES2_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.13"); /// AES cipher block size -const AES_BLOCK_SIZE: usize = 16; +pub const AES_BLOCK_SIZE: usize = 16; /// DES / Triple DES block size #[cfg(any(feature = "3des", feature = "des-insecure"))] -const DES_BLOCK_SIZE: usize = 8; +pub const DES_BLOCK_SIZE: usize = 8; /// Password-Based Encryption Scheme 2 parameters as defined in [RFC 8018 Appendix A.4]. /// @@ -66,24 +66,29 @@ const DES_BLOCK_SIZE: usize = 8; /// /// [RFC 8018 Appendix A.4]: https://tools.ietf.org/html/rfc8018#appendix-A.4 #[derive(Clone, Debug, Eq, PartialEq)] -pub struct Parameters<'a> { +pub struct ParametersInner { /// Key derivation function - pub kdf: Kdf<'a>, + pub kdf: KdfInner, /// Encryption scheme - pub encryption: EncryptionScheme<'a>, + pub encryption: EncryptionSchemeInner, } -impl<'a> Parameters<'a> { +impl ParametersInner +where + Salt: AsRef<[u8]>, + AesBlock: AsRef<[u8]>, + DesBlock: AsRef<[u8]>, +{ /// Initialize PBES2 parameters using PBKDF2-SHA256 as the password-based /// key derivation function and AES-128-CBC as the symmetric cipher. pub fn pbkdf2_sha256_aes128cbc( pbkdf2_iterations: u32, - pbkdf2_salt: &'a [u8], - aes_iv: &'a [u8; AES_BLOCK_SIZE], + pbkdf2_salt: Salt, + aes_iv: AesBlock, ) -> Result { - let kdf = Pbkdf2Params::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into(); - let encryption = EncryptionScheme::Aes128Cbc { iv: aes_iv }; + let kdf = Pbkdf2ParamsInner::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into(); + let encryption = EncryptionSchemeInner::Aes128Cbc { iv: aes_iv }; Ok(Self { kdf, encryption }) } @@ -91,11 +96,11 @@ impl<'a> Parameters<'a> { /// key derivation function and AES-256-CBC as the symmetric cipher. pub fn pbkdf2_sha256_aes256cbc( pbkdf2_iterations: u32, - pbkdf2_salt: &'a [u8], - aes_iv: &'a [u8; AES_BLOCK_SIZE], + pbkdf2_salt: Salt, + aes_iv: AesBlock, ) -> Result { - let kdf = Pbkdf2Params::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into(); - let encryption = EncryptionScheme::Aes256Cbc { iv: aes_iv }; + let kdf = Pbkdf2ParamsInner::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into(); + let encryption = EncryptionSchemeInner::Aes256Cbc { iv: aes_iv }; Ok(Self { kdf, encryption }) } @@ -106,13 +111,9 @@ impl<'a> Parameters<'a> { /// [`scrypt::Params`] struct. // TODO(tarcieri): encapsulate `scrypt::Params`? #[cfg(feature = "pbes2")] - pub fn scrypt_aes128cbc( - params: scrypt::Params, - salt: &'a [u8], - aes_iv: &'a [u8; AES_BLOCK_SIZE], - ) -> Result { - let kdf = ScryptParams::from_params_and_salt(params, salt)?.into(); - let encryption = EncryptionScheme::Aes128Cbc { iv: aes_iv }; + pub fn scrypt_aes128cbc(params: scrypt::Params, salt: Salt, aes_iv: AesBlock) -> Result { + let kdf = ScryptParamsInner::from_params_and_salt(params, salt)?.into(); + let encryption = EncryptionSchemeInner::Aes128Cbc { iv: aes_iv }; Ok(Self { kdf, encryption }) } @@ -126,13 +127,9 @@ impl<'a> Parameters<'a> { /// This also avoids the need to import the type from the `scrypt` crate. // TODO(tarcieri): encapsulate `scrypt::Params`? #[cfg(feature = "pbes2")] - pub fn scrypt_aes256cbc( - params: scrypt::Params, - salt: &'a [u8], - aes_iv: &'a [u8; AES_BLOCK_SIZE], - ) -> Result { - let kdf = ScryptParams::from_params_and_salt(params, salt)?.into(); - let encryption = EncryptionScheme::Aes256Cbc { iv: aes_iv }; + pub fn scrypt_aes256cbc(params: scrypt::Params, salt: Salt, aes_iv: AesBlock) -> Result { + let kdf = ScryptParamsInner::from_params_and_salt(params, salt)?.into(); + let encryption = EncryptionSchemeInner::Aes256Cbc { iv: aes_iv }; Ok(Self { kdf, encryption }) } @@ -192,13 +189,23 @@ impl<'a> Parameters<'a> { } } -impl<'a> DecodeValue<'a> for Parameters<'a> { +impl<'a, AesBlock, DesBlock, Salt> DecodeValue<'a> for ParametersInner +where + Salt: From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ fn decode_value>(reader: &mut R, header: der::Header) -> der::Result { AnyRef::decode_value(reader, header)?.try_into() } } -impl EncodeValue for Parameters<'_> { +impl<'a, AesBlock, DesBlock, Salt> EncodeValue for ParametersInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ fn value_len(&self) -> der::Result { self.kdf.encoded_len()? + self.encryption.encoded_len()? } @@ -210,9 +217,20 @@ impl EncodeValue for Parameters<'_> { } } -impl<'a> Sequence<'a> for Parameters<'a> {} +impl<'a, AesBlock, DesBlock, Salt> Sequence<'a> for ParametersInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ +} -impl<'a> TryFrom> for Parameters<'a> { +impl<'a, AesBlock, DesBlock, Salt> TryFrom> for ParametersInner +where + Salt: From<&'a [u8]>, + AesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, + DesBlock: AsRef<[u8]> + TryFrom<&'a [u8]>, +{ type Error = der::Error; fn try_from(any: AnyRef<'a>) -> der::Result { @@ -228,44 +246,52 @@ impl<'a> TryFrom> for Parameters<'a> { } } +pub type Parameters<'a> = + ParametersInner<&'a [u8; AES_BLOCK_SIZE], &'a [u8; DES_BLOCK_SIZE], &'a [u8]>; + +pub type EncryptionScheme<'a> = + EncryptionSchemeInner<&'a [u8; AES_BLOCK_SIZE], &'a [u8; DES_BLOCK_SIZE]>; + +pub type EncryptionSchemeOwned = EncryptionSchemeInner<[u8; AES_BLOCK_SIZE], [u8; DES_BLOCK_SIZE]>; + /// Symmetric encryption scheme used by PBES2. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[non_exhaustive] -pub enum EncryptionScheme<'a> { +pub enum EncryptionSchemeInner { /// AES-128 in CBC mode Aes128Cbc { /// Initialization vector - iv: &'a [u8; AES_BLOCK_SIZE], + iv: AesBlock, }, /// AES-192 in CBC mode Aes192Cbc { /// Initialization vector - iv: &'a [u8; AES_BLOCK_SIZE], + iv: AesBlock, }, /// AES-256 in CBC mode Aes256Cbc { /// Initialization vector - iv: &'a [u8; AES_BLOCK_SIZE], + iv: AesBlock, }, /// 3-Key Triple DES in CBC mode #[cfg(feature = "3des")] DesEde3Cbc { /// Initialisation vector - iv: &'a [u8; DES_BLOCK_SIZE], + iv: DesBlock, }, /// DES in CBC mode #[cfg(feature = "des-insecure")] DesCbc { /// Initialisation vector - iv: &'a [u8; DES_BLOCK_SIZE], + iv: DesBlock, }, } -impl<'a> EncryptionScheme<'a> { +impl EncryptionSchemeInner { /// Get the size of a key used by this algorithm in bytes. pub fn key_size(&self) -> usize { match self { @@ -300,13 +326,22 @@ impl<'a> EncryptionScheme<'a> { } } -impl<'a> Decode<'a> for EncryptionScheme<'a> { +impl<'a, AesBlock, DesBlock> Decode<'a> for EncryptionSchemeInner +where + AesBlock: TryFrom<&'a [u8]>, + DesBlock: TryFrom<&'a [u8]>, +{ fn decode>(reader: &mut R) -> der::Result { AlgorithmIdentifierRef::decode(reader).and_then(TryInto::try_into) } } -impl<'a> TryFrom> for EncryptionScheme<'a> { +impl<'a, AesBlock, DesBlock> TryFrom> + for EncryptionSchemeInner +where + AesBlock: TryFrom<&'a [u8]>, + DesBlock: TryFrom<&'a [u8]>, +{ type Error = der::Error; fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result { @@ -349,33 +384,55 @@ impl<'a> TryFrom> for EncryptionScheme<'a> { } } -impl<'a> TryFrom> for AlgorithmIdentifierRef<'a> { - type Error = der::Error; - - fn try_from(scheme: EncryptionScheme<'a>) -> der::Result { - let parameters = OctetStringRef::new(match scheme { - EncryptionScheme::Aes128Cbc { iv } => iv, - EncryptionScheme::Aes192Cbc { iv } => iv, - EncryptionScheme::Aes256Cbc { iv } => iv, +impl EncryptionSchemeInner +where + AesBlock: AsRef<[u8]>, + DesBlock: AsRef<[u8]>, +{ + fn as_slice(&self) -> &[u8] { + match self { + EncryptionSchemeInner::Aes128Cbc { iv } => iv.as_ref(), + EncryptionSchemeInner::Aes192Cbc { iv } => iv.as_ref(), + EncryptionSchemeInner::Aes256Cbc { iv } => iv.as_ref(), #[cfg(feature = "des-insecure")] - EncryptionScheme::DesCbc { iv } => iv, + EncryptionSchemeInner::DesCbc { iv } => iv.as_ref(), #[cfg(feature = "3des")] - EncryptionScheme::DesEde3Cbc { iv } => iv, - })?; + EncryptionSchemeInner::DesEde3Cbc { iv } => iv.as_ref(), + } + } +} + +impl<'a, AesBlock, DesBlock> TryFrom<&'a EncryptionSchemeInner> + for AlgorithmIdentifierRef<'a> +where + AesBlock: AsRef<[u8]>, + DesBlock: AsRef<[u8]>, +{ + type Error = der::Error; + + fn try_from(scheme: &'a EncryptionSchemeInner) -> der::Result { + let oid = scheme.oid(); + + let parameters: &[u8] = scheme.as_slice(); + let parameters: OctetStringRef<'_> = OctetStringRef::new(parameters)?; Ok(AlgorithmIdentifierRef { - oid: scheme.oid(), + oid, parameters: Some(parameters.into()), }) } } -impl<'a> Encode for EncryptionScheme<'a> { +impl Encode for EncryptionSchemeInner +where + AesBlock: AsRef<[u8]>, + DesBlock: AsRef<[u8]>, +{ fn encoded_len(&self) -> der::Result { - AlgorithmIdentifierRef::try_from(*self)?.encoded_len() + AlgorithmIdentifierRef::try_from(self)?.encoded_len() } fn encode(&self, writer: &mut impl Writer) -> der::Result<()> { - AlgorithmIdentifierRef::try_from(*self)?.encode(writer) + AlgorithmIdentifierRef::try_from(self)?.encode(writer) } } diff --git a/pkcs5/src/pbes2/encryption.rs b/pkcs5/src/pbes2/encryption.rs index ea029b66a..bf1313cff 100644 --- a/pkcs5/src/pbes2/encryption.rs +++ b/pkcs5/src/pbes2/encryption.rs @@ -1,6 +1,9 @@ //! PBES2 encryption. -use super::{EncryptionScheme, Kdf, Parameters, Pbkdf2Params, Pbkdf2Prf, ScryptParams}; +use super::{ + EncryptionSchemeInner, KdfInner, ParametersInner, Pbkdf2ParamsInner, Pbkdf2Prf, + ScryptParamsInner, +}; use crate::{Error, Result}; use cbc::cipher::{ block_padding::Pkcs7, BlockCipher, BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit, @@ -19,8 +22,8 @@ use scrypt::scrypt; /// Maximum size of a derived encryption key const MAX_KEY_LEN: usize = 32; -fn cbc_encrypt<'a, C: BlockEncryptMut + BlockCipher + KeyInit>( - es: EncryptionScheme<'_>, +fn cbc_encrypt<'a, C: BlockEncryptMut + BlockCipher + KeyInit, AesBlock, DesBlock>( + es: &EncryptionSchemeInner, key: EncryptionKey, iv: &[u8], buffer: &'a mut [u8], @@ -32,8 +35,8 @@ fn cbc_encrypt<'a, C: BlockEncryptMut + BlockCipher + KeyInit>( .map_err(|_| Error::EncryptFailed) } -fn cbc_decrypt<'a, C: BlockDecryptMut + BlockCipher + KeyInit>( - es: EncryptionScheme<'_>, +fn cbc_decrypt<'a, C: BlockDecryptMut + BlockCipher + KeyInit, AesBlock, DesBlock>( + es: &EncryptionSchemeInner, key: EncryptionKey, iv: &[u8], buffer: &'a mut [u8], @@ -44,13 +47,18 @@ fn cbc_decrypt<'a, C: BlockDecryptMut + BlockCipher + KeyInit>( .map_err(|_| Error::EncryptFailed) } -pub fn encrypt_in_place<'b>( - params: &Parameters<'_>, +pub fn encrypt_in_place<'b, AesBlock, DesBlock, Salt>( + params: &ParametersInner, password: impl AsRef<[u8]>, buf: &'b mut [u8], pos: usize, -) -> Result<&'b [u8]> { - let es = params.encryption; +) -> Result<&'b [u8]> +where + Salt: AsRef<[u8]>, + AesBlock: AsRef<[u8]>, + DesBlock: AsRef<[u8]>, +{ + let es = ¶ms.encryption; let key_size = es.key_size(); if key_size > MAX_KEY_LEN { return Err(es.to_alg_params_invalid()); @@ -58,35 +66,60 @@ pub fn encrypt_in_place<'b>( let key = EncryptionKey::derive_from_password(password.as_ref(), ¶ms.kdf, key_size)?; match es { - EncryptionScheme::Aes128Cbc { iv } => cbc_encrypt::(es, key, iv, buf, pos), - EncryptionScheme::Aes192Cbc { iv } => cbc_encrypt::(es, key, iv, buf, pos), - EncryptionScheme::Aes256Cbc { iv } => cbc_encrypt::(es, key, iv, buf, pos), + EncryptionSchemeInner::::Aes128Cbc { iv } => { + cbc_encrypt::(es, key, iv.as_ref(), buf, pos) + } + EncryptionSchemeInner::::Aes192Cbc { iv } => { + cbc_encrypt::(es, key, iv.as_ref(), buf, pos) + } + EncryptionSchemeInner::::Aes256Cbc { iv } => { + cbc_encrypt::(es, key, iv.as_ref(), buf, pos) + } #[cfg(feature = "3des")] - EncryptionScheme::DesEde3Cbc { iv } => cbc_encrypt::(es, key, iv, buf, pos), + EncryptionSchemeInner::::DesEde3Cbc { iv } => { + cbc_encrypt::(es, key, iv.as_ref(), buf, pos) + } #[cfg(feature = "des-insecure")] - EncryptionScheme::DesCbc { .. } => Err(Error::UnsupportedAlgorithm { - oid: super::DES_CBC_OID, - }), + EncryptionSchemeInner::::DesCbc { .. } => { + Err(Error::UnsupportedAlgorithm { + oid: super::DES_CBC_OID, + }) + } } } /// Decrypt a message encrypted with PBES2-based key derivation -pub fn decrypt_in_place<'a>( - params: &Parameters<'_>, +pub fn decrypt_in_place<'a, AesBlock, DesBlock, Salt>( + params: &ParametersInner, password: impl AsRef<[u8]>, buf: &'a mut [u8], -) -> Result<&'a [u8]> { - let es = params.encryption; +) -> Result<&'a [u8]> +where + Salt: AsRef<[u8]>, + AesBlock: AsRef<[u8]>, + DesBlock: AsRef<[u8]>, +{ + let es = ¶ms.encryption; let key = EncryptionKey::derive_from_password(password.as_ref(), ¶ms.kdf, es.key_size())?; match es { - EncryptionScheme::Aes128Cbc { iv } => cbc_decrypt::(es, key, iv, buf), - EncryptionScheme::Aes192Cbc { iv } => cbc_decrypt::(es, key, iv, buf), - EncryptionScheme::Aes256Cbc { iv } => cbc_decrypt::(es, key, iv, buf), + EncryptionSchemeInner::::Aes128Cbc { iv } => { + cbc_decrypt::(es, key, iv.as_ref(), buf) + } + EncryptionSchemeInner::::Aes192Cbc { iv } => { + cbc_decrypt::(es, key, iv.as_ref(), buf) + } + EncryptionSchemeInner::::Aes256Cbc { iv } => { + cbc_decrypt::(es, key, iv.as_ref(), buf) + } #[cfg(feature = "3des")] - EncryptionScheme::DesEde3Cbc { iv } => cbc_decrypt::(es, key, iv, buf), + EncryptionSchemeInner::::DesEde3Cbc { iv } => { + cbc_decrypt::(es, key, iv.as_ref(), buf) + } #[cfg(feature = "des-insecure")] - EncryptionScheme::DesCbc { iv } => cbc_decrypt::(es, key, iv, buf), + EncryptionSchemeInner::::DesCbc { iv } => { + cbc_decrypt::(es, key, iv.as_ref(), buf) + } } } @@ -99,7 +132,14 @@ struct EncryptionKey { impl EncryptionKey { /// Derive an encryption key using the supplied PBKDF parameters. - pub fn derive_from_password(password: &[u8], kdf: &Kdf<'_>, key_size: usize) -> Result { + pub fn derive_from_password( + password: &[u8], + kdf: &KdfInner, + key_size: usize, + ) -> Result + where + Salt: AsRef<[u8]>, + { // if the kdf params defined a key length, ensure it matches the required key size if let Some(len) = kdf.key_length() { if key_size != len.into() { @@ -108,52 +148,62 @@ impl EncryptionKey { } match kdf { - Kdf::Pbkdf2(pbkdf2_params) => { + KdfInner::Pbkdf2(pbkdf2_params) => { let key = match pbkdf2_params.prf { #[cfg(feature = "sha1-insecure")] - Pbkdf2Prf::HmacWithSha1 => EncryptionKey::derive_with_pbkdf2::( - password, - pbkdf2_params, - key_size, - ), + Pbkdf2Prf::HmacWithSha1 => { + EncryptionKey::derive_with_pbkdf2::( + password, + pbkdf2_params, + key_size, + ) + } #[cfg(not(feature = "sha1-insecure"))] Pbkdf2Prf::HmacWithSha1 => { return Err(Error::UnsupportedAlgorithm { oid: super::HMAC_WITH_SHA1_OID, }) } - Pbkdf2Prf::HmacWithSha224 => EncryptionKey::derive_with_pbkdf2::( - password, - pbkdf2_params, - key_size, + Pbkdf2Prf::HmacWithSha224 => EncryptionKey::derive_with_pbkdf2::< + sha2::Sha224, + Salt, + >( + password, pbkdf2_params, key_size ), - Pbkdf2Prf::HmacWithSha256 => EncryptionKey::derive_with_pbkdf2::( - password, - pbkdf2_params, - key_size, + Pbkdf2Prf::HmacWithSha256 => EncryptionKey::derive_with_pbkdf2::< + sha2::Sha256, + Salt, + >( + password, pbkdf2_params, key_size ), - Pbkdf2Prf::HmacWithSha384 => EncryptionKey::derive_with_pbkdf2::( - password, - pbkdf2_params, - key_size, + Pbkdf2Prf::HmacWithSha384 => EncryptionKey::derive_with_pbkdf2::< + sha2::Sha384, + Salt, + >( + password, pbkdf2_params, key_size ), - Pbkdf2Prf::HmacWithSha512 => EncryptionKey::derive_with_pbkdf2::( - password, - pbkdf2_params, - key_size, + Pbkdf2Prf::HmacWithSha512 => EncryptionKey::derive_with_pbkdf2::< + sha2::Sha512, + Salt, + >( + password, pbkdf2_params, key_size ), }; Ok(key) } - Kdf::Scrypt(scrypt_params) => { + KdfInner::Scrypt(scrypt_params) => { EncryptionKey::derive_with_scrypt(password, scrypt_params, key_size) } } } /// Derive key using PBKDF2. - fn derive_with_pbkdf2(password: &[u8], params: &Pbkdf2Params<'_>, length: usize) -> Self + fn derive_with_pbkdf2( + password: &[u8], + params: &Pbkdf2ParamsInner, + length: usize, + ) -> Self where D: CoreProxy, D::Core: Sync @@ -165,12 +215,13 @@ impl EncryptionKey { + Clone, ::BlockSize: IsLess, Le<::BlockSize, U256>: NonZero, + Salt: AsRef<[u8]>, { let mut buffer = [0u8; MAX_KEY_LEN]; pbkdf2_hmac::( password, - params.salt, + params.salt.as_ref(), params.iteration_count, &mut buffer[..length], ); @@ -179,15 +230,18 @@ impl EncryptionKey { } /// Derive key using scrypt. - fn derive_with_scrypt( + fn derive_with_scrypt( password: &[u8], - params: &ScryptParams<'_>, + params: &ScryptParamsInner, length: usize, - ) -> Result { + ) -> Result + where + Salt: AsRef<[u8]>, + { let mut buffer = [0u8; MAX_KEY_LEN]; scrypt( password, - params.salt, + params.salt.as_ref(), ¶ms.try_into()?, &mut buffer[..length], ) diff --git a/pkcs5/src/pbes2/kdf.rs b/pkcs5/src/pbes2/kdf.rs index 63378cbcb..0434d8894 100644 --- a/pkcs5/src/pbes2/kdf.rs +++ b/pkcs5/src/pbes2/kdf.rs @@ -40,15 +40,15 @@ type ScryptCost = u64; /// Password-based key derivation function. #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] -pub enum Kdf<'a> { +pub enum KdfInner { /// Password-Based Key Derivation Function 2 (PBKDF2). - Pbkdf2(Pbkdf2Params<'a>), + Pbkdf2(Pbkdf2ParamsInner), /// scrypt sequential memory-hard password hashing function. - Scrypt(ScryptParams<'a>), + Scrypt(ScryptParamsInner), } -impl<'a> Kdf<'a> { +impl KdfInner { /// Get derived key length in bytes, if defined. // TODO(tarcieri): rename to `key_size` to match `EncryptionScheme::key_size`? pub fn key_length(&self) -> Option { @@ -67,7 +67,7 @@ impl<'a> Kdf<'a> { } /// Get [`Pbkdf2Params`] if it is the selected algorithm. - pub fn pbkdf2(&self) -> Option<&Pbkdf2Params<'a>> { + pub fn pbkdf2(&self) -> Option<&Pbkdf2ParamsInner> { match self { Self::Pbkdf2(params) => Some(params), _ => None, @@ -75,7 +75,7 @@ impl<'a> Kdf<'a> { } /// Get [`ScryptParams`] if it is the selected algorithm. - pub fn scrypt(&self) -> Option<&ScryptParams<'a>> { + pub fn scrypt(&self) -> Option<&ScryptParamsInner> { match self { Self::Scrypt(params) => Some(params), _ => None, @@ -99,13 +99,19 @@ impl<'a> Kdf<'a> { } } -impl<'a> DecodeValue<'a> for Kdf<'a> { +impl<'a, Salt> DecodeValue<'a> for KdfInner +where + Salt: From<&'a [u8]>, +{ fn decode_value>(reader: &mut R, header: der::Header) -> der::Result { AlgorithmIdentifierRef::decode_value(reader, header)?.try_into() } } -impl EncodeValue for Kdf<'_> { +impl<'a, Salt> EncodeValue for KdfInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, +{ fn value_len(&self) -> der::Result { self.oid().encoded_len()? + match self { @@ -126,21 +132,24 @@ impl EncodeValue for Kdf<'_> { } } -impl<'a> Sequence<'a> for Kdf<'a> {} +impl<'a, Salt> Sequence<'a> for KdfInner where Salt: AsRef<[u8]> + From<&'a [u8]> {} -impl<'a> From> for Kdf<'a> { - fn from(params: Pbkdf2Params<'a>) -> Self { - Kdf::Pbkdf2(params) +impl From> for KdfInner { + fn from(params: Pbkdf2ParamsInner) -> Self { + Self::Pbkdf2(params) } } -impl<'a> From> for Kdf<'a> { - fn from(params: ScryptParams<'a>) -> Self { - Kdf::Scrypt(params) +impl From> for KdfInner { + fn from(params: ScryptParamsInner) -> Self { + Self::Scrypt(params) } } -impl<'a> TryFrom> for Kdf<'a> { +impl<'a, Salt> TryFrom> for KdfInner +where + Salt: From<&'a [u8]>, +{ type Error = der::Error; fn try_from(alg: AlgorithmIdentifierRef<'a>) -> der::Result { @@ -156,6 +165,9 @@ impl<'a> TryFrom> for Kdf<'a> { } } +/// [`KdfInner`] with `&[u8]` salt. +pub type Kdf<'a> = KdfInner<&'a [u8]>; + /// Password-Based Key Derivation Scheme 2 parameters as defined in /// [RFC 8018 Appendix A.2]. /// @@ -173,10 +185,10 @@ impl<'a> TryFrom> for Kdf<'a> { /// /// [RFC 8018 Appendix A.2]: https://tools.ietf.org/html/rfc8018#appendix-A.2 #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Pbkdf2Params<'a> { +pub struct Pbkdf2ParamsInner { /// PBKDF2 salt // TODO(tarcieri): support `CHOICE` with `otherSource` - pub salt: &'a [u8], + pub salt: Salt, /// PBKDF2 iteration count pub iteration_count: u32, @@ -188,7 +200,7 @@ pub struct Pbkdf2Params<'a> { pub prf: Pbkdf2Prf, } -impl<'a> Pbkdf2Params<'a> { +impl Pbkdf2ParamsInner { /// Implementation defined maximum iteration count of 100,000,000. /// /// > For especially critical keys, or @@ -203,7 +215,7 @@ impl<'a> Pbkdf2Params<'a> { const INVALID_ERR: Error = Error::AlgorithmParametersInvalid { oid: PBKDF2_OID }; /// Initialize PBKDF2-SHA256 with the given iteration count and salt - pub fn hmac_with_sha256(iteration_count: u32, salt: &'a [u8]) -> Result { + pub fn hmac_with_sha256(iteration_count: u32, salt: Salt) -> Result { if iteration_count > Self::MAX_ITERATION_COUNT { return Err(Self::INVALID_ERR); } @@ -216,15 +228,21 @@ impl<'a> Pbkdf2Params<'a> { } } -impl<'a> DecodeValue<'a> for Pbkdf2Params<'a> { +impl<'a, Salt> DecodeValue<'a> for Pbkdf2ParamsInner +where + Salt: From<&'a [u8]>, +{ fn decode_value>(reader: &mut R, header: der::Header) -> der::Result { AnyRef::decode_value(reader, header)?.try_into() } } -impl EncodeValue for Pbkdf2Params<'_> { +impl<'a, Salt> EncodeValue for Pbkdf2ParamsInner +where + Salt: AsRef<[u8]> + From<&'a [u8]>, +{ fn value_len(&self) -> der::Result { - let len = OctetStringRef::new(self.salt)?.encoded_len()? + let len = OctetStringRef::new(self.salt.as_ref())?.encoded_len()? + self.iteration_count.encoded_len()? + self.key_length.encoded_len()?; @@ -236,7 +254,7 @@ impl EncodeValue for Pbkdf2Params<'_> { } fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { - OctetStringRef::new(self.salt)?.encode(writer)?; + OctetStringRef::new(self.salt.as_ref())?.encode(writer)?; self.iteration_count.encode(writer)?; self.key_length.encode(writer)?; @@ -248,16 +266,19 @@ impl EncodeValue for Pbkdf2Params<'_> { } } -impl<'a> Sequence<'a> for Pbkdf2Params<'a> {} +impl<'a, Salt> Sequence<'a> for Pbkdf2ParamsInner where Salt: From<&'a [u8]> + AsRef<[u8]> {} -impl<'a> TryFrom> for Pbkdf2Params<'a> { +impl<'a, Salt> TryFrom> for Pbkdf2ParamsInner +where + Salt: From<&'a [u8]>, +{ type Error = der::Error; fn try_from(any: AnyRef<'a>) -> der::Result { any.sequence(|reader| { // TODO(tarcieri): support salt `CHOICE` w\ `AlgorithmIdentifier` Ok(Self { - salt: OctetStringRef::decode(reader)?.as_bytes(), + salt: OctetStringRef::decode(reader)?.as_bytes().into(), iteration_count: reader.decode()?, key_length: reader.decode()?, prf: Option::>::decode(reader)? @@ -269,6 +290,9 @@ impl<'a> TryFrom> for Pbkdf2Params<'a> { } } +/// [`Pbkdf2ParamsInner`] with `&[u8]` salt. +pub type Pbkdf2Params<'a> = Pbkdf2ParamsInner<&'a [u8]>; + /// Pseudo-random function used by PBKDF2. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[non_exhaustive] @@ -377,9 +401,9 @@ impl Encode for Pbkdf2Prf { /// /// [RFC 7914 Section 7.1]: https://datatracker.ietf.org/doc/html/rfc7914#section-7.1 #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct ScryptParams<'a> { +pub struct ScryptParamsInner { /// scrypt salt - pub salt: &'a [u8], + pub salt: Salt, /// CPU/Memory cost parameter `N`. pub cost_parameter: ScryptCost, @@ -394,7 +418,7 @@ pub struct ScryptParams<'a> { pub key_length: Option, } -impl<'a> ScryptParams<'a> { +impl ScryptParamsInner { #[cfg(feature = "pbes2")] const INVALID_ERR: Error = Error::AlgorithmParametersInvalid { oid: SCRYPT_OID }; @@ -402,7 +426,7 @@ impl<'a> ScryptParams<'a> { /// and a provided salt string. // TODO(tarcieri): encapsulate `scrypt::Params`? #[cfg(feature = "pbes2")] - pub fn from_params_and_salt(params: scrypt::Params, salt: &'a [u8]) -> Result { + pub fn from_params_and_salt(params: scrypt::Params, salt: Salt) -> Result { Ok(Self { salt, cost_parameter: 1 << params.log_n(), @@ -413,15 +437,21 @@ impl<'a> ScryptParams<'a> { } } -impl<'a> DecodeValue<'a> for ScryptParams<'a> { +impl<'a, Salt> DecodeValue<'a> for ScryptParamsInner +where + Salt: From<&'a [u8]>, +{ fn decode_value>(reader: &mut R, header: der::Header) -> der::Result { AnyRef::decode_value(reader, header)?.try_into() } } -impl EncodeValue for ScryptParams<'_> { +impl EncodeValue for ScryptParamsInner +where + Salt: AsRef<[u8]>, +{ fn value_len(&self) -> der::Result { - OctetStringRef::new(self.salt)?.encoded_len()? + OctetStringRef::new(self.salt.as_ref())?.encoded_len()? + self.cost_parameter.encoded_len()? + self.block_size.encoded_len()? + self.parallelization.encoded_len()? @@ -429,7 +459,7 @@ impl EncodeValue for ScryptParams<'_> { } fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { - OctetStringRef::new(self.salt)?.encode(writer)?; + OctetStringRef::new(self.salt.as_ref())?.encode(writer)?; self.cost_parameter.encode(writer)?; self.block_size.encode(writer)?; self.parallelization.encode(writer)?; @@ -438,15 +468,18 @@ impl EncodeValue for ScryptParams<'_> { } } -impl<'a> Sequence<'a> for ScryptParams<'a> {} +impl<'a, Salt> Sequence<'a> for ScryptParamsInner where Salt: From<&'a [u8]> + AsRef<[u8]> {} -impl<'a> TryFrom> for ScryptParams<'a> { +impl<'a, Salt> TryFrom> for ScryptParamsInner +where + Salt: From<&'a [u8]>, +{ type Error = der::Error; fn try_from(any: AnyRef<'a>) -> der::Result { any.sequence(|reader| { Ok(Self { - salt: OctetStringRef::decode(reader)?.as_bytes(), + salt: OctetStringRef::decode(reader)?.as_bytes().into(), cost_parameter: reader.decode()?, block_size: reader.decode()?, parallelization: reader.decode()?, @@ -457,26 +490,26 @@ impl<'a> TryFrom> for ScryptParams<'a> { } #[cfg(feature = "pbes2")] -impl<'a> TryFrom> for scrypt::Params { +impl TryFrom> for scrypt::Params { type Error = Error; - fn try_from(params: ScryptParams<'a>) -> Result { + fn try_from(params: ScryptParamsInner) -> Result { scrypt::Params::try_from(¶ms) } } #[cfg(feature = "pbes2")] -impl<'a> TryFrom<&ScryptParams<'a>> for scrypt::Params { +impl TryFrom<&ScryptParamsInner> for scrypt::Params { type Error = Error; - fn try_from(params: &ScryptParams<'a>) -> Result { + fn try_from(params: &ScryptParamsInner) -> Result { let n = params.cost_parameter; // Compute log2 and verify its correctness let log_n = ((8 * core::mem::size_of::() as u32) - n.leading_zeros() - 1) as u8; if 1 << log_n != n { - return Err(ScryptParams::INVALID_ERR); + return Err(ScryptParamsInner::::INVALID_ERR); } scrypt::Params::new( @@ -485,6 +518,9 @@ impl<'a> TryFrom<&ScryptParams<'a>> for scrypt::Params { params.parallelization.into(), scrypt::Params::RECOMMENDED_LEN, ) - .map_err(|_| ScryptParams::INVALID_ERR) + .map_err(|_| ScryptParamsInner::::INVALID_ERR) } } + +/// [`ScryptParamsInner`] with `&[u8]` Salt parameter. +pub type ScryptParams<'a> = ScryptParamsInner<&'a [u8]>;