Skip to main content

vest_lib/asn1/
tag.rs

1//! ASN.1 identifier octets, tag classes, and universal tag numbers.
2use crate::combinators::{Bind, Empty, Sum};
3use crate::core::exec::input::*;
4use crate::core::exec::output::*;
5use crate::core::exec::{parser::*, serializer::*, ParseError, ParseErrorKind};
6use crate::primitives::base128::*;
7use crate::{
8    combinators::{
9        implicit::*,
10        mapped::spec::{FnSpecMapper, SpecMap},
11        Mapped, Refined, U8,
12    },
13    core::{proof::*, spec::*},
14};
15#[cfg(feature = "alloc")]
16use alloc::vec;
17use vstd::prelude::*;
18use OutputBuf;
19use Sum::Inl as L;
20use Sum::Inr as R;
21
22#[cfg(verus_only)]
23use vstd::std_specs::convert::FromSpecImpl;
24
25verus! {
26
27/// Bit-mask for the class bits (bits 7–6) of the first tag byte.
28pub const TAG_CLASS_MASK: u8 = 0b1100_0000u8;
29
30/// Bit-mask for the "constructed" bit (bit 5) of the first tag byte.
31pub const TAG_CONSTRUCTED_MASK: u8 = 0b0010_0000u8;
32
33/// Bit-mask for the tag-number bits (bits 4–0) of the first tag byte.
34pub const TAG_NUMBER_MASK: u8 = 0b0001_1111u8;
35
36/// Sentinel value in bits 4–0 signalling the long (high-tag) form.
37pub const TAG_LONG_FORM_SENTINEL: u8 = 0b0001_1111u8;
38
39/// One identifier octet plus the maximum ten base-128 octets needed by a `u64` tag number.
40pub(crate) const TAG_FMT_MAX_BYTE_LEN: usize = 11;
41
42#[derive(StructuralEq, Clone, Copy, PartialEq, Eq, Debug)]
43#[verifier::ext_equal]
44#[repr(u64)]
45pub enum TagNumber {
46    EOC = 0,
47    Boolean = 1,
48    Integer = 2,
49    BitString = 3,
50    OctetString = 4,
51    Null = 5,
52    ObjectIdentifier = 6,
53    Real = 9,
54    Enumerated = 10,
55    Utf8String = 12,
56    RelativeOid = 13,
57    Sequence = 16,
58    Set = 17,
59    NumericString = 18,
60    PrintableString = 19,
61    TeletexString = 20,
62    VideotexString = 21,
63    Ia5String = 22,
64    UtcTime = 23,
65    GeneralizedTime = 24,
66    VisibleString = 26,
67    GeneralString = 27,
68    UniversalString = 28,
69    BmpString = 30,
70    Other { tag_num: UInt },
71}
72
73#[derive(StructuralEq, Clone, Copy, PartialEq, Eq, Debug)]
74#[verifier::ext_equal]
75pub enum Class {
76    Universal,
77    Application,
78    ContextSpecific,
79    Private,
80}
81
82#[derive(StructuralEq, Clone, Copy, PartialEq, Eq, Debug)]
83#[verifier::ext_equal]
84pub struct Tag {
85    pub class: Class,
86    pub constructed: bool,
87    pub number: TagNumber,
88}
89
90pub open spec fn tag_num_to_uint(num: TagNumber) -> UInt {
91    match num {
92        TagNumber::EOC => 0,
93        TagNumber::Boolean => 1,
94        TagNumber::Integer => 2,
95        TagNumber::BitString => 3,
96        TagNumber::OctetString => 4,
97        TagNumber::Null => 5,
98        TagNumber::ObjectIdentifier => 6,
99        TagNumber::Real => 9,
100        TagNumber::Enumerated => 10,
101        TagNumber::Utf8String => 12,
102        TagNumber::RelativeOid => 13,
103        TagNumber::Sequence => 16,
104        TagNumber::Set => 17,
105        TagNumber::NumericString => 18,
106        TagNumber::PrintableString => 19,
107        TagNumber::TeletexString => 20,
108        TagNumber::VideotexString => 21,
109        TagNumber::Ia5String => 22,
110        TagNumber::UtcTime => 23,
111        TagNumber::GeneralizedTime => 24,
112        TagNumber::VisibleString => 26,
113        TagNumber::GeneralString => 27,
114        TagNumber::UniversalString => 28,
115        TagNumber::BmpString => 30,
116        TagNumber::Other { tag_num } => tag_num,
117    }
118}
119
120pub open spec fn uint_to_tag_num(num: UInt) -> TagNumber {
121    match num {
122        0 => TagNumber::EOC,
123        1 => TagNumber::Boolean,
124        2 => TagNumber::Integer,
125        3 => TagNumber::BitString,
126        4 => TagNumber::OctetString,
127        5 => TagNumber::Null,
128        6 => TagNumber::ObjectIdentifier,
129        9 => TagNumber::Real,
130        10 => TagNumber::Enumerated,
131        12 => TagNumber::Utf8String,
132        13 => TagNumber::RelativeOid,
133        16 => TagNumber::Sequence,
134        17 => TagNumber::Set,
135        18 => TagNumber::NumericString,
136        19 => TagNumber::PrintableString,
137        20 => TagNumber::TeletexString,
138        21 => TagNumber::VideotexString,
139        22 => TagNumber::Ia5String,
140        23 => TagNumber::UtcTime,
141        24 => TagNumber::GeneralizedTime,
142        26 => TagNumber::VisibleString,
143        27 => TagNumber::GeneralString,
144        28 => TagNumber::UniversalString,
145        30 => TagNumber::BmpString,
146        other => TagNumber::Other { tag_num: other },
147    }
148}
149
150pub open spec fn tag_number_wf(num: TagNumber) -> bool {
151    num matches TagNumber::Other { tag_num } ==> {
152        &&& !matches!(tag_num, 0 | 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21
153            | 22 | 23 | 24 | 26 | 27 | 28 | 30)
154        &&& nat_to_base128(tag_num as nat).len() <= BASE128_MAX_BYTES
155    }
156}
157
158#[verifier::allow_in_spec]
159pub const fn tag_num_from_uint(num: u64) -> TagNumber
160    returns
161        uint_to_tag_num(num as UInt),
162{
163    match num {
164        0 => TagNumber::EOC,
165        1 => TagNumber::Boolean,
166        2 => TagNumber::Integer,
167        3 => TagNumber::BitString,
168        4 => TagNumber::OctetString,
169        5 => TagNumber::Null,
170        6 => TagNumber::ObjectIdentifier,
171        9 => TagNumber::Real,
172        10 => TagNumber::Enumerated,
173        12 => TagNumber::Utf8String,
174        13 => TagNumber::RelativeOid,
175        16 => TagNumber::Sequence,
176        17 => TagNumber::Set,
177        18 => TagNumber::NumericString,
178        19 => TagNumber::PrintableString,
179        20 => TagNumber::TeletexString,
180        21 => TagNumber::VideotexString,
181        22 => TagNumber::Ia5String,
182        23 => TagNumber::UtcTime,
183        24 => TagNumber::GeneralizedTime,
184        26 => TagNumber::VisibleString,
185        27 => TagNumber::GeneralString,
186        28 => TagNumber::UniversalString,
187        30 => TagNumber::BmpString,
188        other => TagNumber::Other { tag_num: other as UInt },
189    }
190}
191
192/// Return the numeric identifier represented by an ASN.1 tag number.
193///
194/// This is the executable counterpart of the `tag_num_to_uint` specification.
195#[verifier::allow_in_spec]
196pub const fn tag_number_value(number: TagNumber) -> UInt
197    returns
198        tag_num_to_uint(number),
199{
200    match number {
201        TagNumber::EOC => 0,
202        TagNumber::Boolean => 1,
203        TagNumber::Integer => 2,
204        TagNumber::BitString => 3,
205        TagNumber::OctetString => 4,
206        TagNumber::Null => 5,
207        TagNumber::ObjectIdentifier => 6,
208        TagNumber::Real => 9,
209        TagNumber::Enumerated => 10,
210        TagNumber::Utf8String => 12,
211        TagNumber::RelativeOid => 13,
212        TagNumber::Sequence => 16,
213        TagNumber::Set => 17,
214        TagNumber::NumericString => 18,
215        TagNumber::PrintableString => 19,
216        TagNumber::TeletexString => 20,
217        TagNumber::VideotexString => 21,
218        TagNumber::Ia5String => 22,
219        TagNumber::UtcTime => 23,
220        TagNumber::GeneralizedTime => 24,
221        TagNumber::VisibleString => 26,
222        TagNumber::GeneralString => 27,
223        TagNumber::UniversalString => 28,
224        TagNumber::BmpString => 30,
225        TagNumber::Other { tag_num } => tag_num,
226    }
227}
228
229impl DeepView for TagNumber {
230    type V = Self;
231
232    open spec fn deep_view(&self) -> Self::V {
233        *self
234    }
235}
236
237impl DeepView for Class {
238    type V = Self;
239
240    open spec fn deep_view(&self) -> Self::V {
241        *self
242    }
243}
244
245impl DeepView for Tag {
246    type V = Self;
247
248    open spec fn deep_view(&self) -> Self::V {
249        *self
250    }
251}
252
253/// Return the primitive form of a tag identity.
254#[verifier::allow_in_spec]
255pub fn primitive_tag(tag: Tag) -> Tag
256    returns
257        (Tag { class: tag.class, constructed: false, number: tag.number }),
258{
259    Tag { class: tag.class, constructed: false, number: tag.number }
260}
261
262/// Return the constructed form of a tag identity.
263#[verifier::allow_in_spec]
264pub fn constructed_tag(tag: Tag) -> Tag
265    returns
266        (Tag { class: tag.class, constructed: true, number: tag.number }),
267{
268    Tag { class: tag.class, constructed: true, number: tag.number }
269}
270
271pub open spec fn class_of_first_byte(b1: u8) -> Class {
272    match b1 & TAG_CLASS_MASK {
273        0b0000_0000u8 => Class::Universal,
274        0b0100_0000u8 => Class::Application,
275        0b1000_0000u8 => Class::ContextSpecific,
276        _ => Class::Private,
277    }
278}
279
280pub open spec fn class_bits(class: Class) -> u8 {
281    match class {
282        Class::Universal => 0b0000_0000u8,
283        Class::Application => 0b0100_0000u8,
284        Class::ContextSpecific => 0b1000_0000u8,
285        Class::Private => 0b1100_0000u8,
286    }
287}
288
289pub open spec fn constructed_of_first_byte(b1: u8) -> bool {
290    b1 & TAG_CONSTRUCTED_MASK != 0
291}
292
293pub open spec fn constructed_bit(constructed: bool) -> u8 {
294    if constructed {
295        TAG_CONSTRUCTED_MASK
296    } else {
297        0u8
298    }
299}
300
301pub open spec fn first_byte_from_parts(class: Class, constructed: bool, low_bits: u8) -> u8 {
302    class_bits(class) | constructed_bit(constructed) | (low_bits & TAG_NUMBER_MASK)
303}
304
305type TagWireFmt = Bind<U8, spec_fn(u8) -> Sum<Empty, Refined<Base128Fmt<true>, PredFnSpec<UInt>>>>;
306
307type TagInnerFmt = Mapped<TagWireFmt, FnSpecMapper<(u8, Sum<(), UInt>), Tag>>;
308
309#[verusfmt::skip]
310pub(crate) open(crate) spec fn tag_wire() -> TagWireFmt {
311    Bind(U8, |b1: u8| {
312        if b1 & TAG_NUMBER_MASK == TAG_LONG_FORM_SENTINEL {
313            R(Refined(Base128Fmt::<true>, |n: UInt| n >= TAG_LONG_FORM_SENTINEL as UInt))
314        } else {
315            L(Empty)
316        }
317    })
318}
319
320pub(crate) open(crate) spec fn tag_fmt() -> TagInnerFmt {
321    Mapped {
322        inner: tag_wire(),
323        mapper: (
324            |r: (u8, Sum<(), UInt>)|
325                {
326                    let (b1, rest) = r;
327                    let num = match rest {
328                        L(()) => (b1 & TAG_NUMBER_MASK) as UInt,
329                        R(n) => n,
330                    };
331                    Tag {
332                        class: class_of_first_byte(b1),
333                        constructed: constructed_of_first_byte(b1),
334                        number: uint_to_tag_num(num),
335                    }
336                },
337            |tag: Tag|
338                {
339                    let num = tag_num_to_uint(tag.number);
340                    if num < TAG_LONG_FORM_SENTINEL as UInt {
341                        (first_byte_from_parts(tag.class, tag.constructed, num as u8), L(()))
342                    } else {
343                        (
344                            first_byte_from_parts(
345                                tag.class,
346                                tag.constructed,
347                                TAG_LONG_FORM_SENTINEL,
348                            ),
349                            R(num),
350                        )
351                    }
352                },
353        ),
354    }
355}
356
357// ── Bit-vector helpers ────────────────────────────────────────────────────────
358proof fn lemma_class_bits_roundtrip(b1: u8)
359    ensures
360        class_bits(class_of_first_byte(b1)) == (b1 & TAG_CLASS_MASK),
361{
362    let cls = b1 & TAG_CLASS_MASK;
363    assert({
364        ||| cls == 0b0000_0000u8
365        ||| cls == 0b0100_0000u8
366        ||| cls == 0b1000_0000u8
367        ||| cls == 0b1100_0000u8
368    }) by (bit_vector)
369        requires
370            cls == (b1 & TAG_CLASS_MASK),
371    ;
372}
373
374proof fn lemma_class_bits_only_class_mask(class: Class)
375    ensures
376        class_bits(class) & TAG_CONSTRUCTED_MASK == 0u8,
377        class_bits(class) & TAG_NUMBER_MASK == 0u8,
378        class_bits(class) & TAG_CLASS_MASK == class_bits(class),
379{
380    assert(forall|cls: u8|
381        {
382            ||| cls == 0b0000_0000u8
383            ||| cls == 0b0100_0000u8
384            ||| cls == 0b1000_0000u8
385            ||| cls == 0b1100_0000u8
386        } ==> (cls & TAG_CONSTRUCTED_MASK == 0u8 && cls & TAG_NUMBER_MASK == 0u8 && (cls
387            & TAG_CLASS_MASK) == cls)) by (bit_vector);
388}
389
390proof fn lemma_first_byte_from_parts_roundtrip(class: Class, constructed: bool, low_bits: u8)
391    ensures
392        class_of_first_byte(first_byte_from_parts(class, constructed, low_bits)) == class,
393        constructed_of_first_byte(first_byte_from_parts(class, constructed, low_bits))
394            == constructed,
395        first_byte_from_parts(class, constructed, low_bits) & TAG_NUMBER_MASK == low_bits
396            & TAG_NUMBER_MASK,
397{
398    let fb = first_byte_from_parts(class, constructed, low_bits);
399    let cb = class_bits(class);
400    let cbit = constructed_bit(constructed);
401    lemma_class_bits_only_class_mask(class);
402
403    assert(fb & TAG_CLASS_MASK == cb && (fb & TAG_CONSTRUCTED_MASK != 0u8) == constructed && fb
404        & TAG_NUMBER_MASK == low_bits & TAG_NUMBER_MASK) by (bit_vector)
405        requires
406            fb == cb | cbit | (low_bits & TAG_NUMBER_MASK),
407            cb & TAG_CONSTRUCTED_MASK == 0u8,
408            cb & TAG_NUMBER_MASK == 0u8,
409            (cb & TAG_CLASS_MASK) == cb,
410            cbit == TAG_CONSTRUCTED_MASK || cbit == 0u8,
411            constructed ==> cbit == TAG_CONSTRUCTED_MASK,
412            !constructed ==> cbit == 0u8,
413    ;
414}
415
416proof fn lemma_first_byte_roundtrip(b1: u8)
417    ensures
418        first_byte_from_parts(
419            class_of_first_byte(b1),
420            constructed_of_first_byte(b1),
421            b1 & TAG_NUMBER_MASK,
422        ) == b1,
423{
424    lemma_class_bits_roundtrip(b1);
425    let fb = first_byte_from_parts(
426        class_of_first_byte(b1),
427        constructed_of_first_byte(b1),
428        b1 & TAG_NUMBER_MASK,
429    );
430    let cb = class_bits(class_of_first_byte(b1));
431    assert(fb == b1) by (bit_vector)
432        requires
433            fb == cb | constructed_bit(constructed_of_first_byte(b1)) | ((b1 & TAG_NUMBER_MASK)
434                & TAG_NUMBER_MASK),
435            cb == (b1 & TAG_CLASS_MASK),
436    ;
437}
438
439proof fn lemma_tag_fmt_sound_nonmal_inv()
440    ensures
441        tag_fmt().sound_inv(),
442        tag_fmt().nonmal_inv(),
443{
444    let fmt = tag_fmt();
445    assert forall|v| fmt.inner.consistent(v) implies (fmt.mapper.1)((fmt.mapper.0)(v)) == v by {
446        let (b1, rest) = v;
447        lemma_first_byte_roundtrip(b1);
448        if b1 & TAG_NUMBER_MASK == TAG_LONG_FORM_SENTINEL {
449        } else {
450            let num = (b1 & TAG_NUMBER_MASK) as UInt;
451            assert(num < TAG_LONG_FORM_SENTINEL as UInt) by (bit_vector)
452                requires
453                    (b1 & TAG_NUMBER_MASK) != TAG_LONG_FORM_SENTINEL,
454                    num == (b1 & TAG_NUMBER_MASK) as UInt,
455            ;
456        }
457    }
458}
459
460proof fn lemma_tag_fmt_unambiguous(tag: Tag)
461    requires
462        tag_fmt().consistent(tag),
463        tag_number_wf(tag.number),
464    ensures
465        (tag_fmt().mapper.0)((tag_fmt().mapper.1)(tag)) == tag,
466{
467    let num = tag_num_to_uint(tag.number);
468    if num < TAG_LONG_FORM_SENTINEL as UInt {
469        let low = num as u8;
470        lemma_first_byte_from_parts_roundtrip(tag.class, tag.constructed, low);
471        assert(low & TAG_NUMBER_MASK == low) by (bit_vector)
472            requires
473                low == num as u8,
474                num < TAG_LONG_FORM_SENTINEL as UInt,
475        ;
476    } else {
477        lemma_first_byte_from_parts_roundtrip(tag.class, tag.constructed, TAG_LONG_FORM_SENTINEL);
478    }
479}
480
481proof fn lemma_tag_wf_implies_tag_fmt_consistent(tag: Tag)
482    requires
483        tag_number_wf(tag.number),
484    ensures
485        tag_fmt().consistent(tag),
486{
487    let num = tag_num_to_uint(tag.number);
488    assert(TAG_LONG_FORM_SENTINEL & TAG_NUMBER_MASK == TAG_LONG_FORM_SENTINEL) by (bit_vector);
489    if num < TAG_LONG_FORM_SENTINEL as UInt {
490        let low = num as u8;
491        lemma_first_byte_from_parts_roundtrip(tag.class, tag.constructed, low);
492        assert(low & TAG_NUMBER_MASK == low) by (bit_vector)
493            requires
494                low == num as u8,
495                num < TAG_LONG_FORM_SENTINEL as UInt,
496        ;
497    } else {
498        lemma_first_byte_from_parts_roundtrip(tag.class, tag.constructed, TAG_LONG_FORM_SENTINEL);
499        lemma_base128_fmt_consistent::<true>(num);
500    }
501}
502
503/// Exposes tag-format consistency to external combinator proofs without revealing
504/// the implementation of [`TagFmt`](super::TagFmt).
505pub broadcast proof fn lemma_tag_wf_implies_tag_consistent(tag: Tag)
506    requires
507        tag_number_wf(tag.number),
508    ensures
509        #[trigger] super::TagFmt.consistent(tag),
510{
511}
512
513pub(crate) broadcast proof fn lemma_tag_fmt_byte_len_bound(tag: Tag)
514    ensures
515        #[trigger] super::TagFmt.byte_len(tag) <= TAG_FMT_MAX_BYTE_LEN,
516{
517    let num = tag_num_to_uint(tag.number);
518    lemma_to_base128_len_bounds();
519    lemma_base128_fmt_byte_len::<true>(num);
520}
521
522mod derived_specs {
523    use super::*;
524    use super::super::TagFmt;
525
526    impl SpecParser for TagFmt {
527        type PVal = Tag;
528
529        open(crate) spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
530            tag_fmt().spec_parse(ibuf)
531        }
532    }
533
534    impl Consistency for TagFmt {
535        type Val = Tag;
536
537        open(crate) spec fn consistent(&self, v: Self::Val) -> bool {
538            tag_number_wf(v.number)
539        }
540    }
541
542    impl SpecSerializerDps for TagFmt {
543        type SValue = Tag;
544
545        open(crate) spec fn spec_serialize_dps(&self, v: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
546            tag_fmt().spec_serialize_dps(v, obuf)
547        }
548    }
549
550    impl SpecSerializer for TagFmt {
551        type SVal = Tag;
552
553        open(crate) spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
554            tag_fmt().spec_serialize(v)
555        }
556    }
557
558    impl SpecByteLen for TagFmt {
559        type T = Tag;
560
561        open(crate) spec fn byte_len(&self, v: Self::T) -> nat {
562            tag_fmt().byte_len(v)
563        }
564    }
565
566}
567
568mod derived_proofs {
569    use super::*;
570    use super::super::TagFmt;
571
572    impl SafeParser for TagFmt {
573        proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
574            tag_fmt().lemma_parse_safe(ibuf);
575        }
576    }
577
578    impl Productive for TagFmt {
579        proof fn lemma_productive(&self, s: Seq<u8>) {
580            tag_fmt().lemma_productive(s);
581        }
582    }
583
584    impl SoundParser for TagFmt {
585        proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
586            lemma_tag_fmt_sound_nonmal_inv();
587            tag_fmt().lemma_parse_sound_consumption(ibuf);
588        }
589
590        proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
591            lemma_tag_fmt_sound_nonmal_inv();
592            tag_fmt().lemma_parse_sound_value(ibuf);
593        }
594    }
595
596    impl NonTailFmt for TagFmt {
597        proof fn lemma_serialize_dps_prepend(&self, v: Self::SValue, obuf: Seq<u8>) {
598            tag_fmt().lemma_serialize_dps_prepend(v, obuf);
599        }
600
601        proof fn lemma_serialize_dps_len(&self, v: Self::SValue, obuf: Seq<u8>) {
602            tag_fmt().lemma_serialize_dps_len(v, obuf);
603        }
604    }
605
606    impl GoodSerializer for TagFmt {
607        proof fn lemma_serialize_len(&self, v: Self::SVal) {
608            tag_fmt().lemma_serialize_len(v);
609        }
610    }
611
612    impl SPRoundTripDps for TagFmt {
613        proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
614            lemma_tag_wf_implies_tag_fmt_consistent(v);
615            lemma_tag_fmt_unambiguous(v);
616            tag_fmt().inner.theorem_serialize_dps_parse_roundtrip(tag_fmt().mapper.1(v), obuf);
617        }
618    }
619
620    impl NoLookAhead for TagFmt {
621        proof fn lemma_no_lookahead(&self, i1: Seq<u8>, i2: Seq<u8>) {
622            tag_fmt().lemma_no_lookahead(i1, i2);
623        }
624    }
625
626    impl NonMalleable for TagFmt {
627        proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
628            lemma_tag_fmt_sound_nonmal_inv();
629            tag_fmt().lemma_parse_non_malleable(buf1, buf2);
630        }
631    }
632
633    impl EquivSerializersGeneral for TagFmt {
634        proof fn lemma_serialize_equiv(&self, v: Self::SVal, obuf: Seq<u8>) {
635            tag_fmt().lemma_serialize_equiv(v, obuf);
636        }
637    }
638
639    impl EquivSerializers for TagFmt {
640        proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
641            tag_fmt().lemma_serialize_equiv_on_empty(v);
642        }
643    }
644
645}
646
647impl Parser<&[u8]> for super::TagFmt {
648    type PT = Tag;
649
650    fn parse(&self, ibuf: &&[u8]) -> PResult<Self::PT> {
651        broadcast use crate::core::spec::SafeParser::lemma_parse_safe;
652        broadcast use crate::core::spec::SoundParser::lemma_parse_sound_value;
653
654        let _ = ibuf.len();
655
656        let (n1, b1): (usize, u8) = U8.parse(ibuf)?;
657        let rest = ibuf.skip(n1);
658
659        let (n2, num) = if b1 & TAG_NUMBER_MASK == TAG_LONG_FORM_SENTINEL {
660            let (n2, num) = Base128Fmt::<true>.parse(&rest)?;
661            if num < TAG_LONG_FORM_SENTINEL as UInt {
662                return Err(ParseError::non_canonical());
663            }
664            (n2, num)
665        } else {
666            (0, (b1 & TAG_NUMBER_MASK) as UInt)
667        };
668
669        let class = match b1 & TAG_CLASS_MASK {
670            0b0000_0000u8 => Class::Universal,
671            0b0100_0000u8 => Class::Application,
672            0b1000_0000u8 => Class::ContextSpecific,
673            _ => Class::Private,
674        };
675        let constructed = b1 & TAG_CONSTRUCTED_MASK != 0;
676        let number = match num {
677            0 => TagNumber::EOC,
678            1 => TagNumber::Boolean,
679            2 => TagNumber::Integer,
680            3 => TagNumber::BitString,
681            4 => TagNumber::OctetString,
682            5 => TagNumber::Null,
683            6 => TagNumber::ObjectIdentifier,
684            9 => TagNumber::Real,
685            10 => TagNumber::Enumerated,
686            12 => TagNumber::Utf8String,
687            13 => TagNumber::RelativeOid,
688            16 => TagNumber::Sequence,
689            17 => TagNumber::Set,
690            18 => TagNumber::NumericString,
691            19 => TagNumber::PrintableString,
692            20 => TagNumber::TeletexString,
693            21 => TagNumber::VideotexString,
694            22 => TagNumber::Ia5String,
695            23 => TagNumber::UtcTime,
696            24 => TagNumber::GeneralizedTime,
697            26 => TagNumber::VisibleString,
698            27 => TagNumber::GeneralString,
699            28 => TagNumber::UniversalString,
700            30 => TagNumber::BmpString,
701            other => TagNumber::Other { tag_num: other },
702        };
703
704        Ok((n1 + n2, Tag { class, constructed, number }))
705    }
706}
707
708impl<Output: OutputBuf> Serializer<Output, Tag> for super::TagFmt {
709    fn serialize_into(&self, v: &Tag, obuf: &mut Output) {
710        broadcast use crate::core::exec::output::outbuf_lemmas;
711
712        let num = tag_number_value(v.number);
713
714        let class_bits = match v.class {
715            Class::Universal => 0b0000_0000u8,
716            Class::Application => 0b0100_0000u8,
717            Class::ContextSpecific => 0b1000_0000u8,
718            Class::Private => 0b1100_0000u8,
719        };
720        let constructed_bit = if v.constructed {
721            TAG_CONSTRUCTED_MASK
722        } else {
723            0u8
724        };
725        proof {
726            lemma_tag_wf_implies_tag_fmt_consistent(*v);
727        }
728        if num < TAG_LONG_FORM_SENTINEL as UInt {
729            let low = num as u8;
730            let b1 = class_bits | constructed_bit | (low & TAG_NUMBER_MASK);
731            U8.serialize_into(&b1, obuf);
732        } else {
733            let b1 = class_bits | constructed_bit | TAG_LONG_FORM_SENTINEL & TAG_NUMBER_MASK;
734            U8.serialize_into(&b1, obuf);
735            Base128Fmt::<true>.serialize_into(&num, obuf);
736        }
737    }
738}
739
740impl Prepare<Tag> for super::TagFmt {
741    fn prepare(&self, v: &Tag) -> Result<usize, PreSerializeError> {
742        if let TagNumber::Other { tag_num } = v.number {
743            if matches!(tag_num, 0 | 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21
744                | 22 | 23 | 24 | 26 | 27 | 28 | 30) {
745                return Err(PreSerializeError::custom("Invalid tag number"));
746            }
747        }
748        let num = tag_number_value(v.number);
749
750        proof {
751            lemma_to_base128_len_bounds();
752            lemma_base128_fmt_byte_len::<true>(num);
753        }
754        let nbytes = Base128Fmt::<true>.length(&num);
755        if nbytes > BASE128_MAX_BYTES {
756            return Err(PreSerializeError::length_too_large());
757        }
758        proof {
759            assert(tag_number_wf(v.deep_view().number));
760            lemma_tag_wf_implies_tag_fmt_consistent(v.deep_view());
761        }
762
763        if num < TAG_LONG_FORM_SENTINEL as UInt {
764            Ok(1)
765        } else {
766            Ok(1 + nbytes)
767        }
768    }
769}
770
771impl ByteLen<Tag> for super::TagFmt {
772    fn length(&self, v: &Tag) -> usize {
773        let num = tag_number_value(v.number);
774
775        proof {
776            lemma_to_base128_len_bounds();
777            lemma_base128_fmt_byte_len::<true>(num);
778            lemma_first_byte_from_parts_roundtrip(v.class, v.constructed, TAG_LONG_FORM_SENTINEL);
779            assert(TAG_LONG_FORM_SENTINEL & TAG_NUMBER_MASK == TAG_LONG_FORM_SENTINEL)
780                by (bit_vector);
781        }
782        let nbytes = Base128Fmt::<true>.length(&num);
783
784        if num < TAG_LONG_FORM_SENTINEL as UInt {
785            1
786        } else {
787            1 + nbytes
788        }
789    }
790}
791
792impl super::TagFmt {
793    pub const EOC: Tag = Tag {
794        class: Class::Universal,
795        constructed: false,
796        number: TagNumber::EOC,
797    };
798
799    pub const BOOLEAN: Tag = Tag {
800        class: Class::Universal,
801        constructed: false,
802        number: TagNumber::Boolean,
803    };
804
805    pub const INTEGER: Tag = Tag {
806        class: Class::Universal,
807        constructed: false,
808        number: TagNumber::Integer,
809    };
810
811    pub const NULL: Tag = Tag {
812        class: Class::Universal,
813        constructed: false,
814        number: TagNumber::Null,
815    };
816
817    pub const OBJECT_IDENTIFIER: Tag = Tag {
818        class: Class::Universal,
819        constructed: false,
820        number: TagNumber::ObjectIdentifier,
821    };
822
823    pub const REAL: Tag = Tag {
824        class: Class::Universal,
825        constructed: false,
826        number: TagNumber::Real,
827    };
828
829    pub const ENUMERATED: Tag = Tag {
830        class: Class::Universal,
831        constructed: false,
832        number: TagNumber::Enumerated,
833    };
834
835    pub const RELATIVE_OID: Tag = Tag {
836        class: Class::Universal,
837        constructed: false,
838        number: TagNumber::RelativeOid,
839    };
840
841    pub const BIT_STRING: Tag = Tag {
842        class: Class::Universal,
843        constructed: false,
844        number: TagNumber::BitString,
845    };
846
847    pub const OCTET_STRING: Tag = Tag {
848        class: Class::Universal,
849        constructed: false,
850        number: TagNumber::OctetString,
851    };
852
853    pub const UTF8_STRING: Tag = Tag {
854        class: Class::Universal,
855        constructed: false,
856        number: TagNumber::Utf8String,
857    };
858
859    pub const NUMERIC_STRING: Tag = Tag {
860        class: Class::Universal,
861        constructed: false,
862        number: TagNumber::NumericString,
863    };
864
865    pub const PRINTABLE_STRING: Tag = Tag {
866        class: Class::Universal,
867        constructed: false,
868        number: TagNumber::PrintableString,
869    };
870
871    pub const TELETEX_STRING: Tag = Tag {
872        class: Class::Universal,
873        constructed: false,
874        number: TagNumber::TeletexString,
875    };
876
877    pub const VIDEOTEX_STRING: Tag = Tag {
878        class: Class::Universal,
879        constructed: false,
880        number: TagNumber::VideotexString,
881    };
882
883    pub const IA5_STRING: Tag = Tag {
884        class: Class::Universal,
885        constructed: false,
886        number: TagNumber::Ia5String,
887    };
888
889    pub const UTC_TIME: Tag = Tag {
890        class: Class::Universal,
891        constructed: false,
892        number: TagNumber::UtcTime,
893    };
894
895    pub const GENERALIZED_TIME: Tag = Tag {
896        class: Class::Universal,
897        constructed: false,
898        number: TagNumber::GeneralizedTime,
899    };
900
901    pub const VISIBLE_STRING: Tag = Tag {
902        class: Class::Universal,
903        constructed: false,
904        number: TagNumber::VisibleString,
905    };
906
907    pub const GENERAL_STRING: Tag = Tag {
908        class: Class::Universal,
909        constructed: false,
910        number: TagNumber::GeneralString,
911    };
912
913    pub const UNIVERSAL_STRING: Tag = Tag {
914        class: Class::Universal,
915        constructed: false,
916        number: TagNumber::UniversalString,
917    };
918
919    pub const BMP_STRING: Tag = Tag {
920        class: Class::Universal,
921        constructed: false,
922        number: TagNumber::BmpString,
923    };
924
925    pub const BIT_STRING_CONSTRUCTED: Tag = Tag {
926        class: Class::Universal,
927        constructed: true,
928        number: TagNumber::BitString,
929    };
930
931    pub const OCTET_STRING_CONSTRUCTED: Tag = Tag {
932        class: Class::Universal,
933        constructed: true,
934        number: TagNumber::OctetString,
935    };
936
937    pub const UTF8_STRING_CONSTRUCTED: Tag = Tag {
938        class: Class::Universal,
939        constructed: true,
940        number: TagNumber::Utf8String,
941    };
942
943    pub const NUMERIC_STRING_CONSTRUCTED: Tag = Tag {
944        class: Class::Universal,
945        constructed: true,
946        number: TagNumber::NumericString,
947    };
948
949    pub const PRINTABLE_STRING_CONSTRUCTED: Tag = Tag {
950        class: Class::Universal,
951        constructed: true,
952        number: TagNumber::PrintableString,
953    };
954
955    pub const TELETEX_STRING_CONSTRUCTED: Tag = Tag {
956        class: Class::Universal,
957        constructed: true,
958        number: TagNumber::TeletexString,
959    };
960
961    pub const VIDEOTEX_STRING_CONSTRUCTED: Tag = Tag {
962        class: Class::Universal,
963        constructed: true,
964        number: TagNumber::VideotexString,
965    };
966
967    pub const IA5_STRING_CONSTRUCTED: Tag = Tag {
968        class: Class::Universal,
969        constructed: true,
970        number: TagNumber::Ia5String,
971    };
972
973    pub const UTC_TIME_CONSTRUCTED: Tag = Tag {
974        class: Class::Universal,
975        constructed: true,
976        number: TagNumber::UtcTime,
977    };
978
979    pub const GENERALIZED_TIME_CONSTRUCTED: Tag = Tag {
980        class: Class::Universal,
981        constructed: true,
982        number: TagNumber::GeneralizedTime,
983    };
984
985    pub const VISIBLE_STRING_CONSTRUCTED: Tag = Tag {
986        class: Class::Universal,
987        constructed: true,
988        number: TagNumber::VisibleString,
989    };
990
991    pub const GENERAL_STRING_CONSTRUCTED: Tag = Tag {
992        class: Class::Universal,
993        constructed: true,
994        number: TagNumber::GeneralString,
995    };
996
997    pub const UNIVERSAL_STRING_CONSTRUCTED: Tag = Tag {
998        class: Class::Universal,
999        constructed: true,
1000        number: TagNumber::UniversalString,
1001    };
1002
1003    pub const BMP_STRING_CONSTRUCTED: Tag = Tag {
1004        class: Class::Universal,
1005        constructed: true,
1006        number: TagNumber::BmpString,
1007    };
1008
1009    pub const SEQUENCE: Tag = Tag {
1010        class: Class::Universal,
1011        constructed: true,
1012        number: TagNumber::Sequence,
1013    };
1014
1015    pub const SET: Tag = Tag { class: Class::Universal, constructed: true, number: TagNumber::Set };
1016}
1017
1018use crate::combinators::Const;
1019
1020pub broadcast proof fn lemma_const_tag_fmt_exec_inv(fmt: Const<super::TagFmt, Tag>)
1021    ensures
1022        #![all_triggers]
1023        <_ as Parser<&[u8]>>::exec_inv(&fmt),
1024        <_ as Prepare<Tag>>::exec_inv(&fmt),
1025{
1026    crate::core::exec::bridge_lemmas::lemma_const_parser_exec_inv::<&[u8], _, _>(&fmt);
1027    crate::core::exec::bridge_lemmas::lemma_const_prepare_exec_inv::<_, _>(&fmt);
1028}
1029
1030} // verus!
1031/*
1032*
1033some test functions
1034*/
1035verus! {
1036
1037#[cfg(feature = "alloc")]
1038fn test_exec_const_fmt(buf: &&[u8]) -> PResult<u16> {
1039    use crate::combinators::U16Be;
1040    let const_u16_fmt = Const(U16Be, 0x1234u16);
1041    let (n, v) = const_u16_fmt.parse(buf)?;
1042    if let Ok(len) = const_u16_fmt.prepare(&v) {
1043        let mut obuf = vec![0; len];
1044        const_u16_fmt.serialize(&v, &mut obuf);
1045        proof {
1046            const_u16_fmt.theorem_parse_serialize_roundtrip(buf@);
1047            assert(obuf@ == buf@.take(n as int));
1048        }
1049    }
1050    Err(ParseError::custom("Test function, not meant to succeed"))
1051}
1052
1053#[cfg(feature = "alloc")]
1054fn test_exec_tag_fmt(buf: &&[u8]) -> PResult<Tag> {
1055    broadcast use lemma_const_tag_fmt_exec_inv;
1056
1057    let asn_bool_tag_fmt = Const(super::TagFmt, super::TagFmt::BOOLEAN);
1058    let (n, tag) = asn_bool_tag_fmt.parse(buf)?;
1059    if let Ok(len) = asn_bool_tag_fmt.prepare(&tag) {
1060        let mut obuf = vec![0; len];
1061        asn_bool_tag_fmt.serialize(&tag, &mut obuf);
1062
1063        proof {
1064            asn_bool_tag_fmt.theorem_parse_serialize_roundtrip(buf@);
1065            assert(obuf@ == buf@.take(n as int));
1066        }
1067    }
1068    Err(ParseError::custom("Test function, not meant to succeed"))
1069}
1070
1071} // verus!
1072/*
1073// somehow needed for regular `cargo check/build/test`
1074 *
1075 */
1076#[cfg(not(verus_keep_ghost))]
1077unsafe impl Structural for TagNumber {}
1078#[cfg(not(verus_keep_ghost))]
1079unsafe impl Structural for Class {}
1080#[cfg(not(verus_keep_ghost))]
1081unsafe impl Structural for Tag {}