1use pyo3::prelude::*;
8use pyo3::types::PyBytes;
9use synta::traits::{Decode, Encode};
10use synta::{Decoder, Encoding};
11
12use crate::error::SyntaErr;
13use crate::types::PyObjectIdentifier;
14
15fn encode_to_der<T: Encode>(v: &T) -> Vec<u8> {
16 let mut enc = synta::Encoder::new(Encoding::Der);
17 if v.encode(&mut enc).is_err() {
18 return Vec::new();
19 }
20 enc.finish().unwrap_or_default()
21}
22
23#[pyclass(frozen, name = "MSCSTemplateV2")]
40pub struct PyMSCSTemplateV2 {
41 template_id: Py<PyObjectIdentifier>,
42 template_major_version: i64,
43 template_minor_version: Option<i64>,
44 raw_der: Vec<u8>,
45}
46
47#[pymethods]
48impl PyMSCSTemplateV2 {
49 #[staticmethod]
51 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
52 let mut dec = Decoder::new(data, Encoding::Der);
53 let tmpl =
54 synta_certificate::ms_pki_types::MSCSTemplateV2::decode(&mut dec).map_err(SyntaErr)?;
55 let template_id = Py::new(py, PyObjectIdentifier::from_oid(tmpl.template_id.clone()))?;
56 let template_major_version = tmpl.template_major_version.as_i64().map_err(SyntaErr)?;
57 let template_minor_version = tmpl
58 .template_minor_version
59 .as_ref()
60 .map(|v| v.as_i64().map_err(SyntaErr))
61 .transpose()?;
62 let raw_der = encode_to_der(&tmpl);
63 Ok(PyMSCSTemplateV2 {
64 template_id,
65 template_major_version,
66 template_minor_version,
67 raw_der,
68 })
69 }
70
71 pub fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
73 PyBytes::new(py, &self.raw_der)
74 }
75
76 #[getter]
78 fn template_id(&self, py: Python<'_>) -> Py<PyObjectIdentifier> {
79 self.template_id.clone_ref(py)
80 }
81
82 #[getter]
84 fn template_major_version(&self) -> i64 {
85 self.template_major_version
86 }
87
88 #[getter]
90 fn template_minor_version(&self) -> Option<i64> {
91 self.template_minor_version
92 }
93
94 fn __repr__(&self, py: Python<'_>) -> String {
95 let oid = self.template_id.bind(py).get();
96 format!(
97 "MSCSTemplateV2(template_id='{}', major={}, minor={:?})",
98 oid.inner, self.template_major_version, self.template_minor_version,
99 )
100 }
101}
102
103#[pyclass(frozen, name = "RequestClientInfo")]
119pub struct PyRequestClientInfo {
120 client_id: Option<i64>,
121 machine_name: Option<String>,
122 user_name: Option<String>,
123 process_name: Option<String>,
124}
125
126#[pymethods]
127impl PyRequestClientInfo {
128 #[staticmethod]
130 pub fn from_der(data: &[u8]) -> PyResult<Self> {
131 let mut dec = Decoder::new(data, Encoding::Der);
132 let info = synta_certificate::ms_pki_types::RequestClientInfo::decode(&mut dec)
133 .map_err(SyntaErr)?;
134 let client_id = info
135 .client_id
136 .as_ref()
137 .map(|v| v.as_i64().map_err(SyntaErr))
138 .transpose()?;
139 let machine_name = info.machine_name.as_ref().map(|s| s.as_str().to_owned());
140 let user_name = info.user_name.as_ref().map(|s| s.as_str().to_owned());
141 let process_name = info.process_name.as_ref().map(|s| s.as_str().to_owned());
142 Ok(PyRequestClientInfo {
143 client_id,
144 machine_name,
145 user_name,
146 process_name,
147 })
148 }
149
150 #[getter]
152 fn client_id(&self) -> Option<i64> {
153 self.client_id
154 }
155
156 #[getter]
158 fn machine_name(&self) -> Option<&str> {
159 self.machine_name.as_deref()
160 }
161
162 #[getter]
164 fn user_name(&self) -> Option<&str> {
165 self.user_name.as_deref()
166 }
167
168 #[getter]
170 fn process_name(&self) -> Option<&str> {
171 self.process_name.as_deref()
172 }
173
174 fn __repr__(&self) -> String {
175 format!(
176 "RequestClientInfo(machine={:?}, user={:?}, process={:?})",
177 self.machine_name, self.user_name, self.process_name,
178 )
179 }
180}
181
182pub(super) fn register_ms_pki_submodule(parent: &Bound<'_, PyModule>) -> PyResult<()> {
185 let py = parent.py();
186 let m = PyModule::new(py, "synta.ms_pki")?;
187
188 m.add_class::<PyMSCSTemplateV2>()?;
189 m.add_class::<PyRequestClientInfo>()?;
190
191 let oc = |components: &[u32]| super::oid_const(py, components);
193
194 m.add(
195 "ID_MS_CERTSRV_CA_VERSION",
196 oc(synta_certificate::ms_pki_types::ID_MS_CERTSRV_CA_VERSION),
197 )?;
198 m.add(
199 "ID_MS_CERTSRV_PREVIOUS_CERT_HASH",
200 oc(synta_certificate::ms_pki_types::ID_MS_CERTSRV_PREVIOUS_CERT_HASH),
201 )?;
202 m.add(
203 "ID_MS_CRL_VIRTUAL_BASE",
204 oc(synta_certificate::ms_pki_types::ID_MS_CRL_VIRTUAL_BASE),
205 )?;
206 m.add(
207 "ID_MS_CRL_NEXT_PUBLISH",
208 oc(synta_certificate::ms_pki_types::ID_MS_CRL_NEXT_PUBLISH),
209 )?;
210 m.add(
211 "ID_MS_KP_CA_EXCHANGE",
212 oc(synta_certificate::ms_pki_types::ID_MS_KP_CA_EXCHANGE),
213 )?;
214 m.add(
215 "ID_MS_KP_KEY_RECOVERY_AGENT",
216 oc(synta_certificate::ms_pki_types::ID_MS_KP_KEY_RECOVERY_AGENT),
217 )?;
218 m.add(
219 "ID_MS_ENTERPRISE_OID_ROOT",
220 oc(synta_certificate::ms_pki_types::ID_MS_ENTERPRISE_OID_ROOT),
221 )?;
222 m.add(
223 "ID_MS_APPLICATION_CERT_POLICIES",
224 oc(synta_certificate::ms_pki_types::ID_MS_APPLICATION_CERT_POLICIES),
225 )?;
226 m.add(
227 "ID_MS_REQUEST_CLIENT_INFO",
228 oc(synta_certificate::ms_pki_types::ID_MS_REQUEST_CLIENT_INFO),
229 )?;
230 m.add(
231 "ID_MS_ENCRYPTED_KEY_HASH",
232 oc(synta_certificate::ms_pki_types::ID_MS_ENCRYPTED_KEY_HASH),
233 )?;
234 m.add(
235 "ID_MS_CERTSRV_CROSSCA_VERSION",
236 oc(synta_certificate::ms_pki_types::ID_MS_CERTSRV_CROSSCA_VERSION),
237 )?;
238 m.add(
239 "ID_MS_KP_CTL_USAGE_SIGNING",
240 oc(synta_certificate::ms_pki_types::ID_MS_KP_CTL_USAGE_SIGNING),
241 )?;
242 m.add(
243 "ID_MS_KP_TIME_STAMP_SIGNING",
244 oc(synta_certificate::ms_pki_types::ID_MS_KP_TIME_STAMP_SIGNING),
245 )?;
246 m.add(
247 "ID_MS_KP_EFS_CRYPTO",
248 oc(synta_certificate::ms_pki_types::ID_MS_KP_EFS_CRYPTO),
249 )?;
250 m.add(
251 "ID_MS_KP_EFS_RECOVERY",
252 oc(synta_certificate::ms_pki_types::ID_MS_KP_EFS_RECOVERY),
253 )?;
254 m.add(
255 "ID_MS_KP_KEY_RECOVERY",
256 oc(synta_certificate::ms_pki_types::ID_MS_KP_KEY_RECOVERY),
257 )?;
258 m.add(
259 "ID_MS_KP_DOCUMENT_SIGNING",
260 oc(synta_certificate::ms_pki_types::ID_MS_KP_DOCUMENT_SIGNING),
261 )?;
262 m.add(
263 "ID_MS_KP_LIFETIME_SIGNING",
264 oc(synta_certificate::ms_pki_types::ID_MS_KP_LIFETIME_SIGNING),
265 )?;
266 m.add(
267 "ID_MS_AUTO_ENROLL_CTL_USAGE",
268 oc(synta_certificate::ms_pki_types::ID_MS_AUTO_ENROLL_CTL_USAGE),
269 )?;
270
271 crate::install_submodule(
272 parent,
273 &m,
274 "synta.ms_pki",
275 Some(concat!(
276 "synta.ms_pki — Microsoft PKI (AD CS) certificate extension types.\n\n",
277 "Provides ``MSCSTemplateV2`` for parsing certificate template extensions\n",
278 "and ``RequestClientInfo`` for parsing enrollment client info extensions.\n",
279 )),
280 )?;
281 Ok(())
282}