_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}