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 {
match alg {
KeyMgmtAlg::RsaOaep256 => direction == Direction::Encrypt,
KeyMgmtAlg::Unknown(_) => false,
_ => true,
}
}
#[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 | 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 rsa_oaep_256_is_encrypt_only_and_unknown_is_unsupported() {
for alg in KeyMgmtAlg::KNOWN {
let decrypts = *alg != KeyMgmtAlg::RsaOaep256;
assert!(supported(alg, Direction::Encrypt), "{alg}");
assert_eq!(supported(alg, Direction::Decrypt), decrypts, "{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 rsa_oaep_256_and_unknown_decrypt_fail_as_unsupported() {
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();
for alg in [KeyMgmtAlg::RsaOaep256, KeyMgmtAlg::from("RSA1_5")] {
let mut budget = pbes2::Budget::new(&policy);
let result = decrypt_key(&alg, &key, &enc, &header, &[0; 256], &policy, &mut budget);
assert_eq!(
result.map(|_| ()),
Err(Error::UnsupportedAlgorithm),
"{alg}"
);
}
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));
}
}