Skip to main content

_synta/certificate/cms/
mod.rs

1//! Python bindings for CMS content types (RFC 5652 / RFC 9629).
2//!
3//! Split into focused sub-files:
4//! - `kem`       — `KEMRecipientInfo` + `CMSORIforKEMOtherInfo` (RFC 9629)
5//! - `container` — `ContentInfo` + `IssuerAndSerialNumber`
6//! - `signed`    — `SignedData` + `SignerInfo`
7//! - `enveloped` — `EnvelopedData` + `EncryptedData`
8//! - `digest`    — `DigestedData` + `AuthenticatedData`
9//! - `builder`   — `EnvelopedDataBuilder`
10
11mod builder;
12mod container;
13mod digest;
14mod enveloped;
15pub(super) mod kem;
16mod signed;
17
18use builder::PyEnvelopedDataBuilder;
19use container::{PyContentInfo, PyIssuerAndSerialNumber};
20use digest::{PyAuthenticatedData, PyDigestedData};
21use enveloped::{PyEncryptedData, PyEnvelopedData};
22use kem::{PyCMSORIforKEMOtherInfo, PyKEMRecipientInfo};
23use signed::{PySignedData, PySignerInfo};
24
25use pyo3::prelude::*;
26use pyo3::types::PyBytes;
27
28use synta::traits::Encode;
29use synta::Encoding;
30
31/// Re-encode an ASN.1 `Element` to DER bytes, returning `None` for a missing
32/// optional field.  Used by the algorithm-parameter getters across all CMS types.
33fn encode_element_opt<'py>(
34    py: Python<'py>,
35    elem: Option<&synta::Element<'_>>,
36) -> PyResult<Option<Bound<'py, PyBytes>>> {
37    match elem {
38        None => Ok(None),
39        Some(e) => {
40            let mut encoder = synta::Encoder::new(Encoding::Der);
41            e.encode(&mut encoder)
42                .map_err(|err| pyo3::exceptions::PyValueError::new_err(format!("{err}")))?;
43            let bytes = encoder
44                .finish()
45                .map_err(|err| pyo3::exceptions::PyValueError::new_err(format!("{err}")))?;
46            Ok(Some(PyBytes::new(py, &bytes)))
47        }
48    }
49}
50
51/// Exposes CMS (RFC 5652) content types, CMS-KEM (RFC 9629) types, and OID
52/// constants.  Installs the module into ``sys.modules`` so that
53/// ``from synta.cms import ContentInfo`` works.
54pub(super) fn register_cms_submodule(parent: &Bound<'_, PyModule>) -> PyResult<()> {
55    let py = parent.py();
56    let m = PyModule::new(py, "cms")?;
57
58    m.add_class::<PyContentInfo>()?;
59    m.add_class::<PySignedData>()?;
60    m.add_class::<PySignerInfo>()?;
61    m.add_class::<PyEnvelopedData>()?;
62    m.add_class::<PyEnvelopedDataBuilder>()?;
63    m.add_class::<PyEncryptedData>()?;
64    m.add_class::<PyDigestedData>()?;
65    m.add_class::<PyAuthenticatedData>()?;
66    m.add_class::<PyIssuerAndSerialNumber>()?;
67    m.add_class::<PyKEMRecipientInfo>()?;
68    m.add_class::<PyCMSORIforKEMOtherInfo>()?;
69
70    // Content-type OIDs (RFC 5652 §14)
71    m.add(
72        "ID_DATA",
73        super::oid_const(py, synta_certificate::pkcs7_types::ID_DATA),
74    )?;
75    m.add(
76        "ID_SIGNED_DATA",
77        super::oid_const(py, synta_certificate::pkcs7_types::ID_SIGNED_DATA),
78    )?;
79    m.add(
80        "ID_ENVELOPED_DATA",
81        super::oid_const(py, synta_certificate::pkcs7_types::ID_ENVELOPED_DATA),
82    )?;
83    m.add(
84        "ID_ENCRYPTED_DATA",
85        super::oid_const(py, synta_certificate::pkcs7_types::ID_ENCRYPTED_DATA),
86    )?;
87    m.add(
88        "ID_DIGESTED_DATA",
89        super::oid_const(py, synta_certificate::oids::CMS_DIGESTED_DATA),
90    )?;
91    m.add(
92        "ID_CT_AUTH_DATA",
93        super::oid_const(py, synta_certificate::oids::CMS_AUTH_DATA),
94    )?;
95    // OtherRecipientInfo OIDs (RFC 9629 §6.2)
96    m.add(
97        "ID_ORI",
98        super::oid_const(py, synta_certificate::cms_kem_types::ID_ORI),
99    )?;
100    m.add(
101        "ID_ORI_KEM",
102        super::oid_const(py, synta_certificate::cms_kem_types::ID_ORI_KEM),
103    )?;
104    // Content-encryption algorithm OIDs (NIST AES, RFC 3565)
105    m.add(
106        "ID_AES128_CBC",
107        super::oid_const(py, synta_certificate::pkcs12_types::ID_AES128_CBC),
108    )?;
109    m.add(
110        "ID_AES192_CBC",
111        super::oid_const(py, synta_certificate::pkcs12_types::ID_AES192_CBC),
112    )?;
113    m.add(
114        "ID_AES256_CBC",
115        super::oid_const(py, synta_certificate::pkcs12_types::ID_AES256_CBC),
116    )?;
117    // Key-transport algorithm OIDs (RFC 8017)
118    m.add(
119        "ID_RSAES_OAEP",
120        super::oid_const(py, synta_certificate::oids::RSAES_OAEP),
121    )?;
122    m.add(
123        "ID_RSA_ENCRYPTION",
124        super::oid_const(py, synta_certificate::oids::RSA_ENCRYPTION),
125    )?;
126
127    crate::install_submodule(
128        parent,
129        &m,
130        "synta.cms",
131        Some(
132            "synta.cms — CMS (RFC 5652) and CMS-KEM (RFC 9629) types.\n\
133             \n\
134             Provides ContentInfo, SignedData, SignerInfo, EnvelopedData,\n\
135             EnvelopedDataBuilder, EncryptedData, DigestedData, AuthenticatedData,\n\
136             IssuerAndSerialNumber, KEMRecipientInfo, CMSORIforKEMOtherInfo,\n\
137             along with content-type and OtherRecipientInfo OID constants.",
138        ),
139    )
140}