use super::api::DecryptPolicy;
use super::content::Cek;
use super::key::{Direction, RecipientKey};
use super::random::RandomSource;
use super::{aes_gcm_kw, aes_kw, dir, ecdh_es, pbes2, rsa_oaep};
use crate::header::Header;
use crate::json::Object;
use crate::jwa::{ContentEncAlg, KeyMgmtAlg};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Mode {
KeyEncryption,
KeyWrapping,
DirectAgreement,
AgreementWithWrapping,
DirectEncryption,
}
impl Mode {
pub(crate) fn has_random_cek(self) -> bool {
matches!(
self,
Mode::KeyEncryption | Mode::KeyWrapping | Mode::AgreementWithWrapping
)
}
}
pub(crate) fn mode(alg: &KeyMgmtAlg) -> Option<Mode> {
use KeyMgmtAlg as A;
match alg {
A::RsaOaep256 => Some(Mode::KeyEncryption),
A::A128KW
| A::A192KW
| A::A256KW
| A::A128GCMKW
| A::A192GCMKW
| A::A256GCMKW
| A::Pbes2Hs256A128KW
| A::Pbes2Hs384A192KW
| A::Pbes2Hs512A256KW => Some(Mode::KeyWrapping),
A::EcdhEs => Some(Mode::DirectAgreement),
A::EcdhEsA128KW | A::EcdhEsA192KW | A::EcdhEsA256KW => Some(Mode::AgreementWithWrapping),
A::Dir => Some(Mode::DirectEncryption),
A::Unknown(_) => None,
}
}
pub(crate) fn supported(alg: &KeyMgmtAlg, direction: Direction) -> bool {
let _ = direction;
!matches!(alg, KeyMgmtAlg::Unknown(_))
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct RecipientOptions {
pub(crate) apu: Option<Vec<u8>>,
pub(crate) apv: Option<Vec<u8>>,
pub(crate) p2c: Option<u32>,
pub(crate) p2s_len: Option<usize>,
}
#[derive(Debug)]
pub(crate) struct KeyOutput {
pub(crate) encrypted_key: Vec<u8>,
pub(crate) header_params: Object,
pub(crate) cek: Option<Cek>,
}
pub(crate) fn encrypt_key(
alg: &KeyMgmtAlg,
key: &RecipientKey,
enc: &ContentEncAlg,
cek: Option<&Cek>,
options: &RecipientOptions,
rng: &mut dyn RandomSource,
) -> Result<KeyOutput> {
use KeyMgmtAlg as A;
match alg {
A::Dir => dir::encrypt_key(alg, key, enc, cek, options, rng),
A::A128KW | A::A192KW | A::A256KW => aes_kw::encrypt_key(alg, key, enc, cek, options, rng),
A::A128GCMKW | A::A192GCMKW | A::A256GCMKW => {
aes_gcm_kw::encrypt_key(alg, key, enc, cek, options, rng)
}
A::RsaOaep256 => rsa_oaep::encrypt_key(alg, key, enc, cek, options, rng),
A::Pbes2Hs256A128KW | A::Pbes2Hs384A192KW | A::Pbes2Hs512A256KW => {
pbes2::encrypt_key(alg, key, enc, cek, options, rng)
}
A::EcdhEs | A::EcdhEsA128KW | A::EcdhEsA192KW | A::EcdhEsA256KW => {
ecdh_es::encrypt_key(alg, key, enc, cek, options, rng)
}
A::Unknown(_) => Err(Error::UnsupportedAlgorithm),
}
}
pub(crate) fn decrypt_key(
alg: &KeyMgmtAlg,
key: &RecipientKey,
enc: &ContentEncAlg,
header: &Header,
encrypted_key: &[u8],
policy: &DecryptPolicy,
pbes2_budget: &mut pbes2::Budget,
) -> Result<Cek> {
use KeyMgmtAlg as A;
match alg {
A::Dir => dir::decrypt_key(alg, key, enc, header, encrypted_key, policy),
A::A128KW | A::A192KW | A::A256KW => {
aes_kw::decrypt_key(alg, key, enc, header, encrypted_key, policy)
}
A::A128GCMKW | A::A192GCMKW | A::A256GCMKW => {
aes_gcm_kw::decrypt_key(alg, key, enc, header, encrypted_key, policy)
}
A::Pbes2Hs256A128KW | A::Pbes2Hs384A192KW | A::Pbes2Hs512A256KW => {
pbes2::decrypt_key(alg, key, enc, header, encrypted_key, pbes2_budget)
}
A::EcdhEs | A::EcdhEsA128KW | A::EcdhEsA192KW | A::EcdhEsA256KW => {
ecdh_es::decrypt_key(alg, key, enc, header, encrypted_key, policy)
}
A::RsaOaep256 => rsa_oaep::decrypt_key(alg, key, enc, header, encrypted_key, policy),
A::Unknown(_) => Err(Error::UnsupportedAlgorithm),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_known_alg_has_its_rfc_7516_mode() {
use KeyMgmtAlg as A;
let expected = [
(A::RsaOaep256, Mode::KeyEncryption),
(A::A128KW, Mode::KeyWrapping),
(A::A192KW, Mode::KeyWrapping),
(A::A256KW, Mode::KeyWrapping),
(A::Dir, Mode::DirectEncryption),
(A::EcdhEs, Mode::DirectAgreement),
(A::EcdhEsA128KW, Mode::AgreementWithWrapping),
(A::EcdhEsA192KW, Mode::AgreementWithWrapping),
(A::EcdhEsA256KW, Mode::AgreementWithWrapping),
(A::A128GCMKW, Mode::KeyWrapping),
(A::A192GCMKW, Mode::KeyWrapping),
(A::A256GCMKW, Mode::KeyWrapping),
(A::Pbes2Hs256A128KW, Mode::KeyWrapping),
(A::Pbes2Hs384A192KW, Mode::KeyWrapping),
(A::Pbes2Hs512A256KW, Mode::KeyWrapping),
];
assert_eq!(expected.len(), A::KNOWN.len());
for (alg, m) in expected {
assert_eq!(mode(&alg), Some(m), "{alg}");
}
for name in ["RSA-OAEP", "RSA1_5", "ECDH-1PU", ""] {
assert_eq!(mode(&A::from(name)), None, "{name}");
}
let random: Vec<bool> = [
Mode::KeyEncryption,
Mode::KeyWrapping,
Mode::AgreementWithWrapping,
Mode::DirectAgreement,
Mode::DirectEncryption,
]
.map(Mode::has_random_cek)
.to_vec();
assert_eq!(random, [true, true, true, false, false]);
}
#[test]
fn every_known_alg_is_supported_both_ways_and_unknown_is_not() {
for alg in KeyMgmtAlg::KNOWN {
assert!(supported(alg, Direction::Encrypt), "{alg}");
assert!(supported(alg, Direction::Decrypt), "{alg}");
}
for name in ["RSA-OAEP", "RSA1_5", "nope"] {
let alg = KeyMgmtAlg::from(name);
assert!(!supported(&alg, Direction::Encrypt), "{name}");
assert!(!supported(&alg, Direction::Decrypt), "{name}");
}
}
#[test]
fn rsa1_5_is_unsupported_and_rsa_oaep_256_needs_an_rsa_private_key() {
let key = RecipientKey::RsaPublic {
n: vec![0xC1; 256],
e: vec![1, 0, 1],
};
let policy = DecryptPolicy::new(&[KeyMgmtAlg::RsaOaep256], &[ContentEncAlg::A128GCM])
.unwrap_or_else(|e| panic!("{e}"));
let enc = ContentEncAlg::A128GCM;
let header = Header::new();
let mut budget = pbes2::Budget::new(&policy);
let rsa1_5 = KeyMgmtAlg::from("RSA1_5");
let result = decrypt_key(
&rsa1_5,
&key,
&enc,
&header,
&[0; 256],
&policy,
&mut budget,
);
assert_eq!(result.map(|_| ()), Err(Error::UnsupportedAlgorithm));
let result = decrypt_key(
&KeyMgmtAlg::RsaOaep256,
&key,
&enc,
&header,
&[0; 256],
&policy,
&mut budget,
);
assert_eq!(result.map(|_| ()), Err(Error::KeyAlgorithmMismatch));
let mut rng = super::super::random::OsRandom;
let unknown = KeyMgmtAlg::from("RSA-OAEP");
let options = RecipientOptions::default();
let result = encrypt_key(&unknown, &key, &enc, None, &options, &mut rng);
assert_eq!(result.map(|_| ()), Err(Error::UnsupportedAlgorithm));
}
}