Skip to main content

_synta/certificate/
logotype.rs

1//! Python bindings for the RFC 9399 Logotype certificate extension builder.
2//!
3//! Exposes [`PyLogotypeExtnBuilder`] for constructing DER-encoded
4//! ``LogotypeExtn`` structures (OID ``1.3.6.1.5.5.7.1.12``).
5
6use pyo3::prelude::*;
7use pyo3::types::PyBytes;
8
9// ── PyLogotypeExtnBuilder ─────────────────────────────────────────────────────
10
11/// Python-facing wrapper for [`synta_certificate::LogotypeExtnBuilder`].
12///
13/// Builds a DER-encoded ``LogotypeExtn`` SEQUENCE (RFC 9399).
14///
15/// The extension value goes inside the OCTET STRING wrapper of an X.509
16/// ``Extension`` SEQUENCE for OID ``1.3.6.1.5.5.7.1.12``.
17///
18/// At least one of ``communityLogos``, ``issuerLogo``, ``subjectLogo``, or
19/// ``otherLogos`` must be set before calling :meth:`build`.
20///
21/// Each logo is described by four parameters passed as keyword arguments or
22/// positional values to the setter methods:
23///
24/// - ``media_type`` — MIME type string, e.g. ``"image/png"``
25/// - ``hash_alg_oid`` — hash algorithm OID as a list of arc integers,
26///   e.g. ``list(synta.oids.SHA256.components())``
27/// - ``hash_value`` — raw hash bytes of the logotype object
28/// - ``uri`` — URI from which the logotype can be retrieved
29///
30/// Example::
31///
32///     import hashlib, synta
33///
34///     img_hash = hashlib.sha256(open("logo.png", "rb").read()).digest()
35///     sha256_comps = list(synta.oids.SHA256.components())
36///
37///     extn_der = (
38///         synta.LogotypeExtnBuilder()
39///         .subject_logo_direct(
40///             media_type="image/png",
41///             hash_alg_oid=sha256_comps,
42///             hash_value=img_hash,
43///             uri="https://www.example.com/logo.png",
44///         )
45///         .build()
46///     )
47#[pyclass(name = "LogotypeExtnBuilder")]
48pub struct PyLogotypeExtnBuilder {
49    inner: synta_certificate::LogotypeExtnBuilder,
50    built: bool,
51}
52
53#[pymethods]
54impl PyLogotypeExtnBuilder {
55    /// Create a new, empty ``LogotypeExtnBuilder``.
56    #[new]
57    fn new() -> Self {
58        Self {
59            inner: synta_certificate::LogotypeExtnBuilder::new(),
60            built: false,
61        }
62    }
63
64    /// Set the ``issuerLogo`` to a direct ``LogotypeData`` with one image.
65    ///
66    /// :param media_type: MIME type string (IA5String), e.g. ``"image/png"``.
67    /// :param hash_alg_oid: hash algorithm OID arc components as a list of
68    ///     ints, e.g. ``list(synta.oids.SHA256.components())``.
69    /// :param hash_value: raw hash bytes of the logotype object.
70    /// :param uri: URI from which the logotype can be retrieved (IA5String).
71    /// :raises ValueError: if any value is invalid (deferred to :meth:`build`).
72    #[pyo3(signature = (media_type, hash_alg_oid, hash_value, uri))]
73    fn issuer_logo_direct<'py>(
74        slf: Bound<'py, Self>,
75        media_type: &str,
76        hash_alg_oid: Vec<u32>,
77        hash_value: &[u8],
78        uri: &str,
79    ) -> Bound<'py, Self> {
80        let spec = synta_certificate::LogotypeDetailsSpec {
81            media_type,
82            hash_alg_oid: &hash_alg_oid,
83            hash_value,
84            uri,
85        };
86        {
87            let mut guard = slf.borrow_mut();
88            let old = std::mem::replace(
89                &mut guard.inner,
90                synta_certificate::LogotypeExtnBuilder::new(),
91            );
92            guard.inner = old.issuer_logo_direct(spec);
93        }
94        slf
95    }
96
97    /// Set the ``subjectLogo`` to a direct ``LogotypeData`` with one image.
98    ///
99    /// :param media_type: MIME type string (IA5String), e.g. ``"image/png"``.
100    /// :param hash_alg_oid: hash algorithm OID arc components as a list of
101    ///     ints, e.g. ``list(synta.oids.SHA256.components())``.
102    /// :param hash_value: raw hash bytes of the logotype object.
103    /// :param uri: URI from which the logotype can be retrieved (IA5String).
104    /// :raises ValueError: if any value is invalid (deferred to :meth:`build`).
105    #[pyo3(signature = (media_type, hash_alg_oid, hash_value, uri))]
106    fn subject_logo_direct<'py>(
107        slf: Bound<'py, Self>,
108        media_type: &str,
109        hash_alg_oid: Vec<u32>,
110        hash_value: &[u8],
111        uri: &str,
112    ) -> Bound<'py, Self> {
113        let spec = synta_certificate::LogotypeDetailsSpec {
114            media_type,
115            hash_alg_oid: &hash_alg_oid,
116            hash_value,
117            uri,
118        };
119        {
120            let mut guard = slf.borrow_mut();
121            let old = std::mem::replace(
122                &mut guard.inner,
123                synta_certificate::LogotypeExtnBuilder::new(),
124            );
125            guard.inner = old.subject_logo_direct(spec);
126        }
127        slf
128    }
129
130    /// Add one community logo (direct ``LogotypeData`` with one image) to
131    /// the ``communityLogos`` list.
132    ///
133    /// :param media_type: MIME type string (IA5String), e.g. ``"image/png"``.
134    /// :param hash_alg_oid: hash algorithm OID arc components as a list of
135    ///     ints.
136    /// :param hash_value: raw hash bytes of the logotype object.
137    /// :param uri: URI from which the logotype can be retrieved (IA5String).
138    /// :raises ValueError: if any value is invalid (deferred to :meth:`build`).
139    #[pyo3(signature = (media_type, hash_alg_oid, hash_value, uri))]
140    fn add_community_logo_direct<'py>(
141        slf: Bound<'py, Self>,
142        media_type: &str,
143        hash_alg_oid: Vec<u32>,
144        hash_value: &[u8],
145        uri: &str,
146    ) -> Bound<'py, Self> {
147        let spec = synta_certificate::LogotypeDetailsSpec {
148            media_type,
149            hash_alg_oid: &hash_alg_oid,
150            hash_value,
151            uri,
152        };
153        {
154            let mut guard = slf.borrow_mut();
155            let old = std::mem::replace(
156                &mut guard.inner,
157                synta_certificate::LogotypeExtnBuilder::new(),
158            );
159            guard.inner = old.add_community_logo_direct(spec);
160        }
161        slf
162    }
163
164    /// Add an ``OtherLogotypeInfo`` entry with a direct ``LogotypeData``.
165    ///
166    /// :param logotype_type_oid: OID arc components identifying the logotype
167    ///     type.
168    /// :param media_type: MIME type string (IA5String).
169    /// :param hash_alg_oid: hash algorithm OID arc components as a list of
170    ///     ints.
171    /// :param hash_value: raw hash bytes of the logotype object.
172    /// :param uri: URI from which the logotype can be retrieved (IA5String).
173    /// :raises ValueError: if any value is invalid (deferred to :meth:`build`).
174    #[pyo3(signature = (logotype_type_oid, media_type, hash_alg_oid, hash_value, uri))]
175    fn add_other_logo_direct<'py>(
176        slf: Bound<'py, Self>,
177        logotype_type_oid: Vec<u32>,
178        media_type: &str,
179        hash_alg_oid: Vec<u32>,
180        hash_value: &[u8],
181        uri: &str,
182    ) -> Bound<'py, Self> {
183        let spec = synta_certificate::LogotypeDetailsSpec {
184            media_type,
185            hash_alg_oid: &hash_alg_oid,
186            hash_value,
187            uri,
188        };
189        {
190            let mut guard = slf.borrow_mut();
191            let old = std::mem::replace(
192                &mut guard.inner,
193                synta_certificate::LogotypeExtnBuilder::new(),
194            );
195            guard.inner = old.add_other_logo_direct(&logotype_type_oid, spec);
196        }
197        slf
198    }
199
200    /// Build the DER-encoded ``LogotypeExtn`` SEQUENCE.
201    ///
202    /// :returns: DER bytes of the ``LogotypeExtn``.
203    /// :raises ValueError: if no logotype fields were set or if DER encoding
204    ///     fails.
205    fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
206        if self.built {
207            return Err(pyo3::exceptions::PyValueError::new_err(
208                "build() has already been called; create a new builder",
209            ));
210        }
211        self.built = true;
212        let inner = std::mem::replace(
213            &mut self.inner,
214            synta_certificate::LogotypeExtnBuilder::new(),
215        );
216        let der = inner
217            .build()
218            .map_err(pyo3::exceptions::PyValueError::new_err)?;
219        Ok(PyBytes::new(py, &der))
220    }
221
222    fn __repr__(&self) -> String {
223        "LogotypeExtnBuilder()".to_string()
224    }
225}
226
227// ── register ──────────────────────────────────────────────────────────────────
228
229/// Register logotype builder classes into the given module.
230pub(super) fn register_logotype_classes(m: &Bound<'_, PyModule>) -> PyResult<()> {
231    m.add_class::<PyLogotypeExtnBuilder>()?;
232    Ok(())
233}