use crate::error::PdfError;
use super::cms::{
OID_AES128_CBC, OID_AES256_CBC, OID_DATA, OID_DES_EDE3_CBC, OID_ENVELOPED_DATA, OID_RC2_CBC,
OID_RC4, OID_RSA_ENCRYPTION,
};
use super::der::{
write_context_constructed, write_context_primitive, write_integer_bytes, write_integer_u64,
write_null, write_octet_string, write_oid, write_sequence, write_set,
};
#[derive(Debug, Clone)]
pub enum RecipientIdRef {
IssuerAndSerial {
issuer_der: Vec<u8>,
serial: Vec<u8>,
},
SubjectKeyIdentifier { ski: Vec<u8> },
}
#[derive(Debug, Clone)]
pub struct RecipientPlain {
pub rid: RecipientIdRef,
pub encrypted_key: Vec<u8>,
}
impl RecipientPlain {
pub fn ias(issuer_der: Vec<u8>, serial: Vec<u8>, encrypted_key: Vec<u8>) -> Self {
Self {
rid: RecipientIdRef::IssuerAndSerial { issuer_der, serial },
encrypted_key,
}
}
pub fn ski(ski: Vec<u8>, encrypted_key: Vec<u8>) -> Self {
Self {
rid: RecipientIdRef::SubjectKeyIdentifier { ski },
encrypted_key,
}
}
}
pub fn build_envelope_aes128(
recipients: &[RecipientPlain],
plaintext: &[u8],
cek: &[u8; 16],
iv: &[u8; 16],
) -> Vec<u8> {
let encrypted = aes128_cbc_encrypt_padded(cek, iv, plaintext);
build_envelope_inner(recipients, &OID_AES128_CBC, Some(iv), &encrypted, None)
}
pub fn build_envelope_aes256_with_originator_info(
recipients: &[RecipientPlain],
plaintext: &[u8],
cek: &[u8; 32],
iv: &[u8; 16],
originator_certs: &[Vec<u8>],
originator_crls: &[Vec<u8>],
) -> Vec<u8> {
let encrypted = aes256_cbc_encrypt_padded(cek, iv, plaintext);
let oi = if originator_certs.is_empty() && originator_crls.is_empty() {
None
} else {
Some(build_originator_info(originator_certs, originator_crls))
};
build_envelope_inner(
recipients,
&OID_AES256_CBC,
Some(iv),
&encrypted,
oi.as_deref(),
)
}
fn build_originator_info(certs: &[Vec<u8>], crls: &[Vec<u8>]) -> Vec<u8> {
let mut out = Vec::new();
if !certs.is_empty() {
let mut certs_body = Vec::new();
for c in certs {
certs_body.extend_from_slice(c);
}
out.extend_from_slice(&super::der::write_tlv(
super::der::Class::ContextSpecific,
true,
0,
&certs_body,
));
}
if !crls.is_empty() {
let mut crls_body = Vec::new();
for c in crls {
crls_body.extend_from_slice(c);
}
out.extend_from_slice(&super::der::write_tlv(
super::der::Class::ContextSpecific,
true,
1,
&crls_body,
));
}
out
}
pub fn build_envelope_aes256(
recipients: &[RecipientPlain],
plaintext: &[u8],
cek: &[u8; 32],
iv: &[u8; 16],
) -> Vec<u8> {
let encrypted = aes256_cbc_encrypt_padded(cek, iv, plaintext);
build_envelope_inner(recipients, &OID_AES256_CBC, Some(iv), &encrypted, None)
}
pub fn build_envelope_rc4(recipients: &[RecipientPlain], plaintext: &[u8], cek: &[u8]) -> Vec<u8> {
let encrypted = crate::decrypt::rc4(cek, plaintext);
build_envelope_inner(recipients, &OID_RC4, None, &encrypted, None)
}
#[doc(hidden)]
pub fn build_envelope_rc2_cbc(
recipients: &[RecipientPlain],
plaintext: &[u8],
cek: &[u8],
effective_key_bits: u32,
iv: &[u8; 8],
) -> Vec<u8> {
let encrypted = rc2_cbc_encrypt_padded(cek, effective_key_bits, iv, plaintext);
let version_byte: u32 = match effective_key_bits {
40 => 160,
64 => 120,
128 => 58,
v => v,
};
let mut params_body = super::der::write_integer_u64(version_byte as u64);
params_body.extend_from_slice(&super::der::write_octet_string(iv));
let params = super::der::write_sequence(¶ms_body);
build_envelope_inner_raw_params(recipients, &OID_RC2_CBC, ¶ms, &encrypted)
}
#[doc(hidden)]
pub fn build_envelope_des_ede3_cbc(
recipients: &[RecipientPlain],
plaintext: &[u8],
cek: &[u8; 24],
iv: &[u8; 8],
) -> Vec<u8> {
let encrypted = des_ede3_cbc_encrypt_padded(cek, iv, plaintext);
let params = super::der::write_octet_string(iv);
build_envelope_inner_raw_params(recipients, &OID_DES_EDE3_CBC, ¶ms, &encrypted)
}
fn build_envelope_inner(
recipients: &[RecipientPlain],
content_alg_oid: &[u64],
iv: Option<&[u8; 16]>,
encrypted_content: &[u8],
originator_info_body: Option<&[u8]>,
) -> Vec<u8> {
let envelope_version: u64 = if recipients
.iter()
.any(|r| matches!(r.rid, RecipientIdRef::SubjectKeyIdentifier { .. }))
{
2
} else {
0
};
let mut ri_set_body = Vec::new();
for r in recipients {
ri_set_body.extend_from_slice(&build_ktri(r));
}
let ri_set = write_set(&ri_set_body);
let alg_id = {
let mut body = write_oid(content_alg_oid);
match iv {
Some(iv) => body.extend_from_slice(&write_octet_string(iv)),
None => body.extend_from_slice(&write_null()),
}
write_sequence(&body)
};
let eci = {
let mut body = write_oid(&OID_DATA);
body.extend_from_slice(&alg_id);
body.extend_from_slice(&write_context_primitive(0, encrypted_content));
write_sequence(&body)
};
let enveloped_body = {
let mut b = write_integer_u64(envelope_version);
if let Some(oi_body) = originator_info_body {
b.extend_from_slice(&super::der::write_tlv(
super::der::Class::ContextSpecific,
true,
0,
oi_body,
));
}
b.extend_from_slice(&ri_set);
b.extend_from_slice(&eci);
b
};
let enveloped = write_sequence(&enveloped_body);
let outer_body = {
let mut b = write_oid(&OID_ENVELOPED_DATA);
b.extend_from_slice(&write_context_constructed(0, &enveloped));
b
};
write_sequence(&outer_body)
}
fn build_envelope_inner_raw_params(
recipients: &[RecipientPlain],
content_alg_oid: &[u64],
raw_params: &[u8],
encrypted_content: &[u8],
) -> Vec<u8> {
let envelope_version: u64 = if recipients
.iter()
.any(|r| matches!(r.rid, RecipientIdRef::SubjectKeyIdentifier { .. }))
{
2
} else {
0
};
let mut ri_set_body = Vec::new();
for r in recipients {
ri_set_body.extend_from_slice(&build_ktri(r));
}
let ri_set = write_set(&ri_set_body);
let alg_id = {
let mut body = write_oid(content_alg_oid);
body.extend_from_slice(raw_params);
write_sequence(&body)
};
let eci = {
let mut body = write_oid(&OID_DATA);
body.extend_from_slice(&alg_id);
body.extend_from_slice(&write_context_primitive(0, encrypted_content));
write_sequence(&body)
};
let enveloped_body = {
let mut b = write_integer_u64(envelope_version);
b.extend_from_slice(&ri_set);
b.extend_from_slice(&eci);
b
};
let enveloped = write_sequence(&enveloped_body);
let outer_body = {
let mut b = write_oid(&OID_ENVELOPED_DATA);
b.extend_from_slice(&write_context_constructed(0, &enveloped));
b
};
write_sequence(&outer_body)
}
fn build_ktri(r: &RecipientPlain) -> Vec<u8> {
let (version, rid_bytes) = match &r.rid {
RecipientIdRef::IssuerAndSerial { issuer_der, serial } => {
let serial_int = write_integer_bytes(serial);
let ias_body = {
let mut b = Vec::with_capacity(issuer_der.len() + serial_int.len());
b.extend_from_slice(issuer_der);
b.extend_from_slice(&serial_int);
b
};
(0u64, write_sequence(&ias_body))
}
RecipientIdRef::SubjectKeyIdentifier { ski } => {
(2u64, write_context_primitive(0, ski))
}
};
let kea = {
let mut b = write_oid(&OID_RSA_ENCRYPTION);
b.extend_from_slice(&super::der::write_null());
write_sequence(&b)
};
let mut body = write_integer_u64(version);
body.extend_from_slice(&rid_bytes);
body.extend_from_slice(&kea);
body.extend_from_slice(&write_octet_string(&r.encrypted_key));
write_sequence(&body)
}
fn aes128_cbc_encrypt_padded(key: &[u8; 16], iv: &[u8; 16], data: &[u8]) -> Vec<u8> {
use aes::cipher::{BlockEncryptMut, KeyIvInit};
type Enc = cbc::Encryptor<aes::Aes128>;
let enc = Enc::new(key.into(), iv.into());
let pad_block = (data.len() / 16) + 1;
let mut buf = vec![0u8; pad_block * 16];
let n = enc
.encrypt_padded_b2b_mut::<aes::cipher::block_padding::Pkcs7>(data, &mut buf)
.expect("PKCS7 padding")
.len();
buf.truncate(n);
buf
}
fn aes256_cbc_encrypt_padded(key: &[u8; 32], iv: &[u8; 16], data: &[u8]) -> Vec<u8> {
use aes::cipher::{BlockEncryptMut, KeyIvInit};
type Enc = cbc::Encryptor<aes::Aes256>;
let enc = Enc::new(key.into(), iv.into());
let pad_block = (data.len() / 16) + 1;
let mut buf = vec![0u8; pad_block * 16];
let n = enc
.encrypt_padded_b2b_mut::<aes::cipher::block_padding::Pkcs7>(data, &mut buf)
.expect("PKCS7 padding")
.len();
buf.truncate(n);
buf
}
fn rc2_cbc_encrypt_padded(
key: &[u8],
effective_key_bits: u32,
iv: &[u8; 8],
data: &[u8],
) -> Vec<u8> {
use cbc::cipher::{BlockEncryptMut, InnerIvInit};
use rc2::Rc2;
let cipher = Rc2::new_with_eff_key_len(key, effective_key_bits as usize);
let enc = cbc::Encryptor::<Rc2>::inner_iv_slice_init(cipher, iv).expect("RC2 IV init");
let pad_block = (data.len() / 8) + 1;
let mut buf = vec![0u8; pad_block * 8];
let n = enc
.encrypt_padded_b2b_mut::<cbc::cipher::block_padding::Pkcs7>(data, &mut buf)
.expect("PKCS7 padding")
.len();
buf.truncate(n);
buf
}
fn des_ede3_cbc_encrypt_padded(key: &[u8; 24], iv: &[u8; 8], data: &[u8]) -> Vec<u8> {
use cbc::cipher::{BlockEncryptMut, KeyIvInit};
use des::TdesEde3;
type Enc = cbc::Encryptor<TdesEde3>;
let enc = Enc::new(key.into(), iv.into());
let pad_block = (data.len() / 8) + 1;
let mut buf = vec![0u8; pad_block * 8];
let n = enc
.encrypt_padded_b2b_mut::<cbc::cipher::block_padding::Pkcs7>(data, &mut buf)
.expect("PKCS7 padding")
.len();
buf.truncate(n);
buf
}
pub fn rsa_pkcs1_encrypt(pubkey: &rsa::RsaPublicKey, cek: &[u8]) -> Result<Vec<u8>, PdfError> {
let mut rng = rsa::rand_core::OsRng;
pubkey
.encrypt(&mut rng, rsa::Pkcs1v15Encrypt, cek)
.map_err(|e| PdfError::other(format!("CMS: RSA encrypt failed: {e}")))
}
#[derive(Debug, Clone)]
pub enum OriginatorIdRef {
IssuerAndSerial {
issuer_der: Vec<u8>,
serial: Vec<u8>,
},
SubjectKeyIdentifier { ski: Vec<u8> },
OriginatorKey {
algorithm_oid: Vec<u64>,
algorithm_params: Vec<u8>,
public_key: Vec<u8>,
},
}
#[derive(Debug, Clone)]
pub enum KariRecipientIdRef {
IssuerAndSerial {
issuer_der: Vec<u8>,
serial: Vec<u8>,
},
RecipientKeyIdentifier {
ski: Vec<u8>,
date: Option<Vec<u8>>,
other: Option<(Vec<u64>, Vec<u8>)>,
},
}
#[derive(Debug, Clone)]
pub struct KariRecipientPlain {
pub rid: KariRecipientIdRef,
pub encrypted_key: Vec<u8>,
}
#[allow(clippy::too_many_arguments)]
pub fn build_envelope_kari_aes256(
originator: &OriginatorIdRef,
ukm: Option<&[u8]>,
key_encryption_oid: &[u64],
key_encryption_params: &[u8],
recipient_keys: &[KariRecipientPlain],
plaintext: &[u8],
cek: &[u8; 32],
iv: &[u8; 16],
) -> Vec<u8> {
let encrypted = aes256_cbc_encrypt_padded(cek, iv, plaintext);
build_envelope_kari_inner(
originator,
ukm,
key_encryption_oid,
key_encryption_params,
recipient_keys,
&OID_AES256_CBC,
Some(iv),
&encrypted,
)
}
#[allow(clippy::too_many_arguments)]
fn build_envelope_kari_inner(
originator: &OriginatorIdRef,
ukm: Option<&[u8]>,
key_encryption_oid: &[u64],
key_encryption_params: &[u8],
recipient_keys: &[KariRecipientPlain],
content_alg_oid: &[u64],
iv: Option<&[u8; 16]>,
encrypted_content: &[u8],
) -> Vec<u8> {
use super::der::{
write_context_constructed, write_context_primitive, write_oid, write_sequence,
};
let envelope_version: u64 = 2;
let kari_body = build_kari(
originator,
ukm,
key_encryption_oid,
key_encryption_params,
recipient_keys,
);
let kari_tagged =
super::der::write_tlv(super::der::Class::ContextSpecific, true, 1, &kari_body);
let ri_set = super::der::write_set(&kari_tagged);
let alg_id = {
let mut body = write_oid(content_alg_oid);
match iv {
Some(iv) => body.extend_from_slice(&super::der::write_octet_string(iv)),
None => body.extend_from_slice(&super::der::write_null()),
}
write_sequence(&body)
};
let eci = {
let mut body = write_oid(&OID_DATA);
body.extend_from_slice(&alg_id);
body.extend_from_slice(&write_context_primitive(0, encrypted_content));
write_sequence(&body)
};
let enveloped_body = {
let mut b = super::der::write_integer_u64(envelope_version);
b.extend_from_slice(&ri_set);
b.extend_from_slice(&eci);
b
};
let enveloped = write_sequence(&enveloped_body);
let outer = {
let mut b = write_oid(&OID_ENVELOPED_DATA);
b.extend_from_slice(&write_context_constructed(0, &enveloped));
b
};
write_sequence(&outer)
}
fn build_kari(
originator: &OriginatorIdRef,
ukm: Option<&[u8]>,
key_encryption_oid: &[u64],
key_encryption_params: &[u8],
recipient_keys: &[KariRecipientPlain],
) -> Vec<u8> {
use super::der::{
write_context_constructed, write_context_primitive, write_octet_string, write_oid,
write_sequence,
};
let mut body = super::der::write_integer_u64(3);
let originator_inner = match originator {
OriginatorIdRef::IssuerAndSerial { issuer_der, serial } => {
let serial_int = super::der::write_integer_bytes(serial);
let mut ias_body = Vec::with_capacity(issuer_der.len() + serial_int.len());
ias_body.extend_from_slice(issuer_der);
ias_body.extend_from_slice(&serial_int);
write_sequence(&ias_body)
}
OriginatorIdRef::SubjectKeyIdentifier { ski } => write_context_primitive(0, ski),
OriginatorIdRef::OriginatorKey {
algorithm_oid,
algorithm_params,
public_key,
} => {
let alg = {
let mut b = write_oid(algorithm_oid);
b.extend_from_slice(algorithm_params);
write_sequence(&b)
};
let mut bs = vec![0x00];
bs.extend_from_slice(public_key);
let bit_string = super::der::write_tlv(
super::der::Class::Universal,
false,
3, &bs,
);
let mut opk_body = alg;
opk_body.extend_from_slice(&bit_string);
super::der::write_tlv(super::der::Class::ContextSpecific, true, 1, &opk_body)
}
};
body.extend_from_slice(&write_context_constructed(0, &originator_inner));
if let Some(ukm) = ukm {
body.extend_from_slice(&write_context_constructed(1, &write_octet_string(ukm)));
}
let kea_seq = {
let mut b = write_oid(key_encryption_oid);
b.extend_from_slice(key_encryption_params);
write_sequence(&b)
};
body.extend_from_slice(&kea_seq);
let mut reks = Vec::new();
for r in recipient_keys {
let rid_bytes = match &r.rid {
KariRecipientIdRef::IssuerAndSerial { issuer_der, serial } => {
let serial_int = super::der::write_integer_bytes(serial);
let mut ias_body = Vec::with_capacity(issuer_der.len() + serial_int.len());
ias_body.extend_from_slice(issuer_der);
ias_body.extend_from_slice(&serial_int);
write_sequence(&ias_body)
}
KariRecipientIdRef::RecipientKeyIdentifier { ski, date, other } => {
let mut inner = write_octet_string(ski);
if let Some(date_bytes) = date {
inner.extend_from_slice(&super::der::write_tlv(
super::der::Class::Universal,
false,
24,
date_bytes,
));
}
if let Some((oid_arcs, key_attr)) = other {
let mut oka_body = write_oid(oid_arcs);
oka_body.extend_from_slice(key_attr);
inner.extend_from_slice(&write_sequence(&oka_body));
}
super::der::write_tlv(super::der::Class::ContextSpecific, true, 0, &inner)
}
};
let mut rek_body = rid_bytes;
rek_body.extend_from_slice(&write_octet_string(&r.encrypted_key));
reks.extend_from_slice(&write_sequence(&rek_body));
}
body.extend_from_slice(&write_sequence(&reks));
body
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pubsec::cms::{parse_envelope, RecipientInfoVariant};
#[test]
fn kari_envelope_round_trips_through_parser() {
let originator_pubkey = b"OXIDEAV-ECDH-ORIGINATOR-FAKE-PT!".to_vec();
let ukm = b"OXIDEAV-UKM-1234".to_vec();
let originator = OriginatorIdRef::OriginatorKey {
algorithm_oid: vec![1, 2, 840, 10045, 2, 1],
algorithm_params: super::super::der::write_oid(&[1, 2, 840, 10045, 3, 1, 7]),
public_key: originator_pubkey.clone(),
};
let kea_oid = vec![1u64, 3, 133, 16, 840, 63, 0, 11, 1];
let aes256_wrap_oid = [2u64, 16, 840, 1, 101, 3, 4, 1, 45];
let kea_params =
super::super::der::write_sequence(&{ super::super::der::write_oid(&aes256_wrap_oid) });
let recipient_ski = vec![0xCDu8; 20];
let wrapped_cek = vec![0xDEu8; 40]; let rek = KariRecipientPlain {
rid: KariRecipientIdRef::RecipientKeyIdentifier {
ski: recipient_ski.clone(),
date: None,
other: None,
},
encrypted_key: wrapped_cek.clone(),
};
let plaintext = b"OXIDEAV-KARI-FIXTURE-PLAINTEXT-32";
let envelope = build_envelope_kari_aes256(
&originator,
Some(&ukm),
&kea_oid,
&kea_params,
&[rek],
plaintext,
&[0xAAu8; 32],
&[0xBBu8; 16],
);
let parsed = parse_envelope(&envelope).expect("parse");
assert!(parsed.recipients.is_empty());
assert_eq!(parsed.all_recipients.len(), 1);
match &parsed.all_recipients[0] {
RecipientInfoVariant::KeyAgree(kari) => {
assert_eq!(kari.key_encryption_oid, kea_oid);
assert_eq!(kari.ukm, ukm);
assert_eq!(kari.recipient_encrypted_keys.len(), 1);
match &kari.recipient_encrypted_keys[0].rid {
crate::pubsec::cms::KeyAgreeRecipientId::RecipientKeyIdentifier {
ski,
date,
other,
} => {
assert_eq!(ski, &recipient_ski);
assert!(date.is_none());
assert!(other.is_none());
}
other => panic!("expected RKID got {other:?}"),
}
assert_eq!(kari.recipient_encrypted_keys[0].encrypted_key, wrapped_cek);
match &kari.originator {
crate::pubsec::cms::OriginatorId::OriginatorKey(opk) => {
assert_eq!(opk.public_key, originator_pubkey);
}
other => panic!("expected OriginatorKey got {other:?}"),
}
}
other => panic!("expected KARI got {other:?}"),
}
}
#[test]
fn kari_envelope_with_ias_recipient_and_ski_originator() {
let issuer_der = super::super::der::write_sequence(b"O=KARI Test CA");
let serial = vec![0x99, 0x42];
let originator_ski = vec![0xEEu8; 20];
let originator = OriginatorIdRef::SubjectKeyIdentifier {
ski: originator_ski.clone(),
};
let kea_oid = vec![1u64, 3, 132, 1, 11, 3]; let rek = KariRecipientPlain {
rid: KariRecipientIdRef::IssuerAndSerial {
issuer_der: issuer_der.clone(),
serial: serial.clone(),
},
encrypted_key: vec![0xFFu8; 24],
};
let envelope = build_envelope_kari_aes256(
&originator,
None, &kea_oid,
&[],
&[rek],
b"plaintext-bytes-padding-",
&[0x33u8; 32],
&[0x44u8; 16],
);
let parsed = parse_envelope(&envelope).expect("parse");
match &parsed.all_recipients[0] {
RecipientInfoVariant::KeyAgree(kari) => {
assert_eq!(kari.ukm, Vec::<u8>::new());
match &kari.originator {
crate::pubsec::cms::OriginatorId::SubjectKeyIdentifier(b) => {
assert_eq!(b, &originator_ski);
}
_ => panic!("expected SKI originator"),
}
match &kari.recipient_encrypted_keys[0].rid {
crate::pubsec::cms::KeyAgreeRecipientId::IssuerAndSerial(ias) => {
assert_eq!(ias.issuer_der, issuer_der);
assert_eq!(ias.serial, serial);
}
_ => panic!("expected IAS recipient"),
}
}
_ => panic!("expected KARI"),
}
}
}