Skip to content

Commit

Permalink
Add support for KEMRecipientInfo as defined in RFC9629 (#1485)
Browse files Browse the repository at this point in the history
  • Loading branch information
carl-wallace authored Aug 21, 2024
1 parent 8b96039 commit e0c3e08
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 0 deletions.
65 changes: 65 additions & 0 deletions cms/src/kemri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! KEMRecipientInfo-related types

use crate::{
content_info::CmsVersion,
enveloped_data::{EncryptedKey, RecipientIdentifier, UserKeyingMaterial},
};
use const_oid::ObjectIdentifier;
use der::{asn1::OctetString, Sequence};
use spki::AlgorithmIdentifierOwned;

/// From [RFC9629 Section 3]
/// ```text
/// id-ori OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
/// rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) 13 }
///
/// id-ori-kem OBJECT IDENTIFIER ::= { id-ori 3 }
/// ```
/// [RFC9629 Section 3]: https://datatracker.ietf.org/doc/html/rfc9629#section-3
pub const ID_ORI_KEM: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.16.13.3");

/// The `KEMRecipientInfo` type is defined in [RFC9629 Section 3]
/// ```text
/// KEMRecipientInfo ::= SEQUENCE {
/// version CMSVersion, -- always set to 0
/// rid RecipientIdentifier,
/// kem KEMAlgorithmIdentifier,
/// kemct OCTET STRING,
/// kdf KeyDerivationAlgorithmIdentifier,
/// kekLength INTEGER (1..65535),
/// ukm [0] EXPLICIT UserKeyingMaterial OPTIONAL,
/// wrap KeyEncryptionAlgorithmIdentifier,
/// encryptedKey EncryptedKey }
/// ```
/// [RFC9629 Section 3]: https://datatracker.ietf.org/doc/html/rfc9629#section-3
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
#[allow(missing_docs)]
pub struct KemRecipientInfo {
pub version: CmsVersion,
pub rid: RecipientIdentifier,
pub kem: AlgorithmIdentifierOwned,
pub kem_ct: OctetString,
pub kdf: AlgorithmIdentifierOwned,
pub kek_length: u16,
#[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")]
pub ukm: Option<UserKeyingMaterial>,
pub wrap: AlgorithmIdentifierOwned,
pub encrypted_key: EncryptedKey,
}

/// The `CMSORIforKEMOtherInfo` type is defined in [RFC9629 Section 5]
/// ```text
/// CMSORIforKEMOtherInfo ::= SEQUENCE {
/// wrap KeyEncryptionAlgorithmIdentifier,
/// kekLength INTEGER (1..65535),
/// ukm [0] EXPLICIT UserKeyingMaterial OPTIONAL }
/// ```
/// [RFC9629 Section 5]: https://datatracker.ietf.org/doc/html/rfc9629#section-5
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
#[allow(missing_docs)]
pub struct CmsOriForKemOtherInfo {
pub wrap: AlgorithmIdentifierOwned,
pub kek_length: u16,
#[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")]
pub ukm: Option<UserKeyingMaterial>,
}
1 change: 1 addition & 0 deletions cms/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub mod content_info;
pub mod digested_data;
pub mod encrypted_data;
pub mod enveloped_data;
pub mod kemri;
pub mod revocation;
pub mod signed_data;
pub mod timestamped_data;
Binary file not shown.
Binary file not shown.
Binary file not shown.
91 changes: 91 additions & 0 deletions cms/tests/kemri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use cms::authenveloped_data::AuthEnvelopedData;
use cms::content_info::{CmsVersion, ContentInfo};
use cms::enveloped_data::{EnvelopedData, RecipientIdentifier, RecipientInfo};
use cms::kemri::ID_ORI_KEM;
use const_oid::ObjectIdentifier;
use der::{Decode, Encode};
use hex_literal::hex;

#[test]
fn kemri_auth_enveloped_data() {
let data = include_bytes!("examples/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_kemri_auth.der");
let ci = ContentInfo::from_der(data).unwrap();
let aed = AuthEnvelopedData::from_der(&ci.content.to_der().unwrap()).unwrap();
for ri in aed.recip_infos.0.iter() {
if let RecipientInfo::Ori(ori) = ri {
let ori_value = ori.ori_value.to_der().unwrap();
assert_eq!(ori.ori_type, ID_ORI_KEM);
let kemri = cms::kemri::KemRecipientInfo::from_der(&ori_value).unwrap();
let reenc = kemri.to_der().unwrap();
assert_eq!(reenc, ori_value);
} else {
panic!("Unexpected recipient info type");
}
}
}

#[test]
fn kemri_enveloped_data() {
let data = include_bytes!(
"examples/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_kemri_id-alg-hkdf-with-sha256.der"
);
let ci = ContentInfo::from_der(data).unwrap();
let ed = EnvelopedData::from_der(&ci.content.to_der().unwrap()).unwrap();
for ri in ed.recip_infos.0.iter() {
if let RecipientInfo::Ori(ori) = ri {
let ori_value = ori.ori_value.to_der().unwrap();
assert_eq!(ori.ori_type, ID_ORI_KEM);
let kemri = cms::kemri::KemRecipientInfo::from_der(&ori_value).unwrap();
let reenc = kemri.to_der().unwrap();
assert_eq!(reenc, ori_value);
} else {
panic!("Unexpected recipient info type");
}
}
}

#[test]
fn kemri_enveloped_data_ukm() {
let data = include_bytes!("examples/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_kemri_ukm.der");
let ci = ContentInfo::from_der(data).unwrap();
let ed = EnvelopedData::from_der(&ci.content.to_der().unwrap()).unwrap();
for ri in ed.recip_infos.0.iter() {
if let RecipientInfo::Ori(ori) = ri {
let ori_value = ori.ori_value.to_der().unwrap();
assert_eq!(ori.ori_type, ID_ORI_KEM);
let kemri = cms::kemri::KemRecipientInfo::from_der(&ori_value).unwrap();

assert_eq!(kemri.version, CmsVersion::V0);

if let RecipientIdentifier::SubjectKeyIdentifier(skid) = &kemri.rid {
let rid = hex!("B17B1D588029CA33201230C50CE75F57E6AAC916");
assert_eq!(skid.0.as_bytes(), rid);
} else {
panic!("Unexpected recipient identifier type");
}

pub const ML_KEM_512_IPD: ObjectIdentifier =
ObjectIdentifier::new_unwrap("1.3.6.1.4.1.22554.5.6.1");
assert_eq!(kemri.kem.oid, ML_KEM_512_IPD);

pub const ID_KMAC128: ObjectIdentifier =
ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.21");
assert_eq!(kemri.kdf.oid, ID_KMAC128);

assert_eq!(
kemri.ukm.clone().unwrap().as_bytes(),
"This is some User Keying Material\n".as_bytes()
);

let enc_key = hex!(
"B9F524FB59CAD7420127B1FEA61D5F15F5BED04078AB7A3BEDD501B6B215D6AA417BDA0303C78A6C"
);
assert_eq!(kemri.encrypted_key.as_bytes(), enc_key);

let reenc = kemri.to_der().unwrap();
assert_eq!(reenc, ori_value);
} else {
panic!("Unexpected recipient info type");
}
}
}

0 comments on commit e0c3e08

Please sign in to comment.