Skip to main content

vest_lib/asn1/
integer.rs

1//! ASN.1 INTEGER values, general contents, and bounded integer formats.
2use crate::core::exec::input::{InputBuf, InputSlice};
3use crate::core::exec::output::*;
4use crate::core::exec::{
5    parser::{PResult, Parser},
6    serializer::{ByteLen, PreSerializeError, Prepare, Serializer},
7    ParseError,
8};
9use crate::primitives::base256::*;
10use crate::{
11    combinators::{
12        mapped::spec::{FnSpecMapper, LosslessMapper, LossyMapper, SpecMapper},
13        I16Be, Mapped, Refined, Tail, I8,
14    },
15    core::{proof::*, spec::*},
16};
17#[cfg(feature = "alloc")]
18use alloc::vec::Vec;
19use vstd::arithmetic::mul::*;
20use vstd::arithmetic::power::*;
21use vstd::arithmetic::power2::*;
22use vstd::assert_seqs_equal;
23use vstd::bits::*;
24use vstd::prelude::*;
25
26verus! {
27
28pub type IntegerInnerFmt = Mapped<Refined<Tail, PredFnSpec<Seq<u8>>>, FnSpecMapper<Seq<u8>, int>>;
29
30pub open spec fn integer_fmt() -> IntegerInnerFmt {
31    Mapped {
32        inner: Refined(Tail, |bytes: Seq<u8>| integer_bytes_wf(bytes)),
33        mapper: (|bytes: Seq<u8>| int_from_be_bytes(bytes), |o: int| int_to_be_bytes(o)),
34    }
35}
36
37pub open spec fn sign_bit_set(b: u8) -> bool {
38    0x80u8 <= b
39}
40
41pub open spec fn invert_byte(b: u8) -> u8 {
42    !b
43}
44
45pub open spec fn invert_bytes(bytes: Seq<u8>) -> Seq<u8> {
46    bytes.map_values(|b: u8| invert_byte(b))
47}
48
49/// 8.3.2 If the contents octets of an integer value encoding consist of more than one octet, then the bits of the first octet and
50/// bit 8 of the second octet:
51///
52/// a) shall not all be ones; and
53/// b) shall not all be zero.
54///
55/// NOTE – These rules ensure that an integer value is always encoded in the smallest possible number of octets.
56pub open spec fn integer_bytes_minimal(bytes: Seq<u8>) -> bool {
57    bytes.len() > 1 ==> {
58        &&& !(bytes[0] == 0x00u8 && !sign_bit_set(bytes[1]))
59        &&& !(bytes[0] == 0xFFu8 && sign_bit_set(bytes[1]))
60    }
61}
62
63pub open spec fn integer_bytes_wf(bytes: Seq<u8>) -> bool {
64    // 8.3.1 The encoding of an integer value shall be primitive. The contents octets shall consist of one or more octets.
65    &&& bytes.len() > 0
66    &&& integer_bytes_minimal(bytes)
67}
68
69pub open spec fn int_from_be_bytes(bytes: Seq<u8>) -> int {
70    let unsigned = nat_from_be_bytes(bytes);
71    if sign_bit_set(bytes[0]) {
72        unsigned as int - pow(256, bytes.len()) as int
73    } else {
74        unsigned as int
75    }
76}
77
78pub open spec fn nonnegative_int_to_bytes(n: nat) -> Seq<u8> {
79    let body = nat_to_be_bytes(n);
80    if sign_bit_set(body[0]) {
81        seq![0x00u8] + body
82    } else {
83        body
84    }
85}
86
87pub open spec fn negative_int_to_bytes(n: nat) -> Seq<u8> {
88    let body = invert_bytes(nat_to_be_bytes(n));
89    if sign_bit_set(body[0]) {
90        body
91    } else {
92        seq![0xFFu8] + body
93    }
94}
95
96pub open spec fn int_to_be_bytes(v: int) -> Seq<u8> {
97    if v >= 0 {
98        nonnegative_int_to_bytes(v as nat)
99    } else {
100        negative_int_to_bytes((-1 - v) as nat)
101    }
102}
103
104pub proof fn lemma_invert_byte_props(b: u8)
105    ensures
106        invert_byte(b) as nat + b as nat == 0xFF,
107        invert_byte(invert_byte(b)) == b,
108        sign_bit_set(invert_byte(b)) <==> !sign_bit_set(b),
109{
110    assert(invert_byte(b) == (0xFFu8 - b)) by (bit_vector);
111    assert(invert_byte(b) as nat + b as nat == 0xFF);
112    assert(invert_byte(invert_byte(b)) == b) by (bit_vector);
113    assert(sign_bit_set(invert_byte(b)) <==> !sign_bit_set(b));
114}
115
116pub proof fn lemma_invert_bytes_involutive(bytes: Seq<u8>)
117    ensures
118        invert_bytes(invert_bytes(bytes)) == bytes,
119{
120    assert_seqs_equal!(invert_bytes(invert_bytes(bytes)) == bytes, i => {
121        lemma_invert_byte_props(bytes[i]);
122    });
123}
124
125pub proof fn lemma_from_be_bytes_invert(bytes: Seq<u8>)
126    ensures
127        nat_from_be_bytes(invert_bytes(bytes)) + nat_from_be_bytes(bytes) + 1 == pow(
128            256,
129            bytes.len(),
130        ),
131    decreases bytes.len(),
132{
133    if bytes.len() == 0 {
134        lemma_pow0(256);
135    } else {
136        let prefix = bytes.drop_last();
137        let last = bytes.last();
138        lemma_from_be_bytes_invert(prefix);
139        prefix.lemma_push_map_commute(|x: u8| invert_byte(x), last);
140        lemma_from_be_bytes_push(invert_bytes(prefix), invert_byte(last));
141        lemma_invert_byte_props(last);
142        lemma_pow256_succ(prefix.len());
143        assert(bytes == prefix.push(last));
144    }
145}
146
147pub proof fn lemma_integer_from_to_bytes(i: Seq<u8>)
148    requires
149        integer_bytes_wf(i),
150    ensures
151        int_to_be_bytes(int_from_be_bytes(i)) == i,
152{
153    if sign_bit_set(i[0]) {
154        let c = invert_bytes(i);
155        lemma_invert_bytes_involutive(i);
156        lemma_from_be_bytes_invert(i);
157        assert((-1 - int_from_be_bytes(i)) as nat == nat_from_be_bytes(invert_bytes(i)));
158        if i.len() > 1 && i[0] == 0xFFu8 {
159            let body = i.drop_first();
160            let c_body = c.drop_first();
161            assert(!sign_bit_set(body[0]));
162            lemma_invert_byte_props(body[0]);
163            lemma_from_be_bytes_prepend(c_body, 0x00u8);
164            lemma_from_to_be_bytes_roundtrip(c_body);
165            lemma_invert_bytes_involutive(body);
166            let first = i[0];
167            assert(first == 0xFFu8);
168            assert(invert_byte(0xFFu8) == 0x00u8) by (bit_vector);
169            assert_seqs_equal!(c == seq![0x00u8] + c_body);
170            assert(i == seq![0xFFu8] + body);
171        } else {
172            if c.len() > 1 {
173                lemma_invert_byte_props(i[0]);
174                assert(c[0] != 0x00u8);
175            }
176            lemma_from_to_be_bytes_roundtrip(c);
177        }
178        lemma_from_be_bytes_upper_bound(i);
179        assert(int_from_be_bytes(i) < 0);
180        assert(int_to_be_bytes(int_from_be_bytes(i)) == i);
181    } else {
182        if i.len() == 1 {
183            lemma_from_be_bytes_singleton(i[0]);
184            assert(i == seq![i[0]]);
185        } else if i[0] == 0x00u8 {
186            let body = i.drop_first();
187            assert(sign_bit_set(body[0]));
188            lemma_from_be_bytes_prepend(body, 0x00u8);
189            lemma_from_to_be_bytes_roundtrip(body);
190            assert(i == seq![0x00u8] + body);
191        } else {
192            lemma_from_to_be_bytes_roundtrip(i);
193        }
194    }
195}
196
197pub proof fn lemma_integer_to_from_bytes(o: int)
198    ensures
199        int_from_be_bytes(int_to_be_bytes(o)) == o,
200        integer_bytes_wf(int_to_be_bytes(o)),
201{
202    if o >= 0 {
203        let n = o as nat;
204        let body = nat_to_be_bytes(n);
205        lemma_to_from_be_bytes_roundtrip(n);
206        if sign_bit_set(body[0]) {
207            lemma_from_be_bytes_prepend(body, 0x00u8);
208        }
209        lemma_to_be_bytes_props(n);
210    } else {
211        let n = (-1 - o) as nat;
212        let unsigned = nat_to_be_bytes(n);
213        let body = invert_bytes(unsigned);
214        lemma_to_from_be_bytes_roundtrip(n);
215        lemma_from_be_bytes_invert(unsigned);
216        if !sign_bit_set(body[0]) {
217            lemma_from_be_bytes_prepend(body, 0xFFu8);
218            lemma_pow256_succ(unsigned.len());
219        }
220        lemma_to_be_bytes_props(n);
221        lemma_invert_byte_props(nat_to_be_bytes(n)[0]);
222    }
223}
224
225pub proof fn lemma_integer_fmt_sound_nonmal_inv()
226    ensures
227        integer_fmt().sound_inv(),
228        integer_fmt().nonmal_inv(),
229{
230    assert forall|v: Seq<u8>| #[trigger] integer_fmt().inner.consistent(v) implies (
231    integer_fmt().mapper.1)((integer_fmt().mapper.0)(v)) == v by {
232        lemma_integer_from_to_bytes(v);
233    }
234}
235
236pub proof fn lemma_integer_fmt_unambiguous()
237    ensures
238        integer_fmt().unambiguous(),
239{
240    assert forall|o: int| #[trigger] integer_fmt().consistent(o) implies (integer_fmt().mapper.0)(
241        (integer_fmt().mapper.1)(o),
242    ) == o by {
243        lemma_integer_to_from_bytes(o);
244    }
245}
246
247#[derive(Copy, Clone)]
248pub struct BigInt<'a> {
249    pub(crate) raw: &'a [u8],
250}
251
252proof fn lemma_large_nonnegative_integer(bytes: Seq<u8>)
253    requires
254        bytes.len() > 8,
255        !sign_bit_set(bytes[0]),
256        bytes[0] == 0 ==> sign_bit_set(bytes[1]),
257    ensures
258        nat_from_be_bytes(bytes) > i64::MAX as int,
259{
260    broadcast use lemma_pow_increases;
261
262    reveal_with_fuel(pow, 9);
263    if bytes[0] != 0 {
264        lemma_from_be_bytes_lower_bound(bytes);
265        lemma_pow_increases(256, 8, (bytes.len() - 1) as nat);
266    } else {
267        let rest = bytes.drop_first();
268        let tail = rest.drop_first();
269        assert(bytes == seq![bytes[0]] + rest);
270        assert(rest == seq![rest[0]] + tail);
271        lemma_from_be_bytes_prepend(rest, 0);
272        lemma_from_be_bytes_prepend(tail, rest[0]);
273        lemma_pow_increases(256, 7, tail.len());
274        let first_int: int = rest[0] as int;
275        let power: int = pow(256, tail.len());
276        lemma_mul_inequality(128, first_int, power);
277    }
278}
279
280pub(crate) proof fn lemma_large_integer_outside_i64(bytes: Seq<u8>)
281    requires
282        bytes.len() > 8,
283        integer_bytes_wf(bytes),
284    ensures
285        sign_bit_set(bytes[0]) ==> int_from_be_bytes(bytes) < i64::MIN as int,
286        !sign_bit_set(bytes[0]) ==> int_from_be_bytes(bytes) > i64::MAX as int,
287{
288    if sign_bit_set(bytes[0]) {
289        let inverted = invert_bytes(bytes);
290        lemma_invert_byte_props(bytes[0]);
291        if inverted[0] == 0 {
292            let first = bytes[0];
293            lemma_invert_byte_props(first);
294            lemma_invert_byte_props(bytes[1]);
295        }
296        lemma_large_nonnegative_integer(inverted);
297        lemma_from_be_bytes_invert(bytes);
298    } else {
299        lemma_large_nonnegative_integer(bytes);
300    }
301}
302
303impl<'a> BigInt<'a> {
304    pub open(crate) spec fn view(&self) -> Seq<u8> {
305        self.raw.deep_view()
306    }
307
308    #[verifier::type_invariant]
309    pub(crate) open(crate) spec fn wf(&self) -> bool {
310        integer_bytes_wf(self.view()) && self.view().len() > 8
311    }
312
313    fn new(raw: &'a [u8]) -> (res: Self)
314        requires
315            integer_bytes_wf(raw.deep_view()),
316            raw.len() > 8,
317        ensures
318            res.view() == raw.deep_view(),
319    {
320        BigInt { raw }
321    }
322
323    pub fn as_slice(&self) -> (res: &'a [u8])
324        ensures
325            res.deep_view() == self.view(),
326    {
327        self.raw
328    }
329
330    /// Returns the sign of an arbitrary-size integer.
331    ///
332    /// `BigInt` values are canonical encodings longer than eight octets, so
333    /// their values are strictly outside the signed 64-bit range.
334    pub fn is_negative(&self) -> (negative: bool)
335        ensures
336            negative ==> int_from_be_bytes(self.view()) < i64::MIN as int,
337            !negative ==> int_from_be_bytes(self.view()) > i64::MAX as int,
338    {
339        proof {
340            use_type_invariant(self);
341            lemma_large_integer_outside_i64(self.view());
342        }
343        self.raw[0] >= 0x80
344    }
345}
346
347pub(crate) proof fn lemma_integer_small_view(value: i64)
348    ensures
349        (Integer::Small { v: value }).deep_view() == value as int,
350{
351}
352
353pub(crate) proof fn lemma_integer_big_view(raw: BigInt<'_>)
354    ensures
355        (Integer::Big { raw }).deep_view() == int_from_be_bytes(raw.view()),
356{
357}
358
359#[derive(Copy, Clone)]
360pub enum Integer<'a> {
361    Small { v: i64 },
362    Big { raw: BigInt<'a> },
363}
364
365impl<'a> DeepView for Integer<'a> {
366    type V = int;
367
368    closed spec fn deep_view(&self) -> Self::V {
369        match *self {
370            Integer::Small { v } => v as int,
371            Integer::Big { raw } => int_from_be_bytes(raw.view()),
372        }
373    }
374}
375
376impl<'a> Integer<'a> {
377    /// Constructs the exact ASN.1 INTEGER value represented by an `i64`.
378    pub const fn from_i64(v: i64) -> Self {
379        Integer::Small { v }
380    }
381
382    /// Returns the value as an `i64` when it is represented by the small variant.
383    pub fn as_i64(&self) -> (value: Option<i64>)
384        ensures
385            value matches Some(v) ==> self.deep_view() == v as int,
386    {
387        match *self {
388            Integer::Small { v } => Some(v),
389            Integer::Big { .. } => None,
390        }
391    }
392
393    /// Tests an inclusive interval whose endpoints are representable as `i64`.
394    pub fn in_i64_range<const HAS_MIN: bool, const MIN: i64, const HAS_MAX: bool, const MAX: i64>(
395        &self,
396    ) -> (ok: bool)
397        ensures
398            ok == ({
399                &&& HAS_MIN ==> MIN as int <= self.deep_view()
400                &&& HAS_MAX ==> self.deep_view() <= MAX as int
401            }),
402    {
403        match *self {
404            Integer::Small { v } => { (!HAS_MIN || MIN <= v) && (!HAS_MAX || v <= MAX) },
405            Integer::Big { raw } => {
406                let negative = raw.is_negative();
407                (!HAS_MIN || !negative) && (!HAS_MAX || negative)
408            },
409        }
410    }
411}
412
413/// ASN.1 INTEGER contents specialized to the `i8` representation.
414///
415/// Every `i8` value has a canonical one-octet two's-complement encoding.
416#[derive(Clone, Copy)]
417pub struct Integer8Fmt;
418
419/// ASN.1 INTEGER contents specialized to the `i16` representation.
420///
421/// Values in the `i8` range use one octet; all other values use two
422/// big-endian octets. Redundant two-octet encodings are rejected.
423#[derive(Clone, Copy)]
424pub struct Integer16Fmt;
425
426#[verifier::allow_in_spec]
427pub fn fits_i8(v: i16) -> bool
428    returns
429        i8::MIN <= v <= i8::MAX,
430{
431    i8::MIN as i16 <= v && v <= i8::MAX as i16
432}
433
434pub(crate) broadcast proof fn lemma_integer8_fmt_byte_len(value: i8)
435    ensures
436        #[trigger] Integer8Fmt.byte_len(value) == 1,
437{
438}
439
440pub(crate) broadcast proof fn lemma_integer16_fmt_byte_len_bound(value: i16)
441    ensures
442        #[trigger] Integer16Fmt.byte_len(value) <= 2,
443{
444}
445
446mod derived_specs {
447    use super::*;
448    use super::super::IntegerFmt;
449
450    impl SpecParser for IntegerFmt {
451        type PVal = int;
452
453        open spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
454            integer_fmt().spec_parse(ibuf)
455        }
456    }
457
458    impl Consistency for IntegerFmt {
459        type Val = int;
460
461        open spec fn consistent(&self, v: Self::Val) -> bool {
462            integer_fmt().consistent(v)
463        }
464    }
465
466    impl SpecSerializerDps for IntegerFmt {
467        type SValue = int;
468
469        open spec fn spec_serialize_dps(&self, v: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
470            integer_fmt().spec_serialize_dps(v, obuf)
471        }
472    }
473
474    impl SpecSerializer for IntegerFmt {
475        type SVal = int;
476
477        open spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
478            integer_fmt().spec_serialize(v)
479        }
480    }
481
482    impl SpecByteLen for IntegerFmt {
483        type T = int;
484
485        open spec fn byte_len(&self, v: Self::T) -> nat {
486            integer_fmt().byte_len(v)
487        }
488    }
489
490    impl SpecParser for Integer8Fmt {
491        type PVal = i8;
492
493        open spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
494            if ibuf.len() == 1 {
495                I8.spec_parse(ibuf)
496            } else {
497                None
498            }
499        }
500    }
501
502    impl Consistency for Integer8Fmt {
503        type Val = i8;
504
505        open spec fn consistent(&self, v: Self::Val) -> bool {
506            true
507        }
508    }
509
510    impl SpecSerializerDps for Integer8Fmt {
511        type SValue = i8;
512
513        open spec fn spec_serialize_dps(&self, v: Self::SValue, _obuf: Seq<u8>) -> Seq<u8> {
514            I8.spec_serialize(v)
515        }
516    }
517
518    impl SpecSerializer for Integer8Fmt {
519        type SVal = i8;
520
521        open spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
522            I8.spec_serialize(v)
523        }
524    }
525
526    impl SpecByteLen for Integer8Fmt {
527        type T = i8;
528
529        open spec fn byte_len(&self, v: Self::T) -> nat {
530            I8.byte_len(v)
531        }
532    }
533
534    impl ValueByteLen for Integer8Fmt {
535        open spec fn value_byte_len(v: Self::T) -> nat {
536            I8.byte_len(v)
537        }
538
539        proof fn lemma_value_len_matches_byte_len(&self, v: Self::T) {
540        }
541    }
542
543    impl SpecParser for Integer16Fmt {
544        type PVal = i16;
545
546        open spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
547            if ibuf.len() == 1 {
548                let (_, v) = I8.spec_parse(ibuf)->0;
549                Some((1, v as i16))
550            } else if ibuf.len() == 2 {
551                match I16Be.spec_parse(ibuf) {
552                    Some((_, v)) if !fits_i8(v) => Some((2, v)),
553                    _ => None,
554                }
555            } else {
556                None
557            }
558        }
559    }
560
561    impl Consistency for Integer16Fmt {
562        type Val = i16;
563
564        open spec fn consistent(&self, v: Self::Val) -> bool {
565            true
566        }
567    }
568
569    impl SpecSerializerDps for Integer16Fmt {
570        type SValue = i16;
571
572        open spec fn spec_serialize_dps(&self, v: Self::SValue, _obuf: Seq<u8>) -> Seq<u8> {
573            if fits_i8(v) {
574                I8.spec_serialize(v as i8)
575            } else {
576                I16Be.spec_serialize(v)
577            }
578        }
579    }
580
581    impl SpecSerializer for Integer16Fmt {
582        type SVal = i16;
583
584        open spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
585            if fits_i8(v) {
586                I8.spec_serialize(v as i8)
587            } else {
588                I16Be.spec_serialize(v)
589            }
590        }
591    }
592
593    impl SpecByteLen for Integer16Fmt {
594        type T = i16;
595
596        open spec fn byte_len(&self, v: Self::T) -> nat {
597            if fits_i8(v) {
598                I8.byte_len(v as i8)
599            } else {
600                I16Be.byte_len(v)
601            }
602        }
603    }
604
605}
606
607mod derived_proofs {
608    use super::*;
609    use super::super::IntegerFmt;
610
611    impl SafeParser for IntegerFmt {
612        proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
613            integer_fmt().lemma_parse_safe(ibuf);
614        }
615    }
616
617    impl Productive for IntegerFmt {
618        proof fn lemma_productive(&self, s: Seq<u8>) {
619            if let Some((n, _)) = integer_fmt().spec_parse(s) {
620                assert(n > 0);
621            }
622        }
623    }
624
625    impl SoundParser for IntegerFmt {
626        proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
627            lemma_integer_fmt_sound_nonmal_inv();
628            integer_fmt().lemma_parse_sound_consumption(ibuf);
629        }
630
631        proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
632            lemma_integer_fmt_sound_nonmal_inv();
633            integer_fmt().lemma_parse_sound_value(ibuf);
634        }
635    }
636
637    impl GoodSerializer for IntegerFmt {
638        proof fn lemma_serialize_len(&self, v: Self::SVal) {
639            integer_fmt().lemma_serialize_len(v);
640        }
641    }
642
643    impl SPRoundTripDps for IntegerFmt {
644        proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
645            lemma_integer_fmt_sound_nonmal_inv();
646            lemma_integer_fmt_unambiguous();
647            integer_fmt().theorem_serialize_dps_parse_roundtrip(v, obuf);
648        }
649    }
650
651    impl NonMalleable for IntegerFmt {
652        proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
653            lemma_integer_fmt_sound_nonmal_inv();
654            integer_fmt().lemma_parse_non_malleable(buf1, buf2);
655        }
656    }
657
658    impl EquivSerializers for IntegerFmt {
659        proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
660            integer_fmt().lemma_serialize_equiv_on_empty(v);
661        }
662    }
663
664    impl SafeParser for Integer8Fmt {
665        proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
666            if ibuf.len() == 1 {
667                I8.lemma_parse_safe(ibuf);
668            }
669        }
670    }
671
672    impl Productive for Integer8Fmt {
673        proof fn lemma_productive(&self, s: Seq<u8>) {
674            if s.len() == 1 {
675                I8.lemma_productive(s);
676            }
677        }
678    }
679
680    impl SoundParser for Integer8Fmt {
681        proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
682            if ibuf.len() == 1 {
683                I8.lemma_parse_sound_consumption(ibuf);
684            }
685        }
686
687        proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
688            if ibuf.len() == 1 {
689                I8.lemma_parse_sound_value(ibuf);
690            }
691        }
692    }
693
694    impl GoodSerializer for Integer8Fmt {
695        proof fn lemma_serialize_len(&self, v: Self::SVal) {
696            I8.lemma_serialize_len(v);
697        }
698    }
699
700    impl SPRoundTripDps for Integer8Fmt {
701        proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
702            I8.theorem_serialize_dps_parse_roundtrip(v, Seq::empty());
703        }
704    }
705
706    impl NonMalleable for Integer8Fmt {
707        proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
708            if buf1.len() == 1 && buf2.len() == 1 {
709                I8.lemma_parse_non_malleable(buf1, buf2);
710            }
711        }
712    }
713
714    impl EquivSerializers for Integer8Fmt {
715        proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
716            I8.lemma_serialize_equiv_on_empty(v);
717        }
718    }
719
720    impl SafeParser for Integer16Fmt {
721        proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
722            if ibuf.len() == 1 {
723                I8.lemma_parse_safe(ibuf);
724            } else if ibuf.len() == 2 {
725                I16Be.lemma_parse_safe(ibuf);
726            }
727        }
728    }
729
730    impl Productive for Integer16Fmt {
731        proof fn lemma_productive(&self, s: Seq<u8>) {
732            if s.len() == 1 {
733                I8.lemma_productive(s);
734            } else if s.len() == 2 {
735                I16Be.lemma_productive(s);
736            }
737        }
738    }
739
740    impl SoundParser for Integer16Fmt {
741        proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
742            if ibuf.len() == 1 {
743                I8.lemma_parse_sound_consumption(ibuf);
744            } else if ibuf.len() == 2 {
745                I16Be.lemma_parse_sound_consumption(ibuf);
746            }
747        }
748
749        proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
750            if ibuf.len() == 1 {
751                I8.lemma_parse_sound_value(ibuf);
752            } else if ibuf.len() == 2 {
753                I16Be.lemma_parse_sound_value(ibuf);
754            }
755        }
756    }
757
758    impl GoodSerializer for Integer16Fmt {
759        proof fn lemma_serialize_len(&self, v: Self::SVal) {
760            if fits_i8(v) {
761                I8.lemma_serialize_len(v as i8);
762            } else {
763                I16Be.lemma_serialize_len(v);
764            }
765        }
766    }
767
768    impl SPRoundTripDps for Integer16Fmt {
769        proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
770            if fits_i8(v) {
771                I8.theorem_serialize_dps_parse_roundtrip(v as i8, Seq::empty());
772            } else {
773                I16Be.theorem_serialize_dps_parse_roundtrip(v, Seq::empty());
774            }
775        }
776    }
777
778    impl NonMalleable for Integer16Fmt {
779        proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
780            if buf1.len() == 1 && buf2.len() == 1 {
781                I8.lemma_parse_non_malleable(buf1, buf2);
782            } else if buf1.len() == 2 && buf2.len() == 2 {
783                I16Be.lemma_parse_non_malleable(buf1, buf2);
784            }
785        }
786    }
787
788    impl EquivSerializers for Integer16Fmt {
789        proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
790            if fits_i8(v) {
791                I8.lemma_serialize_equiv_on_empty(v as i8);
792            } else {
793                I16Be.lemma_serialize_equiv_on_empty(v);
794            }
795        }
796    }
797
798}
799
800impl<'i> Parser<&'i [u8]> for super::IntegerFmt {
801    type PT = Integer<'i>;
802
803    fn parse(&self, ibuf: &&'i [u8]) -> PResult<Self::PT> {
804        let (n, bytes) = Tail.parse(ibuf)?;
805        if bytes.len() == 0 {
806            return Err(ParseError::custom("Empty integer"));
807        }
808        if bytes.len() > 1 {
809            let b0 = bytes[0];
810            let b1 = bytes[1];
811            if b0 == 0x00 && b1 < 0x80 {
812                return Err(ParseError::custom("Non-minimal integer"));
813            }
814            if b0 == 0xFF && b1 >= 0x80 {
815                return Err(ParseError::custom("Non-minimal integer"));
816            }
817        }
818        if bytes.len() <= 8 {
819            Ok((n, Integer::Small { v: i64_from_be_bytes(bytes) }))
820        } else {
821            Ok((n, Integer::Big { raw: BigInt::new(bytes) }))
822        }
823    }
824}
825
826impl<'i> Parser<&'i [u8]> for Integer8Fmt {
827    type PT = i8;
828
829    fn parse(&self, ibuf: &&'i [u8]) -> PResult<Self::PT> {
830        if ibuf.len() == 1 {
831            I8.parse(ibuf)
832        } else {
833            Err(ParseError::custom("Integer out of range for i8"))
834        }
835    }
836}
837
838impl<'i> Parser<&'i [u8]> for Integer16Fmt {
839    type PT = i16;
840
841    fn parse(&self, ibuf: &&'i [u8]) -> PResult<Self::PT> {
842        if ibuf.len() == 1 {
843            let (n, v) = I8.parse(ibuf)?;
844            Ok((n, v as i16))
845        } else if ibuf.len() == 2 {
846            let (n, v) = I16Be.parse(ibuf)?;
847            if fits_i8(v) {
848                Err(ParseError::non_canonical())
849            } else {
850                Ok((n, v))
851            }
852        } else {
853            Err(ParseError::custom("Integer out of range for i16"))
854        }
855    }
856}
857
858impl<Output: OutputBuf> Serializer<Output, i8> for Integer8Fmt {
859    fn serialize_into(&self, v: &i8, obuf: &mut Output) {
860        I8.serialize_into(v, obuf);
861    }
862}
863
864impl Prepare<i8> for Integer8Fmt {
865    fn prepare(&self, v: &i8) -> Result<usize, PreSerializeError> {
866        I8.prepare(v)
867    }
868}
869
870impl ByteLen<i8> for Integer8Fmt {
871    fn length(&self, v: &i8) -> usize {
872        I8.length(v)
873    }
874}
875
876impl<Output: OutputBuf> Serializer<Output, i16> for Integer16Fmt {
877    fn serialize_into(&self, v: &i16, obuf: &mut Output) {
878        if fits_i8(*v) {
879            I8.serialize_into(&(*v as i8), obuf);
880        } else {
881            I16Be.serialize_into(v, obuf);
882        }
883    }
884}
885
886impl Prepare<i16> for Integer16Fmt {
887    fn prepare(&self, v: &i16) -> Result<usize, PreSerializeError> {
888        if fits_i8(*v) {
889            I8.prepare(&(*v as i8))
890        } else {
891            I16Be.prepare(v)
892        }
893    }
894}
895
896impl ByteLen<i16> for Integer16Fmt {
897    fn length(&self, v: &i16) -> usize {
898        if fits_i8(*v) {
899            I8.length(&(*v as i8))
900        } else {
901            I16Be.length(v)
902        }
903    }
904}
905
906impl<Output: OutputBuf, 'i> Serializer<Output, Integer<'i>> for super::IntegerFmt {
907    fn serialize_into(&self, v: &Integer<'i>, obuf: &mut Output) {
908        match v {
909            Integer::Small { v } => {
910                let len = i64_to_be_bytes_len(*v);
911                let mut bytes = [0u8;size_of::<i64>() + 1];
912                let (encoded, _) = bytes.split_at_mut(len);
913                i64_to_be_bytes_in_place(*v, encoded);
914                Tail.serialize_into(&bytes[0..len], obuf);
915            },
916            Integer::Big { raw } => {
917                let bytes = raw.as_slice();
918                proof {
919                    use_type_invariant(raw);
920                    lemma_integer_fmt_sound_nonmal_inv();
921                    lemma_integer_from_to_bytes(bytes.deep_view());
922                }
923                Tail.serialize_into(&bytes, obuf);
924            },
925        }
926    }
927}
928
929impl<'i> Prepare<Integer<'i>> for super::IntegerFmt {
930    fn prepare(&self, v: &Integer<'i>) -> Result<usize, PreSerializeError> {
931        match v {
932            Integer::Small { v } => {
933                let len = i64_to_be_bytes_len(*v);
934                proof {
935                    lemma_integer_to_from_bytes(*v as int);
936                }
937                Ok(len)
938            },
939            Integer::Big { raw } => {
940                let bytes = raw.as_slice();
941                proof {
942                    use_type_invariant(raw);
943                    lemma_integer_fmt_sound_nonmal_inv();
944                    lemma_integer_from_to_bytes(bytes.deep_view());
945                }
946                Tail.prepare(&bytes)
947            },
948        }
949    }
950}
951
952impl<'i> ByteLen<Integer<'i>> for super::IntegerFmt {
953    fn length(&self, v: &Integer<'i>) -> usize {
954        match v {
955            Integer::Small { v } => { i64_to_be_bytes_len(*v) },
956            Integer::Big { raw } => {
957                let bytes = raw.as_slice();
958                proof {
959                    use_type_invariant(raw);
960                    lemma_integer_fmt_sound_nonmal_inv();
961                    lemma_integer_from_to_bytes(bytes.deep_view());
962                }
963                Tail.length(&bytes)
964            },
965        }
966    }
967}
968
969broadcast proof fn lemma_u64_as_i64(u: u64)
970    by (bit_vector)
971    ensures
972        u < 0x8000000000000000 ==> #[trigger] (u as i64) as int == u as int,
973        u >= 0x8000000000000000 ==> #[trigger] (u as i64) as int == u as int - 0x10000000000000000,
974{
975}
976
977/// Executable big-endian two's-complement decoding into `i64`.
978pub fn i64_from_be_bytes(bytes: &[u8]) -> (r: i64)
979    requires
980        usize::BITS == 64,
981        1 <= bytes.len() <= 8,
982    ensures
983        r as int == int_from_be_bytes(bytes.deep_view()),
984{
985    broadcast use {lemma_pow_multiplies, lemma_pow2, lemma_pow_increases};
986    broadcast use lemma_from_be_bytes_upper_bound;
987    broadcast use lemma_u64_as_i64;
988
989    let n = bytes.len();
990    let u = u64_from_be_bytes(bytes);
991
992    let ghost s = bytes.deep_view();
993    let ghost (first, rest) = (s.first(), s.drop_first());
994    let ghost pw = pow(256, (n - 1) as nat);
995    let ghost nfb_rest = nat_from_be_bytes(rest);
996    proof {
997        assert(s == seq![first] + rest);
998        lemma_from_be_bytes_prepend(rest, first);
999        // nat_from_be_bytes(s) == first * pow(256, n-1) + nfb_rest, with nfb_rest < pow(256, n-1)
1000        assert(nat_from_be_bytes(s) == first * pw + nfb_rest);
1001        reveal_with_fuel(pow, 9);
1002    }
1003    if bytes[0] >= 0x80 {
1004        // Sign bit is set: int_from_be_bytes(s) == nat_from_be_bytes(s) - pow(256, n).
1005        if n == 8 {
1006            proof {
1007                // u == nat_from_be_bytes(s) >= first * 2^56 >= 0x80 * 2^56 == 2^63
1008                assert(first * pw + nfb_rest >= 0x8000000000000000) by (nonlinear_arith)
1009                    requires
1010                        first >= 0x80,
1011                        pw == 0x100000000000000,
1012                ;
1013            }
1014            // u >= 2^63, so (u as i64) reinterprets as u - 2^64 == nat_from_be_bytes(s) - pow(256,8).
1015            u as i64
1016        } else {  // n < 8
1017            let shift: u64 = 8 * (n as u64);
1018            proof {
1019                // Establish pow(256, n) == pow2(8n) == 1u64 << (8n)
1020                assert(pow(2, 8) == 256) by (compute_only);
1021                lemma_u64_shl_is_mul(1u64, shift);
1022            }
1023            let sub: u64 = 1u64 << shift;
1024            // u < pow(256, n) == sub <= 2^56, so both fit in i64 and the
1025            // subtraction yields nat_from_be_bytes(s) - pow(256, n).
1026            (u as i64) - (sub as i64)
1027        }
1028    } else {
1029        // Sign bit clear: int_from_be_bytes(s) == nat_from_be_bytes(s), which fits in i64.
1030        proof {
1031            // nat_from_be_bytes(s) < (first + 1) * pow(256, n-1) <= 0x80 * 2^56 == 2^63
1032            assert(nat_from_be_bytes(s) < 0x8000000000000000) by (nonlinear_arith)
1033                requires
1034                    nat_from_be_bytes(s) == first as nat * pw + nfb_rest,
1035                    nfb_rest < pw,
1036                    first < 0x80,
1037                    pw <= 0x100000000000000,
1038            ;
1039        }
1040        u as i64
1041    }
1042}
1043
1044/// Executable allocation-backed big-endian two's-complement encoding from `i64`.
1045///
1046/// ASN.1 serializer implementations use their in-place path instead of this
1047/// convenience helper.
1048#[cfg(feature = "alloc")]
1049pub fn i64_to_be_bytes(v: i64) -> (buf: Vec<u8>)
1050    requires
1051        usize::BITS == 64,
1052    ensures
1053        buf@ == int_to_be_bytes(v as int),
1054{
1055    if v >= 0 {
1056        let mut body = u64_to_be_bytes(v as u64);
1057        if body[0] >= 0x80 {  // sign bit set
1058            body.insert(0, 0x00u8);
1059            body
1060        } else {  // sign bit clear
1061            body
1062        }
1063    } else {
1064        let m: u64 = (-1 - v) as u64;
1065        let mut body = u64_to_be_bytes(m);
1066        // Invert the bytes in place.
1067        let ghost orig = body@;
1068        invert_bytes_in_place(&mut body);
1069        if body[0] >= 0x80 {  // sign bit set
1070            body
1071        } else {  // sign bit clear
1072            body.insert(0, 0xFFu8);
1073            body
1074        }
1075    }
1076}
1077
1078/// Allocation-free length of the minimal big-endian two's-complement encoding.
1079pub fn i64_to_be_bytes_len(v: i64) -> (len: usize)
1080    requires
1081        usize::BITS == 64,
1082    ensures
1083        len == int_to_be_bytes(v as int).len(),
1084        len <= size_of::<i64>() + 1,
1085{
1086    let magnitude = if v >= 0 {
1087        v as u64
1088    } else {
1089        (-1 - v) as u64
1090    };
1091    let body_len = u64_to_be_bytes_len(magnitude);
1092    let first = u64_to_be_bytes_first(magnitude);
1093    proof {
1094        lemma_usize_to_be_bytes_len_bound(magnitude as usize);
1095        assert(body_len <= 8);
1096        if v >= 0 {
1097            assert(magnitude as nat == v as int);
1098        } else {
1099            assert(-1i64 - v >= 0);
1100            assert(magnitude as int == (-1i64 - v) as int);
1101            assert((-1i64 - v) as int == -1 - v as int);
1102            lemma_invert_byte_props(first);
1103        }
1104    }
1105    if first >= 0x80 {
1106        body_len + 1
1107    } else {
1108        body_len
1109    }
1110}
1111
1112/// Inverts every byte of `obuf` in place (bitwise NOT).
1113fn invert_bytes_in_place(obuf: &mut [u8])
1114    ensures
1115        final(obuf)@ == invert_bytes(old(obuf)@),
1116{
1117    let n = obuf.len();
1118    for i in 0..n
1119        invariant
1120            n == obuf.len(),
1121            forall|k: int| 0 <= k < i ==> #[trigger] obuf@[k] == invert_byte(old(obuf)@[k]),
1122            forall|k: int| i <= k < n ==> #[trigger] obuf@[k] == old(obuf)@[k],
1123    {
1124        obuf[i] = !obuf[i];
1125    }
1126    assert(obuf@ =~= invert_bytes(old(obuf)@));
1127}
1128
1129/// Writes the minimal big-endian two's-complement encoding of `v` into an exactly-sized slice.
1130pub fn i64_to_be_bytes_in_place(v: i64, obuf: &mut [u8])
1131    requires
1132        usize::BITS == 64,
1133        old(obuf)@.len() == int_to_be_bytes(v as int).len(),
1134    ensures
1135        final(obuf)@ == int_to_be_bytes(v as int),
1136{
1137    let magnitude = if v >= 0 {
1138        v as u64
1139    } else {
1140        (-1 - v) as u64
1141    };
1142    let first = u64_to_be_bytes_first(magnitude);
1143    proof {
1144        if v < 0 {
1145            lemma_invert_byte_props(first);
1146        }
1147    }
1148    if first >= 0x80 {
1149        let (sign, body) = obuf.split_at_mut(1);
1150        if v >= 0 {
1151            sign[0] = 0x00u8;
1152        } else {
1153            sign[0] = 0xFFu8;
1154        }
1155        usize_to_be_bytes_in_place(magnitude as usize, body);
1156        if v < 0 {
1157            invert_bytes_in_place(body);
1158        }
1159    } else {
1160        usize_to_be_bytes_in_place(magnitude as usize, obuf);
1161        if v < 0 {
1162            invert_bytes_in_place(obuf);
1163        }
1164    }
1165}
1166
1167} // verus!
1168#[cfg(test)]
1169mod tests {
1170    use super::{Integer16Fmt, Integer8Fmt};
1171    use crate::core::exec::{Parser, Prepare, SerializerExt};
1172
1173    #[test]
1174    fn integer8_boundaries_and_noncanonical_lengths() {
1175        for (bytes, expected) in [(&[0x80u8][..], -128i8), (&[0x7fu8][..], 127i8)] {
1176            let (_, value) = Integer8Fmt.parse(&bytes).unwrap();
1177            assert_eq!(value, expected);
1178        }
1179
1180        let two_bytes = &[0x00u8, 0x7f][..];
1181        assert!(Integer8Fmt.parse(&two_bytes).is_err());
1182    }
1183
1184    #[test]
1185    fn integer16_uses_minimal_one_or_two_octets() {
1186        for (value, expected) in [
1187            (-32768i16, &[0x80u8, 0x00][..]),
1188            (-129i16, &[0xffu8, 0x7f][..]),
1189            (-128i16, &[0x80u8][..]),
1190            (127i16, &[0x7fu8][..]),
1191            (128i16, &[0x00u8, 0x80][..]),
1192            (32767i16, &[0x7fu8, 0xff][..]),
1193        ] {
1194            let mut encoded = vec![0; Integer16Fmt.prepare(&value).unwrap()];
1195            Integer16Fmt.serialize(&value, &mut encoded);
1196            assert_eq!(encoded, expected);
1197
1198            let (_, decoded) = Integer16Fmt.parse(&expected).unwrap();
1199            assert_eq!(decoded, value);
1200        }
1201
1202        for bytes in [&[0x00u8, 0x7f][..], &[0xffu8, 0x80][..]] {
1203            assert!(Integer16Fmt.parse(&bytes).is_err());
1204        }
1205    }
1206
1207    #[test]
1208    fn test_integer8_equivalence_with_general_integer() {
1209        use super::Integer;
1210        use crate::asn1::IntegerFmt;
1211
1212        for v in -128..=127 {
1213            // Serialize using Integer8Fmt
1214            let mut enc8 = vec![0; Integer8Fmt.prepare(&v).unwrap()];
1215            Integer8Fmt.serialize(&v, &mut enc8);
1216
1217            // Serialize using general Integer
1218            let val = Integer::Small { v: v as i64 };
1219            let mut enc_gen = vec![0; IntegerFmt.prepare(&val).unwrap()];
1220            IntegerFmt.serialize(&val, &mut enc_gen);
1221
1222            assert_eq!(enc8, enc_gen, "Mismatch serialization at {}", v);
1223
1224            // Parse using Integer8Fmt
1225            let enc8_slice = enc8.as_slice();
1226            let (_, dec8) = Integer8Fmt.parse(&enc8_slice).unwrap();
1227            assert_eq!(dec8, v);
1228
1229            // Parse using general Integer
1230            let (_, dec_gen) = IntegerFmt.parse(&enc8_slice).unwrap();
1231            match dec_gen {
1232                Integer::Small { v: val_i64 } => {
1233                    assert_eq!(val_i64, v as i64);
1234                }
1235                _ => panic!("Expected Small for {}", v),
1236            }
1237        }
1238    }
1239
1240    #[test]
1241    fn test_integer16_equivalence_with_general_integer() {
1242        use super::Integer;
1243        use crate::asn1::IntegerFmt;
1244
1245        for v in -32768..=32767 {
1246            // Serialize using Integer16Fmt
1247            let mut enc16 = vec![0; Integer16Fmt.prepare(&v).unwrap()];
1248            Integer16Fmt.serialize(&v, &mut enc16);
1249
1250            // Serialize using general Integer
1251            let val = Integer::Small { v: v as i64 };
1252            let mut enc_gen = vec![0; IntegerFmt.prepare(&val).unwrap()];
1253            IntegerFmt.serialize(&val, &mut enc_gen);
1254
1255            assert_eq!(enc16, enc_gen, "Mismatch serialization at {}", v);
1256
1257            // Parse using Integer16Fmt
1258            let enc16_slice = enc16.as_slice();
1259            let (_, dec16) = Integer16Fmt.parse(&enc16_slice).unwrap();
1260            assert_eq!(dec16, v);
1261
1262            // Parse using general Integer
1263            let (_, dec_gen) = IntegerFmt.parse(&enc16_slice).unwrap();
1264            match dec_gen {
1265                Integer::Small { v: val_i64 } => {
1266                    assert_eq!(val_i64, v as i64);
1267                }
1268                _ => panic!("Expected Small for {}", v),
1269            }
1270        }
1271    }
1272}