Skip to main content

structured_pkix/
der.rs

1//! Holding a range to the distinguished rules.
2//!
3//! Certificates are not the only structure whose schema fixes its encoding
4//! regardless of the envelope carrying it. A signed message is BER, and the
5//! attributes inside it that a signature covers "MUST be DER encoded, even if
6//! the rest of the structure is BER encoded" (RFC 5652 5.3). The walk that
7//! decides that question already exists here, so it is exposed rather than
8//! written a second time: two answers to what counts as DER is one answer more
9//! than a product can defend.
10
11use crate::error::{Error, ErrorCode, Result};
12use structured_asn1::{Element, ScopedReader as Reader};
13
14/// Whether a range and everything under it is DER.
15///
16/// Definite lengths in their shortest form, primitive encodings where the
17/// distinguished rules require them, assigned universal types, and the members
18/// of a set in ascending order of their encodings (X.690 11.6).
19///
20/// The reader decides which rules apply to the outermost header, so a caller
21/// holding a range to DER inside a BER document reads it again under DER first;
22/// [`structured_asn1::Reader::reread`] is what that is for.
23///
24/// # Errors
25///
26/// Reports which rule was broken and where.
27pub fn conforms<'a>(
28    parent: &Reader<'a, '_>,
29    element: &Element<'a>,
30    field: &'static str,
31) -> Result<()> {
32    crate::name::walk_der(parent, element, field, crate::name::Strings::Checked)
33}
34
35/// Validates the contents of a schema-selected IMPLICIT BIT STRING.
36/// The enclosing schema checks its context tag; this reuses the same iterative
37/// BER fragment/padding kernel as universal BIT STRINGs. DER requires primitive.
38pub fn implicit_bit_string<'a>(
39    parent: &Reader<'a, '_>,
40    element: &Element<'a>,
41    field: &'static str,
42) -> Result<()> {
43    if !crate::name::segments::implicit(parent, element, field, true)? {
44        return Err(Error::new(
45            ErrorCode::MalformedValue,
46            field,
47            element.span().start,
48        ));
49    }
50    Ok(())
51}
52
53/// Whether two members of a set are in the order the distinguished rules put
54/// them in.
55///
56/// "The encodings of the component values of a set-of value shall appear in
57/// ascending order" (X.690 11.6), comparing octet by octet with the shorter
58/// encoding treated as though padded with zeros.
59///
60/// It is separate from [`conforms`] because a set is not always written under
61/// a set's own tag: a signed message writes its attributes under a context tag
62/// and signs them under `SET OF`, so the ordering is the schema's to check
63/// where the tag no longer says so.
64///
65/// `parent` supplies the current execution account and original parser-local
66/// cumulative ceiling. Before comparison, one work unit per possible octet
67/// pair is admitted: the longer length, because X.690 compares the shorter
68/// encoding with virtual zero padding. A minimum of one unit covers an empty
69/// comparison. This is an upper bound; an unequal pair may stop the walk early.
70/// `parent` must belong to the session and source that produced `next`; this
71/// is a caller precondition, not a provenance check.
72///
73/// # Errors
74/// Returns a resource refusal at `next`'s original offset before comparing
75/// bytes. `Ok(false)` means incorrect order, independently of resource errors.
76pub fn set_ordered(
77    parent: &Reader<'_, '_>,
78    previous: &[u8],
79    next: &Element<'_>,
80    field: &'static str,
81) -> Result<bool> {
82    let work = previous.len().max(next.as_bytes().len()).max(1) as u64;
83    parent
84        .charge(work)
85        .map_err(|error| Error::new(ErrorCode::Encoding(error.code()), field, next.span().start))?;
86    Ok(crate::name::der_set_ordered(previous, next.as_bytes()))
87}
88
89/// Visits primitive fragments using the SAME iterative BER BIT STRING kernel
90/// as validation. Only final nested padding is permitted (X.690 ยง8.6.4).
91/// The caller may retain original borrowed bits/spans; no payload is joined.
92/// A later error does not undo side effects of earlier visitor calls.
93/// DER admits only the primitive form. Current/origin work and depth apply.
94pub fn bit_fragments<'a>(
95    parent: &structured_asn1::ScopedReader<'a, '_>,
96    element: &Element<'a>,
97    field: &'static str,
98    visitor: impl FnMut(Element<'a>, structured_asn1::BitString<'a>) -> Result<()>,
99) -> Result<()> {
100    crate::name::segments::bit_fragments(parent, element, field, visitor)
101}