use crate::{AlgorithmIdentifier, ObjectIdentifier, Result};
use core::convert::{TryFrom, TryInto};
use der::{Any, Decodable, Encodable, Encoder, Error, ErrorKind, Length, Message, OctetString};
use spki::AlgorithmParameters;
pub const PBES2_OID: ObjectIdentifier = ObjectIdentifier::new(&[1, 2, 840, 113549, 1, 5, 13]);
pub const PBKDF2_OID: ObjectIdentifier = ObjectIdentifier::new(&[1, 2, 840, 113549, 1, 5, 12]);
pub const HMAC_WITH_SHA1_OID: ObjectIdentifier = ObjectIdentifier::new(&[1, 2, 840, 113549, 2, 7]);
pub const HMAC_WITH_SHA256_OID: ObjectIdentifier =
ObjectIdentifier::new(&[1, 2, 840, 113549, 2, 9]);
pub const AES_128_CBC_OID: ObjectIdentifier =
ObjectIdentifier::new(&[2, 16, 840, 1, 101, 3, 4, 1, 2]);
pub const AES_256_CBC_OID: ObjectIdentifier =
ObjectIdentifier::new(&[2, 16, 840, 1, 101, 3, 4, 1, 42]);
const AES_BLOCK_SIZE: usize = 16;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Parameters<'a> {
pub kdf: Kdf<'a>,
pub encryption: EncryptionScheme<'a>,
}
impl<'a> TryFrom<Any<'a>> for Parameters<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.sequence(|params| {
let kdf = AlgorithmIdentifier::decode(params)?;
let encryption = AlgorithmIdentifier::decode(params)?;
Ok(Self {
kdf: kdf.try_into()?,
encryption: encryption.try_into()?,
})
})
}
}
impl<'a> Message<'a> for Parameters<'a> {
fn fields<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(&[&dyn Encodable]) -> Result<T>,
{
f(&[&self.kdf, &self.encryption])
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Kdf<'a> {
Pbkdf2(Pbkdf2Params<'a>),
}
impl<'a> Kdf<'a> {
pub fn oid(&self) -> ObjectIdentifier {
match self {
Self::Pbkdf2(_) => PBKDF2_OID,
}
}
pub fn pbkdf2(&self) -> Option<&Pbkdf2Params<'a>> {
match self {
Self::Pbkdf2(params) => Some(params),
}
}
pub fn is_pbkdf2(&self) -> bool {
self.pbkdf2().is_some()
}
}
impl<'a> TryFrom<Any<'a>> for Kdf<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
AlgorithmIdentifier::try_from(any).and_then(TryInto::try_into)
}
}
impl<'a> TryFrom<AlgorithmIdentifier<'a>> for Kdf<'a> {
type Error = Error;
fn try_from(alg: AlgorithmIdentifier<'a>) -> Result<Self> {
match alg.oid {
PBKDF2_OID => alg
.parameters_any()
.and_then(TryFrom::try_from)
.map(Self::Pbkdf2),
oid => Err(ErrorKind::UnknownOid { oid }.into()),
}
}
}
impl<'a> Message<'a> for Kdf<'a> {
fn fields<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(&[&dyn Encodable]) -> Result<T>,
{
match self {
Self::Pbkdf2(params) => f(&[&self.oid(), params]),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Pbkdf2Params<'a> {
pub salt: &'a [u8],
pub iteration_count: u16,
pub key_length: Option<u16>,
pub prf: Pbkdf2Prf,
}
impl<'a> TryFrom<Any<'a>> for Pbkdf2Params<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.sequence(|params| {
let salt = params.octet_string()?;
let iteration_count = params.decode()?;
let key_length = None;
let prf: Option<AlgorithmIdentifier<'_>> = params.optional()?;
Ok(Self {
salt: salt.as_bytes(),
iteration_count,
key_length,
prf: prf.map(TryInto::try_into).transpose()?.unwrap_or_default(),
})
})
}
}
impl<'a> Message<'a> for Pbkdf2Params<'a> {
fn fields<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(&[&dyn Encodable]) -> Result<T>,
{
if self.prf == Pbkdf2Prf::default() {
f(&[
&OctetString::new(self.salt)?,
&self.iteration_count,
&self.key_length,
])
} else {
f(&[
&OctetString::new(self.salt)?,
&self.iteration_count,
&self.key_length,
&self.prf,
])
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Pbkdf2Prf {
HmacWithSha1,
HmacWithSha256,
}
impl Pbkdf2Prf {
pub fn oid(self) -> ObjectIdentifier {
match self {
Self::HmacWithSha1 => HMAC_WITH_SHA1_OID,
Self::HmacWithSha256 => HMAC_WITH_SHA256_OID,
}
}
}
impl Default for Pbkdf2Prf {
fn default() -> Self {
Self::HmacWithSha1
}
}
impl<'a> TryFrom<Any<'a>> for Pbkdf2Prf {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
AlgorithmIdentifier::try_from(any).and_then(TryInto::try_into)
}
}
impl<'a> TryFrom<AlgorithmIdentifier<'a>> for Pbkdf2Prf {
type Error = Error;
fn try_from(alg: AlgorithmIdentifier<'a>) -> Result<Self> {
if let Some(params) = alg.parameters {
if !params.is_null() {
return Err(ErrorKind::Value { tag: params.tag() }.into());
}
} else {
return Err(ErrorKind::Truncated.into());
}
match alg.oid {
HMAC_WITH_SHA1_OID => Ok(Self::HmacWithSha1),
HMAC_WITH_SHA256_OID => Ok(Self::HmacWithSha256),
oid => Err(ErrorKind::UnknownOid { oid }.into()),
}
}
}
impl<'a> From<Pbkdf2Prf> for AlgorithmIdentifier<'a> {
fn from(prf: Pbkdf2Prf) -> Self {
let parameters = AlgorithmParameters::Null;
AlgorithmIdentifier {
oid: prf.oid(),
parameters: Some(parameters),
}
}
}
impl Encodable for Pbkdf2Prf {
fn encoded_len(&self) -> Result<Length> {
AlgorithmIdentifier::try_from(*self)?.encoded_len()
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
AlgorithmIdentifier::try_from(*self)?.encode(encoder)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum EncryptionScheme<'a> {
Aes128Cbc {
iv: &'a [u8; AES_BLOCK_SIZE],
},
Aes256Cbc {
iv: &'a [u8; AES_BLOCK_SIZE],
},
}
impl<'a> EncryptionScheme<'a> {
pub fn oid(self) -> ObjectIdentifier {
match self {
Self::Aes128Cbc { .. } => AES_128_CBC_OID,
Self::Aes256Cbc { .. } => AES_256_CBC_OID,
}
}
}
impl<'a> TryFrom<Any<'a>> for EncryptionScheme<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
AlgorithmIdentifier::try_from(any).and_then(TryInto::try_into)
}
}
impl<'a> TryFrom<AlgorithmIdentifier<'a>> for EncryptionScheme<'a> {
type Error = Error;
fn try_from(alg: AlgorithmIdentifier<'a>) -> Result<Self> {
let iv = alg
.parameters_any()?
.octet_string()?
.as_bytes()
.try_into()
.map_err(|_| ErrorKind::Value {
tag: der::Tag::OctetString,
})?;
match alg.oid {
AES_128_CBC_OID => Ok(Self::Aes128Cbc { iv }),
AES_256_CBC_OID => Ok(Self::Aes256Cbc { iv }),
oid => Err(ErrorKind::UnknownOid { oid }.into()),
}
}
}
impl<'a> TryFrom<EncryptionScheme<'a>> for AlgorithmIdentifier<'a> {
type Error = Error;
fn try_from(scheme: EncryptionScheme<'a>) -> Result<Self> {
let parameters = match scheme {
EncryptionScheme::Aes128Cbc { iv } => Any::from(OctetString::new(iv)?),
EncryptionScheme::Aes256Cbc { iv } => Any::from(OctetString::new(iv)?),
};
Ok(AlgorithmIdentifier {
oid: scheme.oid(),
parameters: Some(parameters.try_into()?),
})
}
}
impl<'a> Encodable for EncryptionScheme<'a> {
fn encoded_len(&self) -> Result<Length> {
AlgorithmIdentifier::try_from(*self)?.encoded_len()
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
AlgorithmIdentifier::try_from(*self)?.encode(encoder)
}
}