mod kdf;
#[cfg(feature = "pbes2")]
mod encryption;
pub use self::kdf::{
HMAC_WITH_SHA1_OID, HMAC_WITH_SHA256_OID, Kdf, PBKDF2_OID, Pbkdf2Params, Pbkdf2Prf, SCRYPT_OID,
Salt, ScryptParams,
};
use crate::{AlgorithmIdentifierRef, Error, Result};
use der::{
Decode, DecodeValue, Encode, EncodeValue, ErrorKind, Length, Reader, Sequence, Tag, Writer,
asn1::{AnyRef, ObjectIdentifier, OctetStringRef},
};
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
use rand_core::TryCryptoRng;
#[cfg(all(feature = "alloc", feature = "pbes2"))]
use alloc::vec::Vec;
pub const AES_128_CBC_OID: ObjectIdentifier =
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.2");
pub const AES_192_CBC_OID: ObjectIdentifier =
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.22");
pub const AES_256_CBC_OID: ObjectIdentifier =
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.42");
#[cfg(feature = "des-insecure")]
pub const DES_CBC_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.7");
#[cfg(feature = "3des")]
pub const DES_EDE3_CBC_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.3.7");
pub const PBES2_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.5.13");
const AES_BLOCK_SIZE: usize = 16;
#[cfg(any(feature = "3des", feature = "des-insecure"))]
const DES_BLOCK_SIZE: usize = 8;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Parameters {
pub kdf: Kdf,
pub encryption: EncryptionScheme,
}
impl Parameters {
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
const DEFAULT_IV_LEN: usize = AES_BLOCK_SIZE;
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
const DEFAULT_SALT_LEN: usize = 16;
#[cfg(all(feature = "pbes2", feature = "getrandom"))]
#[must_use]
#[track_caller]
pub fn generate() -> Self {
Self::generate_recommended(&mut getrandom::SysRng).expect("random generation failure")
}
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
pub fn generate_recommended<R: TryCryptoRng>(rng: &mut R) -> Result<Self> {
Self::generate_scrypt(rng)
}
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
pub fn generate_pbkdf2<R: TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut iv = [0u8; Self::DEFAULT_IV_LEN];
rng.try_fill_bytes(&mut iv).map_err(|_| Error::Rng)?;
let mut salt = [0u8; Self::DEFAULT_SALT_LEN];
rng.try_fill_bytes(&mut salt).map_err(|_| Error::Rng)?;
Self::generate_pbkdf2_sha256_aes256cbc(Pbkdf2Params::DEFAULT_SHA256_ITERATIONS, &salt, iv)
}
pub fn generate_pbkdf2_sha256_aes128cbc(
pbkdf2_iterations: u32,
pbkdf2_salt: &[u8],
aes_iv: [u8; AES_BLOCK_SIZE],
) -> Result<Self> {
let kdf = Pbkdf2Params::hmac_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
let encryption = EncryptionScheme::Aes128Cbc { iv: aes_iv };
Ok(Self { kdf, encryption })
}
pub fn generate_pbkdf2_sha256_aes256cbc(
pbkdf2_iterations: u32,
pbkdf2_salt: &[u8],
aes_iv: [u8; AES_BLOCK_SIZE],
) -> Result<Self> {
let kdf = Pbkdf2Params::hmac_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
let encryption = EncryptionScheme::Aes256Cbc { iv: aes_iv };
Ok(Self { kdf, encryption })
}
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
#[cfg(feature = "rand_core")]
pub fn generate_scrypt<R: TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut iv = [0u8; Self::DEFAULT_IV_LEN];
rng.try_fill_bytes(&mut iv).map_err(|_| Error::Rng)?;
let mut salt = [0u8; Self::DEFAULT_SALT_LEN];
rng.try_fill_bytes(&mut salt).map_err(|_| Error::Rng)?;
let params = scrypt::Params::new(
ScryptParams::DEFAULT_LOG_N,
ScryptParams::DEFAULT_R,
ScryptParams::DEFAULT_P,
)
.map_err(|_| Error::AlgorithmParametersInvalid { oid: SCRYPT_OID })?;
Self::generate_scrypt_aes256cbc(params, &salt, iv)
}
#[cfg(feature = "pbes2")]
pub fn generate_scrypt_aes128cbc(
params: scrypt::Params,
salt: &[u8],
aes_iv: [u8; AES_BLOCK_SIZE],
) -> Result<Self> {
let kdf = ScryptParams::from_params_and_salt(params, salt)?.into();
let encryption = EncryptionScheme::Aes128Cbc { iv: aes_iv };
Ok(Self { kdf, encryption })
}
#[cfg(feature = "pbes2")]
pub fn generate_scrypt_aes256cbc(
params: scrypt::Params,
salt: &[u8],
aes_iv: [u8; AES_BLOCK_SIZE],
) -> Result<Self> {
let kdf = ScryptParams::from_params_and_salt(params, salt)?.into();
let encryption = EncryptionScheme::Aes256Cbc { iv: aes_iv };
Ok(Self { kdf, encryption })
}
#[cfg(all(feature = "alloc", feature = "pbes2"))]
pub fn decrypt(&self, password: impl AsRef<[u8]>, ciphertext: &[u8]) -> Result<Vec<u8>> {
let mut buffer = ciphertext.to_vec();
let pt_len = self.decrypt_in_place(password, &mut buffer)?.len();
buffer.truncate(pt_len);
Ok(buffer)
}
#[cfg(feature = "pbes2")]
pub fn decrypt_in_place<'a>(
&self,
password: impl AsRef<[u8]>,
buffer: &'a mut [u8],
) -> Result<&'a [u8]> {
encryption::decrypt_in_place(self, password, buffer)
}
#[cfg(all(feature = "alloc", feature = "pbes2"))]
pub fn encrypt(&self, password: impl AsRef<[u8]>, plaintext: &[u8]) -> Result<Vec<u8>> {
let mut buffer = Vec::with_capacity(plaintext.len() + AES_BLOCK_SIZE);
buffer.extend_from_slice(plaintext);
buffer.extend_from_slice(&[0u8; AES_BLOCK_SIZE]);
let ct_len = self
.encrypt_in_place(password, &mut buffer, plaintext.len())?
.len();
buffer.truncate(ct_len);
Ok(buffer)
}
#[cfg(feature = "pbes2")]
pub fn encrypt_in_place<'a>(
&self,
password: impl AsRef<[u8]>,
buffer: &'a mut [u8],
pos: usize,
) -> Result<&'a [u8]> {
encryption::encrypt_in_place(self, password, buffer, pos)
}
}
impl<'a> DecodeValue<'a> for Parameters {
type Error = der::Error;
fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
AnyRef::decode_value(reader, header)?.try_into()
}
}
impl EncodeValue for Parameters {
fn value_len(&self) -> der::Result<Length> {
self.kdf.encoded_len()? + self.encryption.encoded_len()?
}
fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
self.kdf.encode(writer)?;
self.encryption.encode(writer)?;
Ok(())
}
}
impl Sequence<'_> for Parameters {}
impl TryFrom<AnyRef<'_>> for Parameters {
type Error = der::Error;
fn try_from(any: AnyRef<'_>) -> der::Result<Self> {
any.sequence(|params| {
let kdf = AlgorithmIdentifierRef::decode(params)?;
let encryption = AlgorithmIdentifierRef::decode(params)?;
Ok(Self {
kdf: kdf.try_into()?,
encryption: encryption.try_into()?,
})
})
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum EncryptionScheme {
Aes128Cbc {
iv: [u8; AES_BLOCK_SIZE],
},
Aes192Cbc {
iv: [u8; AES_BLOCK_SIZE],
},
Aes256Cbc {
iv: [u8; AES_BLOCK_SIZE],
},
#[cfg(feature = "3des")]
DesEde3Cbc {
iv: [u8; DES_BLOCK_SIZE],
},
#[cfg(feature = "des-insecure")]
DesCbc {
iv: [u8; DES_BLOCK_SIZE],
},
}
impl EncryptionScheme {
#[must_use]
pub fn key_size(&self) -> usize {
match self {
Self::Aes128Cbc { .. } => 16,
Self::Aes192Cbc { .. } => 24,
Self::Aes256Cbc { .. } => 32,
#[cfg(feature = "des-insecure")]
Self::DesCbc { .. } => 8,
#[cfg(feature = "3des")]
Self::DesEde3Cbc { .. } => 24,
}
}
#[must_use]
pub fn oid(&self) -> ObjectIdentifier {
match self {
Self::Aes128Cbc { .. } => AES_128_CBC_OID,
Self::Aes192Cbc { .. } => AES_192_CBC_OID,
Self::Aes256Cbc { .. } => AES_256_CBC_OID,
#[cfg(feature = "des-insecure")]
Self::DesCbc { .. } => DES_CBC_OID,
#[cfg(feature = "3des")]
Self::DesEde3Cbc { .. } => DES_EDE3_CBC_OID,
}
}
#[must_use]
pub fn to_alg_params_invalid(&self) -> Error {
Error::AlgorithmParametersInvalid { oid: self.oid() }
}
}
impl<'a> Decode<'a> for EncryptionScheme {
type Error = der::Error;
fn decode<R: Reader<'a>>(reader: &mut R) -> der::Result<Self> {
AlgorithmIdentifierRef::decode(reader).and_then(TryInto::try_into)
}
}
impl TryFrom<AlgorithmIdentifierRef<'_>> for EncryptionScheme {
type Error = der::Error;
fn try_from(alg: AlgorithmIdentifierRef<'_>) -> der::Result<Self> {
let iv = match alg.parameters {
Some(params) => params.decode_as::<&OctetStringRef>()?.as_bytes(),
None => return Err(Tag::OctetString.value_error().into()),
};
match alg.oid {
AES_128_CBC_OID => Ok(Self::Aes128Cbc {
iv: iv.try_into().map_err(|_| Tag::OctetString.value_error())?,
}),
AES_192_CBC_OID => Ok(Self::Aes192Cbc {
iv: iv.try_into().map_err(|_| Tag::OctetString.value_error())?,
}),
AES_256_CBC_OID => Ok(Self::Aes256Cbc {
iv: iv.try_into().map_err(|_| Tag::OctetString.value_error())?,
}),
#[cfg(feature = "des-insecure")]
DES_CBC_OID => Ok(Self::DesCbc {
iv: iv[0..DES_BLOCK_SIZE]
.try_into()
.map_err(|_| Tag::OctetString.value_error())?,
}),
#[cfg(feature = "3des")]
DES_EDE3_CBC_OID => Ok(Self::DesEde3Cbc {
iv: iv[0..DES_BLOCK_SIZE]
.try_into()
.map_err(|_| Tag::OctetString.value_error())?,
}),
oid => Err(ErrorKind::OidUnknown { oid }.into()),
}
}
}
impl<'a> TryFrom<&'a EncryptionScheme> for AlgorithmIdentifierRef<'a> {
type Error = der::Error;
fn try_from(scheme: &'a EncryptionScheme) -> der::Result<Self> {
let parameters = OctetStringRef::new(match scheme {
EncryptionScheme::Aes128Cbc { iv } => iv.as_slice(),
EncryptionScheme::Aes192Cbc { iv } => iv.as_slice(),
EncryptionScheme::Aes256Cbc { iv } => iv.as_slice(),
#[cfg(feature = "des-insecure")]
EncryptionScheme::DesCbc { iv } => iv.as_slice(),
#[cfg(feature = "3des")]
EncryptionScheme::DesEde3Cbc { iv } => iv.as_slice(),
})?;
Ok(AlgorithmIdentifierRef {
oid: scheme.oid(),
parameters: Some(parameters.into()),
})
}
}
impl Encode for EncryptionScheme {
fn encoded_len(&self) -> der::Result<Length> {
AlgorithmIdentifierRef::try_from(self)?.encoded_len()
}
fn encode(&self, writer: &mut impl Writer) -> der::Result<()> {
AlgorithmIdentifierRef::try_from(self)?.encode(writer)
}
}