1use crate::error::{Error, ErrorCode, Located, Result};
9use core::num::NonZeroUsize;
10use structured_asn1::memory::{Memory, Work};
11use structured_asn1::{Element, Encoding, Oid, Parser, Reader, Span, Tag, universal};
12
13#[derive(Debug, Clone, Copy)]
15pub struct AlgorithmIdentifier<'a> {
16 algorithm: Oid<'a>,
17 parameter_start: Option<NonZeroUsize>,
20 null_parameter: bool,
21 der: &'a [u8],
22 offset: u64,
23 field: &'static str,
24 parser: &'a Parser,
25 outer: u32,
26 encoding: Encoding,
27 indefinite: bool,
28}
29
30#[derive(Debug, Clone, Copy)]
33struct AlgorithmFields<'a> {
34 algorithm: Oid<'a>,
35 parameter_start: Option<NonZeroUsize>,
36 null_parameter: bool,
37 der: &'a [u8],
38 offset: u64,
39 field: &'static str,
40 parser: &'a Parser,
41 outer: u32,
42 encoding: Encoding,
43 indefinite: bool,
44}
45
46#[derive(Debug)]
60pub struct AlgorithmReading<'a> {
61 fields: AlgorithmFields<'a>,
62 fault: Option<Error>,
63}
64
65impl<'a> AlgorithmReading<'a> {
66 #[must_use]
68 pub fn algorithm(&self) -> Oid<'a> {
69 self.fields.algorithm
70 }
71
72 #[must_use]
74 pub fn parameters(&self) -> Option<&'a [u8]> {
75 parameter_bytes(
76 self.fields.der,
77 self.fields.parameter_start,
78 self.fields.indefinite,
79 )
80 }
81
82 #[must_use]
85 pub fn parameters_span(&self) -> Option<Span> {
86 let start = self.fields.parameter_start?.get();
87 Some(Span {
88 start: self.fields.offset + start as u64,
89 length: self.parameters()?.len() as u64,
90 })
91 }
92
93 #[must_use]
96 pub fn has_null_parameter(&self) -> bool {
97 self.fields.null_parameter
98 }
99
100 #[must_use]
102 pub fn as_bytes(&self) -> &'a [u8] {
103 self.fields.der
104 }
105
106 #[must_use]
108 pub fn offset(&self) -> u64 {
109 self.fields.offset
110 }
111
112 #[must_use]
114 pub fn encoding(&self) -> Encoding {
115 self.fields.encoding
116 }
117
118 #[must_use]
120 pub fn fault(&self) -> Option<&Error> {
121 self.fault.as_ref()
122 }
123
124 #[must_use]
127 pub fn identifier(&self) -> Option<AlgorithmIdentifier<'a>> {
128 if self.fault.is_some() {
129 return None;
130 }
131 Some(self.fields.identifier())
132 }
133
134 pub fn same_written_in(&self, other: &Self, parent: &Reader<'_>) -> Result<bool> {
139 written_matches(&self.fields, &other.fields, parent, false)
140 }
141
142 fn into_identifier(self) -> Result<AlgorithmIdentifier<'a>> {
143 match self.fault {
144 Some(error) => Err(error),
145 None => Ok(self.fields.identifier()),
146 }
147 }
148}
149
150impl<'a> AlgorithmFields<'a> {
151 fn identifier(self) -> AlgorithmIdentifier<'a> {
153 AlgorithmIdentifier {
154 algorithm: self.algorithm,
155 parameter_start: self.parameter_start,
156 null_parameter: self.null_parameter,
157 der: self.der,
158 offset: self.offset,
159 field: self.field,
160 parser: self.parser,
161 outer: self.outer,
162 encoding: self.encoding,
163 indefinite: self.indefinite,
164 }
165 }
166}
167
168fn parameter_bytes(bytes: &[u8], start: Option<NonZeroUsize>, indefinite: bool) -> Option<&[u8]> {
169 let start = start?.get();
170 let end = bytes.len() - if indefinite { 2 } else { 0 };
171 Some(&bytes[start..end])
172}
173
174trait WrittenValue {
175 fn oid_bytes(&self) -> &[u8];
176 fn parameter_bytes(&self) -> Option<&[u8]>;
177 fn valid_null(&self) -> bool;
178 fn producer(&self) -> &Parser;
179 fn field(&self) -> &'static str;
180 fn offset(&self) -> u64;
181}
182
183impl WrittenValue for AlgorithmFields<'_> {
184 fn oid_bytes(&self) -> &[u8] {
185 self.algorithm.as_bytes()
186 }
187 fn parameter_bytes(&self) -> Option<&[u8]> {
188 parameter_bytes(self.der, self.parameter_start, self.indefinite)
189 }
190 fn valid_null(&self) -> bool {
191 self.null_parameter
192 }
193 fn producer(&self) -> &Parser {
194 self.parser
195 }
196 fn field(&self) -> &'static str {
197 self.field
198 }
199 fn offset(&self) -> u64 {
200 self.offset
201 }
202}
203
204impl WrittenValue for AlgorithmIdentifier<'_> {
205 fn oid_bytes(&self) -> &[u8] {
206 self.algorithm.as_bytes()
207 }
208 fn parameter_bytes(&self) -> Option<&[u8]> {
209 self.parameters()
210 }
211 fn valid_null(&self) -> bool {
212 self.null_parameter
213 }
214 fn producer(&self) -> &Parser {
215 self.parser
216 }
217 fn field(&self) -> &'static str {
218 self.field
219 }
220 fn offset(&self) -> u64 {
221 self.offset
222 }
223}
224
225fn require_written_parsers(
226 left: &impl WrittenValue,
227 right: &impl WrittenValue,
228 parent: &Reader<'_>,
229) -> Result<()> {
230 if !core::ptr::eq(left.producer(), parent.parser()) {
231 return Err(Error::new(
232 ErrorCode::ForeignReader,
233 left.field(),
234 left.offset(),
235 ));
236 }
237 if !core::ptr::eq(right.producer(), parent.parser()) {
238 return Err(Error::new(
239 ErrorCode::ForeignReader,
240 right.field(),
241 right.offset(),
242 ));
243 }
244 Ok(())
245}
246
247fn written_matches(
248 left: &impl WrittenValue,
249 right: &impl WrittenValue,
250 parent: &Reader<'_>,
251 defaults: bool,
252) -> Result<bool> {
253 require_written_parsers(left, right, parent)?;
254 written_values(left, right, parent, defaults)
255}
256
257fn written_values(
258 left: &impl WrittenValue,
259 right: &impl WrittenValue,
260 parent: &Reader<'_>,
261 defaults: bool,
262) -> Result<bool> {
263 let charge = |amount| {
264 parent.charge(amount).map_err(|error| {
265 Error::new(
266 ErrorCode::Encoding(error.code()),
267 right.field(),
268 right.offset(),
269 )
270 })
271 };
272 charge(1)?;
273 let kind = left.oid_bytes();
274 if kind.len() != right.oid_bytes().len() {
275 return Ok(false);
276 }
277 charge(kind.len() as u64)?;
278 if kind != right.oid_bytes() {
279 return Ok(false);
280 }
281 if defaults || (left.valid_null() && right.valid_null()) {
282 return Ok(true);
283 }
284 let (Some(first), Some(second)) = (left.parameter_bytes(), right.parameter_bytes()) else {
285 return Ok(left.parameter_bytes().is_none() && right.parameter_bytes().is_none());
286 };
287 if first.len() != second.len() {
288 return Ok(false);
289 }
290 charge(first.len() as u64)?;
291 Ok(first == second)
292}
293
294impl<'a> AlgorithmIdentifier<'a> {
295 #[must_use]
298 pub fn has_null_parameter(self) -> bool {
299 self.null_parameter
300 }
301
302 pub fn same_written_in(self, other: &Self, parent: &Reader<'_>) -> Result<bool> {
311 self.value_matches(other, parent, false)
312 }
313
314 pub fn digest_matches_in(self, other: &Self, parent: &Reader<'_>) -> Result<bool> {
324 self.value_matches(other, parent, true)
325 }
326
327 fn value_matches(self, other: &Self, parent: &Reader<'_>, digest: bool) -> Result<bool> {
328 require_written_parsers(&self, other, parent)?;
329 let sha2 = if digest {
330 let known = self.validate_digest_params(parent)?;
331 other.validate_digest_params(parent)?;
332 known
333 } else {
334 false
335 };
336 written_values(&self, other, parent, sha2)
337 }
338
339 fn validate_digest_params(self, parent: &Reader<'_>) -> Result<bool> {
340 let charge = |amount| {
341 parent.charge(amount).map_err(|error| {
342 Error::new(ErrorCode::Encoding(error.code()), self.field, self.offset)
343 })
344 };
345 charge(1)?;
346 let kind = self.algorithm.as_bytes();
347 if kind.len() != 9 {
348 return Ok(false);
349 }
350 charge(9)?;
351 if kind[..8] != [0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2] || kind[8] < 1 || kind[8] > 4 {
352 return Ok(false);
353 }
354 if self.parameter_start.is_some() && !self.null_parameter {
355 return Err(Error::new(
359 ErrorCode::MalformedValue,
360 self.field,
361 self.offset,
362 ));
363 }
364 Ok(true)
365 }
366 #[must_use]
368 pub fn algorithm(self) -> Oid<'a> {
369 self.algorithm
370 }
371
372 #[must_use]
379 pub fn parameters(self) -> Option<&'a [u8]> {
380 parameter_bytes(self.der, self.parameter_start, self.indefinite)
381 }
382
383 #[must_use]
385 pub fn offset(self) -> u64 {
386 self.offset
387 }
388
389 #[must_use]
391 pub fn encoding(self) -> Encoding {
392 self.encoding
393 }
394
395 pub fn parameters_reader_in<'query>(
408 self,
409 parent: &Reader<'query>,
410 field: &'static str,
411 ) -> Result<Option<Reader<'query>>>
412 where
413 'a: 'query,
414 {
415 if !self.same_session(parent) {
416 return Err(Error::new(ErrorCode::ForeignReader, field, self.offset));
417 }
418 let at = self
419 .parameter_start
420 .map_or(self.offset, |start| self.offset + start.get() as u64);
421 parent
422 .execution()
423 .charge_work(1)
424 .map_err(|reason| Error::resource(reason, field, at))?;
425 let Some(parameter) = self.parameters() else {
426 return Ok(None);
427 };
428 self.parser
429 .read_nested_query_in(parent, parameter, at, self.encoding, self.outer)
430 .field(field)
431 .map(Some)
432 }
433
434 #[must_use]
438 pub fn same_session(self, parent: &Reader<'_>) -> bool {
439 core::ptr::eq(self.parser, parent.parser())
440 }
441
442 #[must_use]
447 pub fn as_bytes(self) -> &'a [u8] {
448 self.der
449 }
450
451 #[must_use]
454 pub fn has_no_parameters(self) -> bool {
455 self.parameters()
457 .is_none_or(|parameters| parameters == [0x05, 0x00])
458 }
459
460 pub fn matches(self, other: &AlgorithmIdentifier<'_>, execution: &Memory) -> Result<bool> {
501 self.matches_with(other, |work| {
502 execution
503 .charge_work(work)
504 .map_err(|reason| Error::resource(reason, self.field, self.offset))
505 })
506 }
507
508 pub(crate) fn matches_in(
509 self,
510 other: &AlgorithmIdentifier<'_>,
511 parent: &Reader<'_>,
512 ) -> Result<bool> {
513 self.matches_with(other, |work| {
514 parent.charge(work).map_err(|error| {
515 Error::new(ErrorCode::Encoding(error.code()), self.field, self.offset)
516 })
517 })
518 }
519
520 fn matches_with(
521 self,
522 other: &AlgorithmIdentifier<'_>,
523 mut charge: impl FnMut(u64) -> Result<()>,
524 ) -> Result<bool> {
525 charge(1)?;
526 if self.algorithm.as_bytes().len() != other.algorithm.as_bytes().len() {
527 return Ok(false);
528 }
529 charge(self.algorithm.as_bytes().len() as u64)?;
530 if self.algorithm != other.algorithm {
531 return Ok(false);
532 }
533 if self.has_no_parameters() && other.has_no_parameters() {
534 return Ok(true);
535 }
536 let (Some(left), Some(right)) = (self.parameters(), other.parameters()) else {
537 return Ok(false);
538 };
539 if left.len() != right.len() {
540 return Ok(false);
541 }
542 charge(left.len() as u64)?;
543 Ok(left == right)
544 }
545}
546
547pub fn read<'a>(
558 parent: &Reader<'a>,
559 element: &Element<'a>,
560 field: &'static str,
561) -> Result<AlgorithmIdentifier<'a>> {
562 read_tagged(
563 parent,
564 element,
565 Tag::constructed(universal::SEQUENCE),
566 field,
567 )
568}
569
570pub fn read_implicit<'a>(
579 parent: &Reader<'a>,
580 element: &Element<'a>,
581 number: u32,
582 field: &'static str,
583) -> Result<AlgorithmIdentifier<'a>> {
584 read_tagged(parent, element, Tag::context(true, number), field)
585}
586
587pub fn read_inspection<'a>(
594 parent: &Reader<'a>,
595 element: &Element<'a>,
596 field: &'static str,
597) -> Result<AlgorithmReading<'a>> {
598 read_fields::<true>(
599 parent,
600 element,
601 Tag::constructed(universal::SEQUENCE),
602 field,
603 )
604}
605
606pub fn read_implicit_inspection<'a>(
608 parent: &Reader<'a>,
609 element: &Element<'a>,
610 number: u32,
611 field: &'static str,
612) -> Result<AlgorithmReading<'a>> {
613 read_fields::<true>(parent, element, Tag::context(true, number), field)
614}
615
616fn read_fields<'a, const INSPECTION: bool>(
617 parent: &Reader<'a>,
618 element: &Element<'a>,
619 expected: Tag,
620 field: &'static str,
621) -> Result<AlgorithmReading<'a>> {
622 let fail = |code| Error::new(code, field, element.span().start);
623 if element.tag() != expected {
624 return Err(fail(ErrorCode::Encoding(
625 structured_asn1::ErrorCode::UnexpectedTag,
626 )));
627 }
628 let mut inside = parent.enter(element).field(field)?;
629 let algorithm = inside.read_oid().field(field)?;
630 let parameter = inside.next_element().field(field)?;
631 let mut fault = None;
632 let mut null_parameter = false;
633 if let Some(parameter) = ¶meter {
634 match crate::name::walk_der(&inside, parameter, field, crate::name::Strings::Checked) {
635 Ok(()) => {
636 null_parameter = parameter.tag() == Tag::primitive(universal::NULL);
641 }
642 Err(error) => {
643 if !INSPECTION || error.is_resource() || error.code() == ErrorCode::ForeignReader {
644 return Err(error);
645 }
646 fault = Some(error);
647 }
648 }
649 }
650 if !inside.is_empty() {
651 return Err(fail(ErrorCode::TrailingData));
652 }
653 let parameter_start = match parameter {
654 None => None,
655 Some(parameter) => {
656 let start = usize::try_from(parameter.span().start - element.span().start)
657 .map_err(|_| fail(ErrorCode::ValueTooLarge))?;
658 Some(NonZeroUsize::new(start).expect("the algorithm OID precedes its parameters"))
659 }
660 };
661 Ok(AlgorithmReading {
662 fields: AlgorithmFields {
663 algorithm,
664 parameter_start,
665 null_parameter,
666 der: element.as_bytes(),
667 offset: element.span().start,
668 field,
669 parser: parent.parser(),
670 outer: parent.depth(),
671 encoding: inside.encoding(),
672 indefinite: element.is_indefinite(),
673 },
674 fault,
675 })
676}
677
678fn read_tagged<'a>(
679 parent: &Reader<'a>,
680 element: &Element<'a>,
681 expected: Tag,
682 field: &'static str,
683) -> Result<AlgorithmIdentifier<'a>> {
684 read_fields::<false>(parent, element, expected, field)?.into_identifier()
685}
686
687#[cfg(test)]
688mod tests;