1use pyo3::prelude::*;
4use pyo3::types::PyBytes;
5
6use synta::traits::Decode;
7use synta::{Decoder, Encoding, TagClass};
8
9use super::error::SyntaErr;
10
11fn parse_tag_class(s: &str) -> PyResult<TagClass> {
12 match s {
13 "Universal" => Ok(TagClass::Universal),
14 "Context" => Ok(TagClass::ContextSpecific),
15 "Application" => Ok(TagClass::Application),
16 "Private" => Ok(TagClass::Private),
17 other => Err(pyo3::exceptions::PyValueError::new_err(format!(
18 "Unknown tag class '{other}'; expected 'Universal', 'Context', 'Application', or 'Private'",
19 ))),
20 }
21}
22
23fn tag_class_str(class: TagClass) -> &'static str {
24 match class {
25 TagClass::Universal => "Universal",
26 TagClass::Application => "Application",
27 TagClass::ContextSpecific => "Context",
28 TagClass::Private => "Private",
29 }
30}
31
32use super::types::{
33 PyBitString, PyBmpString, PyBoolean, PyGeneralString, PyGeneralizedTime, PyIA5String,
34 PyInteger, PyNull, PyNumericString, PyObjectIdentifier, PyOctetString, PyPrintableString,
35 PyReal, PyTeletexString, PyUniversalString, PyUtcTime, PyUtf8String, PyVisibleString,
36};
37use super::PyEncoding;
38
39#[pyclass(name = "Decoder")]
50pub struct PyDecoder {
51 data: Vec<u8>,
53 position: usize,
55 encoding: Encoding,
57}
58
59impl PyDecoder {
60 fn advance(&mut self, by: usize) {
62 self.position += by;
63 debug_assert!(
64 self.position <= self.data.len(),
65 "decoder position {} overshot data length {}",
66 self.position,
67 self.data.len()
68 );
69 }
70
71 fn decode_one<T>(&mut self) -> PyResult<T>
81 where
82 T: for<'a> Decode<'a>,
83 {
84 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
85 let result = T::decode(&mut decoder).map_err(SyntaErr)?;
86 self.advance(decoder.position());
87 Ok(result)
88 }
89}
90
91#[pymethods]
92impl PyDecoder {
93 #[new]
99 #[pyo3(signature = (data, encoding=None))]
100 fn new(data: Vec<u8>, encoding: Option<PyEncoding>) -> Self {
101 Self {
102 data,
103 position: 0,
104 encoding: encoding.unwrap_or(PyEncoding::DER).into(),
105 }
106 }
107
108 fn decode_integer(&mut self) -> PyResult<PyInteger> {
110 self.decode_one::<synta::Integer>()
111 .map(|inner| PyInteger { inner })
112 }
113
114 fn decode_octet_string(&mut self) -> PyResult<PyOctetString> {
116 self.decode_one::<synta::OctetString>()
117 .map(|inner| PyOctetString { inner })
118 }
119
120 fn decode_oid(&mut self) -> PyResult<PyObjectIdentifier> {
122 self.decode_one::<synta::ObjectIdentifier>()
123 .map(PyObjectIdentifier::from_oid)
124 }
125
126 fn decode_bit_string(&mut self) -> PyResult<PyBitString> {
128 self.decode_one::<synta::BitString>()
129 .map(|inner| PyBitString { inner })
130 }
131
132 fn decode_boolean(&mut self) -> PyResult<PyBoolean> {
134 self.decode_one::<synta::Boolean>()
135 .map(|inner| PyBoolean { inner })
136 }
137
138 fn decode_utc_time(&mut self) -> PyResult<PyUtcTime> {
140 self.decode_one::<synta::UtcTime>()
141 .map(|inner| PyUtcTime { inner })
142 }
143
144 fn decode_generalized_time(&mut self) -> PyResult<PyGeneralizedTime> {
146 self.decode_one::<synta::GeneralizedTime>()
147 .map(|inner| PyGeneralizedTime { inner })
148 }
149
150 fn decode_null(&mut self) -> PyResult<PyNull> {
152 self.decode_one::<synta::Null>()?;
153 Ok(PyNull)
154 }
155
156 fn decode_real(&mut self) -> PyResult<PyReal> {
158 self.decode_one::<synta::Real>()
159 .map(|inner| PyReal { inner })
160 }
161
162 fn decode_utf8_string(&mut self) -> PyResult<PyUtf8String> {
164 self.decode_one::<synta::Utf8String>()
165 .map(|inner| PyUtf8String { inner })
166 }
167
168 fn decode_printable_string(&mut self) -> PyResult<PyPrintableString> {
170 self.decode_one::<synta::PrintableString>()
171 .map(|inner| PyPrintableString { inner })
172 }
173
174 fn decode_ia5_string(&mut self) -> PyResult<PyIA5String> {
176 self.decode_one::<synta::IA5String>()
177 .map(|inner| PyIA5String { inner })
178 }
179
180 fn decode_numeric_string(&mut self) -> PyResult<PyNumericString> {
182 self.decode_one::<synta::NumericString>()
183 .map(|inner| PyNumericString { inner })
184 }
185
186 fn decode_teletex_string(&mut self) -> PyResult<PyTeletexString> {
188 self.decode_one::<synta::TeletexString>()
189 .map(|inner| PyTeletexString { inner })
190 }
191
192 fn decode_visible_string(&mut self) -> PyResult<PyVisibleString> {
194 self.decode_one::<synta::VisibleString>()
195 .map(|inner| PyVisibleString { inner })
196 }
197
198 fn decode_general_string(&mut self) -> PyResult<PyGeneralString> {
200 self.decode_one::<synta::GeneralString>()
201 .map(|inner| PyGeneralString { inner })
202 }
203
204 fn decode_universal_string(&mut self) -> PyResult<PyUniversalString> {
206 self.decode_one::<synta::UniversalString>()
207 .map(|inner| PyUniversalString { inner })
208 }
209
210 fn decode_bmp_string(&mut self) -> PyResult<PyBmpString> {
212 self.decode_one::<synta::BmpString>()
213 .map(|inner| PyBmpString { inner })
214 }
215
216 fn decode_any_str<'py>(
249 &mut self,
250 py: Python<'py>,
251 ) -> PyResult<Bound<'py, pyo3::types::PyString>> {
252 use pyo3::exceptions::{PyEOFError, PyValueError};
253 use pyo3::types::PyString;
254 use synta::tag::*;
255
256 if self.position >= self.data.len() {
257 return Err(PyEOFError::new_err("No more data"));
258 }
259
260 let (tag_num, val_bytes, advance) = {
261 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
262 let tag = decoder.read_tag().map_err(SyntaErr)?;
263 let length = decoder.read_length().map_err(SyntaErr)?;
264 let len = length.definite().map_err(SyntaErr)?;
265 let val = decoder.read_bytes(len).map_err(SyntaErr)?.to_vec();
266 (tag.number(), val, decoder.position())
267 };
268
269 let s: String = match tag_num {
270 TAG_UTF8_STRING | TAG_PRINTABLE_STRING | TAG_IA5_STRING | TAG_VISIBLE_STRING
271 | TAG_NUMERIC_STRING | TAG_GENERAL_STRING => {
272 String::from_utf8_lossy(&val_bytes).into_owned()
273 }
274
275 TAG_TELETEX_STRING => val_bytes.iter().map(|&b| b as char).collect(),
277
278 TAG_BMP_STRING => val_bytes
280 .chunks_exact(2)
281 .filter_map(|pair| char::from_u32(u16::from_be_bytes([pair[0], pair[1]]) as u32))
282 .collect(),
283
284 TAG_UNIVERSAL_STRING => val_bytes
286 .chunks_exact(4)
287 .filter_map(|quad| {
288 char::from_u32(u32::from_be_bytes([quad[0], quad[1], quad[2], quad[3]]))
289 })
290 .collect(),
291
292 other => {
293 return Err(PyValueError::new_err(format!(
294 "tag {other} is not an ASN.1 string type; \
295 expected UTF8String (12), PrintableString (19), IA5String (22), \
296 NumericString (18), TeletexString (20), VisibleString (26), \
297 GeneralString (27), BMPString (30), or UniversalString (28)"
298 )));
299 }
300 };
301
302 self.advance(advance);
303 Ok(PyString::new(py, &s))
304 }
305
306 fn decode_sequence(&mut self) -> PyResult<PyDecoder> {
314 use pyo3::exceptions::PyValueError;
315 use synta::tag::TAG_SEQUENCE;
316 use synta::TagClass;
317
318 let (data, advance) = {
319 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
320 let tag = decoder.read_tag().map_err(SyntaErr)?;
321 if tag.class() != TagClass::Universal
322 || tag.number() != TAG_SEQUENCE
323 || !tag.is_constructed()
324 {
325 return Err(PyValueError::new_err(format!(
326 "Expected SEQUENCE (0x30), got tag {:?}",
327 tag
328 )));
329 }
330 let length = decoder.read_length().map_err(SyntaErr)?;
331 let len = length.definite().map_err(SyntaErr)?;
332 let data = decoder.read_bytes(len).map_err(SyntaErr)?.to_vec();
333 (data, decoder.position())
334 };
336 self.advance(advance);
337 Ok(PyDecoder {
338 data,
339 position: 0,
340 encoding: self.encoding,
341 })
342 }
343
344 fn decode_explicit_tag(&mut self, tag_num: u32) -> PyResult<PyDecoder> {
351 use pyo3::exceptions::PyValueError;
352 use synta::TagClass;
353
354 let (data, advance) = {
355 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
356 let tag = decoder.read_tag().map_err(SyntaErr)?;
357 if tag.class() != TagClass::ContextSpecific || tag.number() != tag_num {
358 return Err(PyValueError::new_err(format!(
359 "Expected explicit context tag [{}], got {:?}",
360 tag_num, tag
361 )));
362 }
363 let length = decoder.read_length().map_err(SyntaErr)?;
364 let len = length.definite().map_err(SyntaErr)?;
365 let data = decoder.read_bytes(len).map_err(SyntaErr)?.to_vec();
366 (data, decoder.position())
367 };
369 self.advance(advance);
370 Ok(PyDecoder {
371 data,
372 position: 0,
373 encoding: self.encoding,
374 })
375 }
376
377 fn decode_set(&mut self) -> PyResult<PyDecoder> {
381 use pyo3::exceptions::PyValueError;
382 use synta::tag::TAG_SET;
383
384 let (data, advance) = {
385 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
386 let tag = decoder.read_tag().map_err(SyntaErr)?;
387 if tag.class() != TagClass::Universal
388 || tag.number() != TAG_SET
389 || !tag.is_constructed()
390 {
391 return Err(PyValueError::new_err(format!(
392 "Expected SET (0x31), got tag {:?}",
393 tag
394 )));
395 }
396 let length = decoder.read_length().map_err(SyntaErr)?;
397 let len = length.definite().map_err(SyntaErr)?;
398 let data = decoder.read_bytes(len).map_err(SyntaErr)?.to_vec();
399 (data, decoder.position())
400 };
401 self.advance(advance);
402 Ok(PyDecoder {
403 data,
404 position: 0,
405 encoding: self.encoding,
406 })
407 }
408
409 fn decode_implicit_tag(&mut self, tag_num: u32, tag_class: &str) -> PyResult<PyDecoder> {
427 use pyo3::exceptions::PyValueError;
428
429 let class = parse_tag_class(tag_class)?;
430
431 let (data, advance) = {
432 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
433 let tag = decoder.read_tag().map_err(SyntaErr)?;
434 if tag.class() != class || tag.number() != tag_num {
435 return Err(PyValueError::new_err(format!(
436 "Expected implicit tag [{tag_num} {tag_class}], got {:?}",
437 tag
438 )));
439 }
440 let length = decoder.read_length().map_err(SyntaErr)?;
441 let len = length.definite().map_err(SyntaErr)?;
442 let data = decoder.read_bytes(len).map_err(SyntaErr)?.to_vec();
443 (data, decoder.position())
444 };
445 self.advance(advance);
446 Ok(PyDecoder {
447 data,
448 position: 0,
449 encoding: self.encoding,
450 })
451 }
452
453 fn peek_tag(&self) -> PyResult<(u32, String, bool)> {
467 use pyo3::exceptions::PyEOFError;
468 if self.position >= self.data.len() {
469 return Err(PyEOFError::new_err("No more data to peek"));
470 }
471 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
472 let tag = decoder.read_tag().map_err(SyntaErr)?;
473 Ok((
474 tag.number(),
475 tag_class_str(tag.class()).to_string(),
476 tag.is_constructed(),
477 ))
478 }
479
480 fn decode_raw_tlv<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
491 use pyo3::exceptions::PyEOFError;
492 if self.position >= self.data.len() {
493 return Err(PyEOFError::new_err("No more data"));
494 }
495 let total = {
496 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
497 decoder.read_tag().map_err(SyntaErr)?;
498 let length = decoder.read_length().map_err(SyntaErr)?;
499 let content_len = length.definite().map_err(SyntaErr)?;
500 decoder.read_bytes(content_len).map_err(SyntaErr)?;
501 decoder.position()
502 };
503 let end = self.position + total;
504 let bytes = PyBytes::new(py, &self.data[self.position..end]);
505 self.advance(total);
506 Ok(bytes)
507 }
508
509 fn remaining_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
528 PyBytes::new(py, &self.data[self.position..])
529 }
530
531 fn decode_any(&mut self, py: Python) -> PyResult<Py<PyAny>> {
537 let (py_obj, advance) = {
538 let mut decoder = Decoder::new(&self.data[self.position..], self.encoding);
539 let element = synta::Element::decode(&mut decoder).map_err(SyntaErr)?;
540 let advance = decoder.position();
541 let py_obj = super::types::element_to_pyobject(element, py)?;
542 (py_obj, advance)
543 };
544 self.advance(advance);
545 Ok(py_obj)
546 }
547
548 fn is_empty(&self) -> bool {
550 self.position == self.data.len()
551 }
552
553 fn position(&self) -> usize {
555 self.position
556 }
557
558 fn remaining(&self) -> usize {
560 self.data.len().saturating_sub(self.position)
561 }
562
563 fn __repr__(&self) -> String {
564 format!(
565 "Decoder(position={}, remaining={}, encoding={:?})",
566 self.position,
567 self.remaining(),
568 self.encoding
569 )
570 }
571}