1use aes::cipher::block_padding::Pkcs7;
31use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
32use cms::content_info::ContentInfo;
33use cms::enveloped_data::{
34 EncryptedContentInfo, EnvelopedData, KeyTransRecipientInfo, RecipientIdentifier, RecipientInfo,
35 RecipientInfos,
36};
37use der::asn1::{ObjectIdentifier, OctetString, SetOfVec};
38use der::{Any, Decode, Encode};
39use rsa::{Oaep, RsaPrivateKey, RsaPublicKey};
40use spki::AlgorithmIdentifierOwned;
41
42type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
43type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
44
45const ID_ENVELOPED_DATA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.7.3");
47const ID_DATA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.7.1");
48const RSAES_OAEP: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.7");
49const AES_256_CBC: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.42");
50
51#[derive(Debug, thiserror::Error)]
53pub enum RecipientError {
54 #[error("DER encode/decode: {0}")]
55 Der(String),
56 #[error("RSA: {0}")]
57 Rsa(String),
58 #[error("AES-CBC: {0}")]
59 Aes(String),
60 #[error("malformed envelope: {0}")]
61 Malformed(String),
62}
63
64pub fn encode(recipient: &RsaPublicKey, plaintext: &[u8]) -> Result<Vec<u8>, RecipientError> {
68 let mut rng = rand::rngs::OsRng;
69
70 let mut cek = [0u8; 32];
72 let mut iv = [0u8; 16];
73 {
74 use rand::RngCore;
75 rng.fill_bytes(&mut cek);
76 rng.fill_bytes(&mut iv);
77 }
78
79 let encrypted_content = Aes256CbcEnc::new(&cek.into(), &iv.into())
81 .encrypt_padded_vec_mut::<Pkcs7>(plaintext);
82
83 let encrypted_key = recipient
85 .encrypt(&mut rng, Oaep::new::<sha2::Sha256>(), &cek)
86 .map_err(|e| RecipientError::Rsa(e.to_string()))?;
87
88 let ktri = KeyTransRecipientInfo {
92 version: cms::content_info::CmsVersion::V0,
93 rid: RecipientIdentifier::SubjectKeyIdentifier(x509_cert::ext::pkix::SubjectKeyIdentifier(
94 OctetString::new(Vec::new()).map_err(|e| RecipientError::Der(e.to_string()))?,
95 )),
96 key_enc_alg: AlgorithmIdentifierOwned {
97 oid: RSAES_OAEP,
98 parameters: None,
101 },
102 enc_key: OctetString::new(encrypted_key).map_err(|e| RecipientError::Der(e.to_string()))?,
103 };
104
105 let mut recip_infos = SetOfVec::new();
106 recip_infos
107 .insert(RecipientInfo::Ktri(ktri))
108 .map_err(|e| RecipientError::Der(e.to_string()))?;
109
110 let enc_content_info = EncryptedContentInfo {
111 content_type: ID_DATA,
112 content_enc_alg: AlgorithmIdentifierOwned {
113 oid: AES_256_CBC,
114 parameters: Some(
115 Any::new(der::Tag::OctetString, iv.as_slice())
116 .map_err(|e| RecipientError::Der(e.to_string()))?,
117 ),
118 },
119 encrypted_content: Some(
120 OctetString::new(encrypted_content).map_err(|e| RecipientError::Der(e.to_string()))?,
121 ),
122 };
123
124 let enveloped = EnvelopedData {
125 version: cms::content_info::CmsVersion::V0,
126 originator_info: None,
127 recip_infos: RecipientInfos(recip_infos),
128 encrypted_content: enc_content_info,
129 unprotected_attrs: None,
130 };
131
132 let content_info = ContentInfo {
133 content_type: ID_ENVELOPED_DATA,
134 content: Any::encode_from(&enveloped).map_err(|e| RecipientError::Der(e.to_string()))?,
135 };
136 content_info
137 .to_der()
138 .map_err(|e| RecipientError::Der(e.to_string()))
139}
140
141fn ber_err(m: impl std::fmt::Display) -> RecipientError {
142 RecipientError::Malformed(format!("BER->DER transcode: {m}"))
143}
144
145fn ber_to_der(input: &[u8]) -> Result<Vec<u8>, RecipientError> {
155 let (out, rest) = transcode_tlv(input)?;
156 if !rest.is_empty() {
157 return Err(ber_err(format!("{} trailing bytes", rest.len())));
158 }
159 Ok(out)
160}
161
162fn transcode_tlv(input: &[u8]) -> Result<(Vec<u8>, &[u8]), RecipientError> {
164 let id = *input.first().ok_or_else(|| ber_err("unexpected end of input"))?;
165 if id & 0x1f == 0x1f {
166 return Err(ber_err("high-tag-number form unsupported"));
167 }
168 let constructed = id & 0x20 != 0;
169 let (len, after_len) = read_len(&input[1..])?;
170
171 if !constructed {
172 let len = len.ok_or_else(|| ber_err("indefinite length on primitive"))?;
173 if after_len.len() < len {
174 return Err(ber_err("truncated primitive content"));
175 }
176 return Ok((emit_der(id, &after_len[..len]), &after_len[len..]));
177 }
178
179 let mut children: Vec<Vec<u8>> = Vec::new();
193 let after: &[u8];
194 match len {
195 Some(len) => {
196 if after_len.len() < len {
197 return Err(ber_err("truncated constructed content"));
198 }
199 let mut region = &after_len[..len];
200 after = &after_len[len..];
201 while !region.is_empty() {
202 let (child, rest) = transcode_tlv(region)?;
203 children.push(child);
204 region = rest;
205 }
206 }
207 None => {
208 let mut region = after_len;
210 loop {
211 if region.len() >= 2 && region[0] == 0x00 && region[1] == 0x00 {
212 region = ®ion[2..];
213 break;
214 }
215 let (child, rest) = transcode_tlv(region)?;
216 children.push(child);
217 region = rest;
218 }
219 after = region;
220 }
221 }
222
223 let is_universal_octet = id == 0x24;
224 let is_context = id & 0xc0 == 0x80; let context_octet = is_context
226 && !children.is_empty()
227 && children.iter().all(|c| c.first() == Some(&0x04));
228 if is_universal_octet || context_octet {
229 let mut body = Vec::new();
230 for child in &children {
231 body.extend_from_slice(&octet_value(child)?);
232 }
233 let out_id = if is_universal_octet { 0x04 } else { id & !0x20 };
234 return Ok((emit_der(out_id, &body), after));
235 }
236
237 let mut body = Vec::new();
238 for child in &children {
239 body.extend_from_slice(child);
240 }
241 Ok((emit_der(id, &body), after))
242}
243
244fn read_len(input: &[u8]) -> Result<(Option<usize>, &[u8]), RecipientError> {
246 let b = *input.first().ok_or_else(|| ber_err("truncated length"))?;
247 let rest = &input[1..];
248 if b == 0x80 {
249 return Ok((None, rest));
250 }
251 if b & 0x80 == 0 {
252 return Ok((Some(b as usize), rest));
253 }
254 let n = (b & 0x7f) as usize;
255 if n == 0 || n > 4 || rest.len() < n {
256 return Err(ber_err("bad long-form length"));
257 }
258 let mut len = 0usize;
259 for &x in &rest[..n] {
260 len = (len << 8) | x as usize;
261 }
262 Ok((Some(len), &rest[n..]))
263}
264
265fn emit_der(id: u8, content: &[u8]) -> Vec<u8> {
267 let mut out = vec![id];
268 let len = content.len();
269 if len < 0x80 {
270 out.push(len as u8);
271 } else {
272 let be = len.to_be_bytes();
273 let start = be.iter().position(|&b| b != 0).unwrap_or(be.len() - 1);
274 let lb = &be[start..];
275 out.push(0x80 | lb.len() as u8);
276 out.extend_from_slice(lb);
277 }
278 out.extend_from_slice(content);
279 out
280}
281
282fn octet_value(der: &[u8]) -> Result<Vec<u8>, RecipientError> {
284 if der.first() != Some(&0x04) {
285 return Err(ber_err("constructed OCTET STRING child is not an OCTET STRING"));
286 }
287 let (len, content) = read_len(&der[1..])?;
288 let len = len.ok_or_else(|| ber_err("indefinite OCTET STRING child"))?;
289 content
290 .get(..len)
291 .map(|s| s.to_vec())
292 .ok_or_else(|| ber_err("truncated OCTET STRING child"))
293}
294
295pub fn decode(ephemeral: &RsaPrivateKey, cms_der: &[u8]) -> Result<Vec<u8>, RecipientError> {
298 let der = ber_to_der(cms_der)?;
304 let content_info =
305 ContentInfo::from_der(&der).map_err(|e| RecipientError::Der(e.to_string()))?;
306 if content_info.content_type != ID_ENVELOPED_DATA {
307 return Err(RecipientError::Malformed(format!(
308 "content type {} is not id-envelopedData",
309 content_info.content_type
310 )));
311 }
312 let enveloped: EnvelopedData = content_info
313 .content
314 .decode_as()
315 .map_err(|e| RecipientError::Der(e.to_string()))?;
316
317 let ktri = enveloped
319 .recip_infos
320 .0
321 .as_slice()
322 .iter()
323 .find_map(|ri| match ri {
324 RecipientInfo::Ktri(k) => Some(k),
325 _ => None,
326 })
327 .ok_or_else(|| RecipientError::Malformed("no KeyTransRecipientInfo".into()))?;
328 if ktri.key_enc_alg.oid != RSAES_OAEP {
329 return Err(RecipientError::Malformed(format!(
330 "key encryption alg {} is not RSAES-OAEP",
331 ktri.key_enc_alg.oid
332 )));
333 }
334
335 let cek = ephemeral
337 .decrypt(Oaep::new::<sha2::Sha256>(), ktri.enc_key.as_bytes())
338 .map_err(|e| RecipientError::Rsa(e.to_string()))?;
339 if cek.len() != 32 {
340 return Err(RecipientError::Malformed(format!(
341 "content key is {} bytes, expected 32",
342 cek.len()
343 )));
344 }
345
346 let eci = &enveloped.encrypted_content;
347 if eci.content_enc_alg.oid != AES_256_CBC {
348 return Err(RecipientError::Malformed(format!(
349 "content encryption alg {} is not AES-256-CBC",
350 eci.content_enc_alg.oid
351 )));
352 }
353 let iv_any = eci
355 .content_enc_alg
356 .parameters
357 .as_ref()
358 .ok_or_else(|| RecipientError::Malformed("AES-CBC algorithm has no IV parameter".into()))?;
359 let iv = iv_any
360 .decode_as::<OctetString>()
361 .map_err(|e| RecipientError::Der(e.to_string()))?;
362 let iv = iv.as_bytes();
363 if iv.len() != 16 {
364 return Err(RecipientError::Malformed(format!(
365 "IV is {} bytes, expected 16",
366 iv.len()
367 )));
368 }
369
370 let ciphertext = eci
371 .encrypted_content
372 .as_ref()
373 .ok_or_else(|| RecipientError::Malformed("no encrypted content".into()))?
374 .as_bytes();
375
376 let plaintext = Aes256CbcDec::new(cek.as_slice().into(), iv.into())
377 .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
378 .map_err(|e| RecipientError::Aes(e.to_string()))?;
379 Ok(plaintext)
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use rsa::pkcs8::{DecodePrivateKey, EncodePrivateKey};
386
387 fn ephemeral_key() -> RsaPrivateKey {
388 RsaPrivateKey::new(&mut rand::rngs::OsRng, 2048).expect("keygen")
390 }
391
392 #[test]
393 fn round_trip_recovers_plaintext() {
394 let priv_key = ephemeral_key();
395 let pub_key = RsaPublicKey::from(&priv_key);
396 let secret = b"a 32-byte LUKS passphrase-here!!"; assert_eq!(secret.len(), 32);
398
399 let envelope = encode(&pub_key, secret).expect("encode");
400 assert!(envelope.len() > secret.len() + 200);
402 assert_ne!(&envelope, secret);
403
404 let recovered = decode(&priv_key, &envelope).expect("decode");
405 assert_eq!(recovered.as_slice(), secret.as_slice());
406 }
407
408 #[test]
409 fn ber_to_der_normalises_indefinite_length_and_constructed_octet_strings() {
410 let der = [0x30, 0x03, 0x02, 0x01, 0x01];
412 assert_eq!(ber_to_der(&der).unwrap(), der);
413
414 let ber_indef = [0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00];
416 assert_eq!(ber_to_der(&ber_indef).unwrap(), der);
417
418 let want = [0x04, 0x04, 0x61, 0x62, 0x63, 0x64];
421 let ber_octet_indef = [
422 0x24, 0x80, 0x04, 0x02, 0x61, 0x62, 0x04, 0x02, 0x63, 0x64, 0x00, 0x00,
423 ];
424 assert_eq!(ber_to_der(&ber_octet_indef).unwrap(), want);
425
426 let ber_octet_def = [0x24, 0x08, 0x04, 0x02, 0x61, 0x62, 0x04, 0x02, 0x63, 0x64];
428 assert_eq!(ber_to_der(&ber_octet_def).unwrap(), want);
429
430 let ber_nested = [0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00];
432 let der_nested = [0x30, 0x05, 0x30, 0x03, 0x02, 0x01, 0x01];
433 assert_eq!(ber_to_der(&ber_nested).unwrap(), der_nested);
434
435 let want_ctx = [0x80, 0x04, 0x61, 0x62, 0x63, 0x64];
440 let ber_ctx_indef = [
441 0xA0, 0x80, 0x04, 0x02, 0x61, 0x62, 0x04, 0x02, 0x63, 0x64, 0x00, 0x00,
442 ];
443 assert_eq!(ber_to_der(&ber_ctx_indef).unwrap(), want_ctx);
444 let ber_ctx_def = [0xA0, 0x08, 0x04, 0x02, 0x61, 0x62, 0x04, 0x02, 0x63, 0x64];
446 assert_eq!(ber_to_der(&ber_ctx_def).unwrap(), want_ctx);
447
448 let ber_explicit = [0xA0, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00];
451 let der_explicit = [0xA0, 0x05, 0x30, 0x03, 0x02, 0x01, 0x01];
452 assert_eq!(ber_to_der(&ber_explicit).unwrap(), der_explicit);
453
454 let priv_key = ephemeral_key();
456 let pub_key = RsaPublicKey::from(&priv_key);
457 let env = encode(&pub_key, b"a 32-byte LUKS passphrase-here!!").expect("encode");
458 assert_eq!(ber_to_der(&env).unwrap(), env);
459 }
460
461 fn hex(s: &str) -> Vec<u8> {
462 let s: Vec<u8> = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
463 s.chunks(2)
464 .map(|c| u8::from_str_radix(std::str::from_utf8(c).unwrap(), 16).unwrap())
465 .collect()
466 }
467
468 #[test]
469 fn real_kms_envelope_transcodes_and_parses() {
470 let envelope = hex(concat!(
479 "308006092a864886f70d010703a08030800201023182016b30820167020102802000d8032ce4",
480 "b06d81bf38c4fc3ff1200706deb6678591c9b7ad900825840fa7eb303c06092a864886f70d01",
481 "0107302fa00f300d06096086480165030402010500a11c301a06092a864886f70d010108300d",
482 "06096086480165030402010500048201008c0955736cd2c8f2b5e39bd13c1d3d0b8d0b6c3b16",
483 "d29ba3e02bd072d7a54ea43d1b736a609654e3642de874a54f75f78c4abc1f131d87a4e92f36",
484 "c6b54c325371bbbd47ea1b3c141ca7c3c6f68d075469a23ee0e5e780783f858e8db11a019d25",
485 "0c81a3ec03d9171e86e6ecf3189aaa653e22e09cab0869bd08e9d5c007069d38da2a73c3c481",
486 "f19e9b33bcc3dee46fc8ccbbba297fc7a8fc1257875342ba9694173c43ae60602429fe81292b",
487 "d9faf46a54c5cf0ba83a7dcfa71f218c27c088d913b9f709c039348e46ea669e33dc6f0d0224",
488 "dcd52874774db1c0e8a5574f7a397500aaef0ac23395597b372c959f336297eb93734b3f951e",
489 "5b062adba12320308006092a864886f70d010701301d060960864801650304012a04104df038",
490 "d3456a8cba065420e8a0ef0278a0800430df06bc5b3e8ba95b06220d48057835b6bc42e1fa30",
491 "8530422b618a9839f12a0e27f6b1a667f14c7255d6f84d88e2300e00000000000000000000",
492 ));
493
494 let der = ber_to_der(&envelope).expect("transcode real KMS envelope");
495 assert_eq!(ber_to_der(&der).unwrap(), der);
497
498 let ci = ContentInfo::from_der(&der).expect("parse transcoded ContentInfo");
499 assert_eq!(ci.content_type, ID_ENVELOPED_DATA);
500 let ed: EnvelopedData = ci.content.decode_as().expect("decode EnvelopedData");
501
502 let ktri = ed
503 .recip_infos
504 .0
505 .as_slice()
506 .iter()
507 .find_map(|ri| match ri {
508 RecipientInfo::Ktri(k) => Some(k),
509 _ => None,
510 })
511 .expect("a KeyTransRecipientInfo");
512 assert_eq!(ktri.key_enc_alg.oid, RSAES_OAEP);
513 assert_eq!(ktri.enc_key.as_bytes().len(), 256);
515
516 let eci = &ed.encrypted_content;
517 assert_eq!(eci.content_enc_alg.oid, AES_256_CBC);
518 let iv = eci
519 .content_enc_alg
520 .parameters
521 .as_ref()
522 .unwrap()
523 .decode_as::<OctetString>()
524 .unwrap();
525 assert_eq!(iv.as_bytes().len(), 16);
526 assert!(eci.encrypted_content.as_ref().unwrap().as_bytes().len() >= 16);
528 }
529
530 #[test]
531 fn wrong_key_fails_to_open() {
532 let pub_key = RsaPublicKey::from(&ephemeral_key());
533 let envelope = encode(&pub_key, b"0123456789abcdef0123456789abcdef").expect("encode");
534 let other = ephemeral_key();
536 assert!(decode(&other, &envelope).is_err());
537 }
538
539 #[test]
540 fn key_serialisation_round_trips_through_pkcs8() {
541 let priv_key = ephemeral_key();
545 let der = priv_key.to_pkcs8_der().unwrap();
546 let restored = RsaPrivateKey::from_pkcs8_der(der.as_bytes()).unwrap();
547 let pub_key = RsaPublicKey::from(&priv_key);
548 let env = encode(&pub_key, b"0123456789abcdef0123456789abcdef").unwrap();
549 assert_eq!(
550 decode(&restored, &env).unwrap().as_slice(),
551 b"0123456789abcdef0123456789abcdef".as_slice()
552 );
553 }
554}