Skip to main content

structured_cms/
encoding.rs

1//! Schema-selected DER projections for archive inputs. Signed/content octets
2//! are never silently replaced in an ordinary signature verification.
3use crate::{
4    Error, ErrorCode, Octets, Result,
5    stream::{Receiver, Refused, Source},
6};
7use core::cmp::Ordering;
8use structured_asn1::{
9    Class, Element, Encoding, Span, Tag,
10    length::DerLength,
11    memory::{HashWork, MemoryError, OwnedMap, OwnedVec, SortError, Work},
12    universal,
13};
14const FIELD: &str = "archive.der";
15
16#[derive(Clone, Copy)]
17pub(crate) enum Form {
18    Set,
19    Octets,
20    Omit,
21}
22#[derive(Clone, Copy, Hash, PartialEq, Eq)]
23struct Key(u64, u64);
24impl From<Span> for Key {
25    fn from(span: Span) -> Self {
26        Self(span.start, span.length)
27    }
28}
29impl HashWork for Key {
30    fn hash_work(&self) -> core::result::Result<u64, MemoryError> {
31        Ok(3)
32    }
33}
34pub(crate) struct Rules<W: Work> {
35    items: OwnedMap<Key, Form, W>,
36}
37impl<W: Work> Rules<W> {
38    pub(crate) fn new(work: &W) -> Self {
39        Self { items: work.map() }
40    }
41    pub(crate) fn insert(&mut self, span: Span, form: Form, _work: &W) -> Result<()> {
42        self.items
43            .try_insert(Key::from(span), form)
44            .map(|_| ())
45            .map_err(|cause| Error::resource(cause, FIELD, span.start))
46    }
47    pub(crate) fn algorithm(
48        &mut self,
49        identifier: structured_pkix::AlgorithmIdentifier<'_>,
50        work: &W,
51    ) -> Result<()> {
52        let at = identifier.offset();
53        work.charge_work(
54            1 + identifier
55                .algorithm()
56                .as_bytes()
57                .len()
58                .min(structured_pkix::oid::RSASSA_PSS.as_bytes().len()) as u64,
59        )
60        .map_err(|cause| Error::resource(cause, FIELD, at))?;
61        if identifier.algorithm() != structured_pkix::oid::RSASSA_PSS {
62            return Ok(());
63        }
64        if let Some(parameters) =
65            structured_pkix::algorithm::pss::read(identifier, work).map_err(Error::pkix)?
66        {
67            for element in parameters.written_defaults() {
68                self.insert(element.span(), Form::Omit, work)?;
69            }
70        }
71        Ok(())
72    }
73}
74enum Body<'d> {
75    Bytes(&'d [u8]),
76    Byte(u8),
77    Bits {
78        prefix: &'d [u8],
79        last: u8,
80    },
81    String(Octets<'d>),
82    Segments {
83        pieces: OwnedVec<&'d [u8]>,
84        unused: u8,
85        last: Option<u8>,
86    },
87    Children,
88}
89struct Node<'d> {
90    tag: &'d [u8],
91    primitive: Option<[u8; 1]>,
92    length: DerLength,
93    total: u64,
94    body: Body<'d>,
95    parent: Option<usize>,
96    first: Option<usize>,
97    next: Option<usize>,
98}
99/// Metadata-only encoding tree. Content payloads remain borrowed original
100/// ranges, including constructed eContent; traversal AND drop are iterative.
101pub(crate) struct Projection<'d> {
102    nodes: OwnedVec<Node<'d>>,
103}
104struct Frame<'d, 'w> {
105    reader: structured_asn1::ScopedReader<'d, 'w>,
106    index: usize,
107    last: Option<usize>,
108    sort: bool,
109    at: u64,
110}
111impl<'d> Projection<'d> {
112    pub(crate) fn new(
113        parent: &structured_asn1::ScopedReader<'d, '_>,
114        element: Element<'d>,
115        rules: &Rules<impl Work>,
116    ) -> Result<Self> {
117        let work = parent.execution();
118        let mut nodes = work.vector();
119        let mut frames = work.vector();
120        let (node, entered) = make(parent, element, rules, None)?;
121        nodes
122            .try_push(node)
123            .map_err(|cause| Error::resource(cause, FIELD, element.span().start))?;
124        if let Some((reader, sort)) = entered {
125            frames
126                .try_push(Frame {
127                    reader,
128                    index: 0,
129                    last: None,
130                    sort,
131                    at: element.span().start,
132                })
133                .map_err(|cause| Error::resource(cause, FIELD, element.span().start))?;
134        }
135        while let Some(frame) = frames.last_mut() {
136            work.charge_work(1)
137                .map_err(|cause| Error::resource(cause, FIELD, frame.at))?;
138            if let Some(child) = frame
139                .reader
140                .next_element()
141                .map_err(|cause| Error::encoding(cause, FIELD))?
142            {
143                if matches!(
144                    rules
145                        .items
146                        .get(&Key::from(child.span()), &work)
147                        .map_err(|cause| Error::resource(cause, FIELD, child.span().start))?,
148                    Some(Form::Omit)
149                ) {
150                    continue;
151                }
152                let index = nodes.len();
153                let (node, entered) = make(&frame.reader, child, rules, Some(frame.index))?;
154                nodes
155                    .try_push(node)
156                    .map_err(|cause| Error::resource(cause, FIELD, child.span().start))?;
157                if let Some(previous) = frame.last {
158                    nodes[previous].next = Some(index);
159                } else {
160                    nodes[frame.index].first = Some(index);
161                }
162                frame.last = Some(index);
163                if let Some((reader, sort)) = entered {
164                    frames
165                        .try_push(Frame {
166                            reader,
167                            index,
168                            last: None,
169                            sort,
170                            at: child.span().start,
171                        })
172                        .map_err(|cause| Error::resource(cause, FIELD, child.span().start))?;
173                }
174            } else {
175                let frame = frames.pop().expect("last frame exists");
176                if frame.sort {
177                    sort_children(&mut nodes, frame.index, &work, frame.at)?;
178                }
179                let mut length = 0u64;
180                let mut next = nodes[frame.index].first;
181                while let Some(index) = next {
182                    work.charge_work(1)
183                        .map_err(|cause| Error::resource(cause, FIELD, frame.at))?;
184                    length = length
185                        .checked_add(nodes[index].total)
186                        .ok_or_else(|| Error::schema(ErrorCode::LengthOverflow, FIELD, frame.at))?;
187                    next = nodes[index].next;
188                }
189                finish(&mut nodes[frame.index], length, frame.at)?;
190            }
191        }
192        Ok(Self {
193            nodes: nodes.into_owned(),
194        })
195    }
196    pub(crate) fn len(&self) -> u64 {
197        self.nodes[0].total
198    }
199    pub(crate) fn feed<W: Work, E>(
200        &self,
201        work: &W,
202        mut write: impl FnMut(&[u8]) -> core::result::Result<(), E>,
203    ) -> core::result::Result<(), FeedError<E>> {
204        let mut cursor = Cursor::new(&self.nodes, 0);
205        while let Some(bytes) = cursor.next(work).map_err(FeedError::Resource)? {
206            write(bytes).map_err(FeedError::Callback)?;
207        }
208        Ok(())
209    }
210    pub(crate) fn write<W: Work>(
211        &self,
212        out: &mut impl Receiver,
213        work: &W,
214    ) -> core::result::Result<(), ProjectionError> {
215        let mut cursor = Cursor::new(&self.nodes, 0);
216        while let Some(bytes) = cursor.next(work).map_err(ProjectionError::Resource)? {
217            out.write(bytes).map_err(ProjectionError::Receiver)?;
218        }
219        Ok(())
220    }
221}
222fn finish(node: &mut Node<'_>, length: u64, at: u64) -> Result<()> {
223    node.length = DerLength::new(length);
224    let tag = if node.primitive.is_some() {
225        1
226    } else {
227        node.tag.len() as u64
228    };
229    node.total = length
230        .checked_add(node.length.as_bytes().len() as u64)
231        .and_then(|value| value.checked_add(tag))
232        .ok_or_else(|| Error::schema(ErrorCode::LengthOverflow, FIELD, at))?;
233    Ok(())
234}
235fn identifier<'d>(
236    element: Element<'d>,
237    parent: &structured_asn1::ScopedReader<'_, '_>,
238) -> Result<&'d [u8]> {
239    // Header validity/minimal identifier spelling was already proved by the
240    // parser. This locates the ORIGINAL identifier boundary, never decodes a
241    // second ASN.1 grammar (wide opaque tags remain original).
242    parent
243        .charge(1)
244        .map_err(|cause| Error::encoding(cause, FIELD))?;
245    let bytes = element.as_bytes();
246    if bytes[0] & 31 != 31 {
247        return Ok(&bytes[..1]);
248    }
249    let mut end = 1;
250    loop {
251        parent
252            .charge(1)
253            .map_err(|cause| Error::encoding(cause, FIELD))?;
254        let byte = bytes[end];
255        end += 1;
256        if byte & 128 == 0 {
257            return Ok(&bytes[..end]);
258        }
259    }
260}
261fn make<'d, 'w>(
262    parent: &structured_asn1::ScopedReader<'d, 'w>,
263    element: Element<'d>,
264    rules: &Rules<impl Work>,
265    owner: Option<usize>,
266) -> Result<(
267    Node<'d>,
268    Option<(structured_asn1::ScopedReader<'d, 'w>, bool)>,
269)> {
270    let at = element.span().start;
271    let tag = element.tag();
272    let form = rules
273        .items
274        .get(&Key::from(element.span()), &parent.execution())
275        .map_err(|cause| Error::resource(cause, FIELD, at))?
276        .copied();
277    let mut node = Node {
278        tag: identifier(element, parent)?,
279        primitive: None,
280        length: DerLength::new(0),
281        total: 0,
282        body: Body::Children,
283        parent: owner,
284        first: None,
285        next: None,
286    };
287    let string = matches!(form, Some(Form::Octets))
288        || (tag.class() == Class::Universal
289            && matches!(tag.number(), Some(4 | 12 | 18..=22 | 25..=28 | 30)));
290    if string {
291        let number = tag
292            .number()
293            .expect("known universal or implicit string tag");
294        let octets = Octets::projection_string(
295            parent,
296            &element,
297            number,
298            tag.class() != Class::Universal,
299            FIELD,
300        )?;
301        node.primitive = Some([node.tag[0] & !32]);
302        let length = octets.len() as u64;
303        node.body = Body::String(octets);
304        finish(&mut node, length, at)?;
305        return Ok((node, None));
306    }
307    if tag == Tag::constructed(universal::BIT_STRING) {
308        return Ok((bit_node(parent, element, node)?, None));
309    }
310    if tag.is_constructed() {
311        let reader = parent
312            .enter(&element)
313            .map_err(|cause| Error::encoding(cause, FIELD))?;
314        if tag.class() == Class::Universal && !matches!(tag.number(), Some(8 | 11 | 16 | 17 | 29)) {
315            // A known segmented BIT/time/other open encoding needs its own
316            // canonical codec. Never mistake a constructed value for a
317            // SEQUENCE, or turn unsupported projection into malformed input.
318            structured_pkix::der::conforms(parent, &element, FIELD).map_err(Error::pkix)?;
319            return Err(Error::schema(ErrorCode::UnsupportedEncoding, FIELD, at));
320        }
321        return Ok((
322            node,
323            Some((
324                reader,
325                matches!(form, Some(Form::Set)) || tag == Tag::constructed(universal::SET),
326            )),
327        ));
328    }
329    let mut length = element.contents().len() as u64;
330    node.body = if tag == Tag::primitive(universal::BOOLEAN) {
331        let value = parent
332            .boolean(&element)
333            .map_err(|cause| Error::encoding(cause, FIELD))?;
334        length = 1;
335        Body::Byte(if value { 255 } else { 0 })
336    } else if tag == Tag::primitive(universal::BIT_STRING) {
337        let bits = parent
338            .bit_string(&element)
339            .map_err(|cause| Error::encoding(cause, FIELD))?;
340        if bits.unused_bits() != 0 {
341            let contents = element.contents();
342            Body::Bits {
343                prefix: &contents[..contents.len() - 1],
344                last: contents[contents.len() - 1] & (255 << bits.unused_bits()),
345            }
346        } else {
347            Body::Bytes(element.contents())
348        }
349    } else {
350        let strict = parent
351            .reread(&element, Encoding::Der)
352            .map_err(|cause| Error::encoding(cause, FIELD))?;
353        if let Err(cause) = structured_pkix::der::conforms(&strict, &element, FIELD) {
354            if cause.is_resource() {
355                return Err(Error::pkix(cause));
356            }
357            structured_pkix::der::conforms(parent, &element, FIELD).map_err(Error::pkix)?;
358            return Err(Error::schema(ErrorCode::UnsupportedEncoding, FIELD, at));
359        }
360        Body::Bytes(element.contents())
361    };
362    finish(&mut node, length, at)?;
363    Ok((node, None))
364}
365fn bit_node<'d>(
366    parent: &structured_asn1::ScopedReader<'d, '_>,
367    element: Element<'d>,
368    mut node: Node<'d>,
369) -> Result<Node<'d>> {
370    let at = element.span().start;
371    let work = parent.execution();
372    let mut pieces = work.vector();
373    let mut unused = 0u8;
374    let mut last = None;
375    let mut length = 1u64;
376    structured_pkix::der::bit_fragments(parent, &element, FIELD, |fragment, bits| {
377        let bytes = bits.as_bytes();
378        unused = bits.unused_bits();
379        length = length.checked_add(bytes.len() as u64).ok_or_else(|| {
380            structured_pkix::Error::new(
381                structured_pkix::ErrorCode::MalformedValue,
382                FIELD,
383                fragment.span().start,
384            )
385        })?;
386        let bytes = if unused == 0 {
387            bytes
388        } else {
389            last = Some(bytes[bytes.len() - 1] & (255 << unused));
390            &bytes[..bytes.len() - 1]
391        };
392        pieces
393            .try_push(bytes)
394            .map_err(|cause| structured_pkix::Error::resource(cause, FIELD, fragment.span().start))
395    })
396    .map_err(Error::pkix)?;
397    node.primitive = Some([node.tag[0] & !32]);
398    node.body = Body::Segments {
399        pieces: pieces.into_owned(),
400        unused,
401        last,
402    };
403    finish(&mut node, length, at)?;
404    Ok(node)
405}
406
407pub(crate) enum FeedError<E> {
408    Resource(MemoryError),
409    Callback(E),
410}
411/// Original projection traversal/receiver refusal, without fabricated offsets.
412#[derive(Debug)]
413pub enum ProjectionError {
414    /// Current work refused before traversal.
415    Resource(MemoryError),
416    /// The driver retains the first receiver cause.
417    Receiver(Refused),
418}
419struct Cursor<'n, 'd> {
420    nodes: &'n [Node<'d>],
421    root: usize,
422    index: Option<usize>,
423    phase: u8,
424    piece: usize,
425}
426impl<'n, 'd> Cursor<'n, 'd> {
427    fn new(nodes: &'n [Node<'d>], root: usize) -> Self {
428        Self {
429            nodes,
430            root,
431            index: Some(root),
432            phase: 0,
433            piece: 0,
434        }
435    }
436    fn advance<W: Work>(
437        &mut self,
438        mut index: usize,
439        work: &W,
440    ) -> core::result::Result<(), MemoryError> {
441        loop {
442            work.charge_work(1)?;
443            if index == self.root {
444                self.index = None;
445                return Ok(());
446            }
447            if let Some(next) = self.nodes[index].next {
448                self.index = Some(next);
449                self.phase = 0;
450                self.piece = 0;
451                return Ok(());
452            }
453            index = self.nodes[index].parent.expect("non-root projection child");
454        }
455    }
456    fn next<W: Work>(&mut self, work: &W) -> core::result::Result<Option<&'n [u8]>, MemoryError> {
457        while let Some(index) = self.index {
458            work.charge_work(1)?;
459            let node = &self.nodes[index];
460            match self.phase {
461                0 => {
462                    self.phase = 1;
463                    return Ok(Some(
464                        node.primitive.as_ref().map_or(node.tag, |tag| &tag[..]),
465                    ));
466                }
467                1 => {
468                    self.phase = 2;
469                    return Ok(Some(node.length.as_bytes()));
470                }
471                _ => match &node.body {
472                    Body::Children => {
473                        if let Some(child) = node.first {
474                            self.index = Some(child);
475                            self.phase = 0;
476                            self.piece = 0;
477                        } else {
478                            self.advance(index, work)?;
479                        }
480                    }
481                    Body::Bytes(bytes) => {
482                        self.advance(index, work)?;
483                        return Ok(Some(bytes));
484                    }
485                    Body::Byte(byte) => {
486                        self.advance(index, work)?;
487                        return Ok(Some(core::slice::from_ref(byte)));
488                    }
489                    Body::Bits { prefix, last } => {
490                        if self.piece == 0 {
491                            self.piece = 1;
492                            return Ok(Some(prefix));
493                        }
494                        self.advance(index, work)?;
495                        return Ok(Some(core::slice::from_ref(last)));
496                    }
497                    Body::Segments {
498                        pieces,
499                        unused,
500                        last,
501                    } => {
502                        if self.piece == 0 {
503                            self.piece = 1;
504                            return Ok(Some(core::slice::from_ref(unused)));
505                        }
506                        if let Some(bytes) = pieces.get(self.piece - 1) {
507                            self.piece += 1;
508                            return Ok(Some(bytes));
509                        }
510                        self.advance(index, work)?;
511                        if let Some(last) = last {
512                            return Ok(Some(core::slice::from_ref(last)));
513                        }
514                    }
515                    Body::String(value) => {
516                        if let Some(piece) = value.pieces().get(self.piece) {
517                            self.piece += 1;
518                            return Ok(Some(piece.as_bytes()));
519                        }
520                        self.advance(index, work)?;
521                    }
522                },
523            }
524        }
525        Ok(None)
526    }
527}
528fn nonempty<'n, W: Work>(
529    cursor: &mut Cursor<'n, '_>,
530    work: &W,
531) -> core::result::Result<Option<&'n [u8]>, MemoryError> {
532    loop {
533        match cursor.next(work)? {
534            Some([]) => {}
535            other => return Ok(other),
536        }
537    }
538}
539fn compare<W: Work>(
540    nodes: &[Node<'_>],
541    a: usize,
542    b: usize,
543    work: &W,
544) -> core::result::Result<Ordering, MemoryError> {
545    let mut left = Cursor::new(nodes, a);
546    let mut right = Cursor::new(nodes, b);
547    let mut a = &[][..];
548    let mut b = &[][..];
549    loop {
550        if a.is_empty() {
551            a = match nonempty(&mut left, work)? {
552                Some(bytes) => bytes,
553                None => {
554                    return Ok(if b.is_empty() && nonempty(&mut right, work)?.is_none() {
555                        Ordering::Equal
556                    } else {
557                        Ordering::Less
558                    });
559                }
560            };
561        }
562        if b.is_empty() {
563            b = match nonempty(&mut right, work)? {
564                Some(bytes) => bytes,
565                None => return Ok(Ordering::Greater),
566            };
567        }
568        let length = a.len().min(b.len());
569        work.charge_work(1 + length as u64)?;
570        let order = a[..length].cmp(&b[..length]);
571        if order != Ordering::Equal {
572            return Ok(order);
573        }
574        a = &a[length..];
575        b = &b[length..];
576    }
577}
578fn sort_children<W: Work>(nodes: &mut [Node<'_>], parent: usize, work: &W, at: u64) -> Result<()> {
579    let resource = |cause| Error::resource(cause, FIELD, at);
580    let mut indices = work.vector();
581    let mut next = nodes[parent].first;
582    while let Some(index) = next {
583        indices.try_push(index).map_err(resource)?;
584        next = nodes[index].next;
585    }
586    indices
587        .try_sort_unstable_by(|a, b| compare(nodes, *a, *b, work))
588        .map_err(|cause| match cause {
589            SortError::Resource(cause) | SortError::Comparison(cause) => resource(cause),
590        })?;
591    nodes[parent].first = indices.first().copied();
592    for (ordinal, index) in indices.iter().enumerate() {
593        work.charge_work(1).map_err(resource)?;
594        nodes[*index].next = indices.get(ordinal + 1).copied();
595    }
596    Ok(())
597}
598
599/// Complete original `SignerInfo` fields projected to the DER inputs selected
600/// by TS101733 ยง6.5.1. Payload strings remain original borrowed ranges.
601pub struct DerFields<'d> {
602    values: OwnedVec<Projection<'d>>,
603}
604impl Source for DerFields<'_> {
605    type Error = ProjectionError;
606    fn stream<W: Work>(
607        &mut self,
608        out: &mut impl Receiver,
609        work: &W,
610    ) -> core::result::Result<(), Self::Error> {
611        for value in &self.values {
612            work.charge_work(1).map_err(ProjectionError::Resource)?;
613            value.write(out, work)?;
614        }
615        Ok(())
616    }
617}
618impl<'d> crate::signer::Fields<'d> {
619    /// DER version/SID/digestAlgorithm, with original field depth and execution.
620    pub fn identity_der(
621        &self,
622        parent: &structured_asn1::ScopedReader<'d, '_>,
623    ) -> Result<DerFields<'d>> {
624        self.project(parent, &self.identity, self.identity[2])
625    }
626    /// DER signatureAlgorithm/signature; a constructed signature is flattened
627    /// through the SAME OCTET STRING kernel, without joining its bytes.
628    pub fn signature_der(
629        &self,
630        parent: &structured_asn1::ScopedReader<'d, '_>,
631    ) -> Result<DerFields<'d>> {
632        self.project(parent, &self.signature, self.signature[0])
633    }
634    fn project(
635        &self,
636        parent: &structured_asn1::ScopedReader<'d, '_>,
637        elements: &[Element<'d>],
638        algorithm: Element<'d>,
639    ) -> Result<DerFields<'d>> {
640        parent
641            .charge(1)
642            .map_err(|cause| Error::encoding(cause, FIELD))?;
643        if !core::ptr::eq(self.parser, parent.parser()) || self.depth != parent.depth() {
644            return Err(Error::schema(
645                ErrorCode::InvalidLocation,
646                FIELD,
647                self.identity[0].span().start,
648            ));
649        }
650        let work = parent.execution();
651        let mut rules = Rules::new(&work);
652        let identifier =
653            structured_pkix::algorithm::read(parent, &algorithm, FIELD).map_err(Error::pkix)?;
654        rules.algorithm(identifier, &work)?;
655        if self.identity[1].tag().class() == Class::ContextSpecific {
656            rules.insert(self.identity[1].span(), Form::Octets, &work)?;
657        }
658        let mut values = work.vector();
659        values
660            .try_reserve_exact(elements.len())
661            .map_err(|cause| Error::resource(cause, FIELD, self.identity[0].span().start))?;
662        for element in elements {
663            values
664                .try_push(Projection::new(parent, *element, &rules)?)
665                .map_err(|cause| Error::resource(cause, FIELD, element.span().start))?;
666        }
667        Ok(DerFields {
668            values: values.into_owned(),
669        })
670    }
671}
672
673#[cfg(test)]
674mod tests;