1use 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::{
10 combinators::{
11 mapped::spec::{LosslessMapper, LossyMapper, SpecMapper},
12 Mapped, Refined, Tail,
13 },
14 core::{proof::*, spec::*},
15};
16use vstd::assert_seqs_equal;
17use vstd::prelude::*;
18use OutputBuf;
19
20use super::datetime::*;
21
22verus! {
23
24#[verifier::ext_equal]
28pub struct GeneralizedTimeSpec {
29 pub datetime: DateTime,
30 pub precision: TimePrecision,
31 pub fraction: Seq<u8>,
32 pub zone: TimeZone,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct GeneralizedTime<'a> {
37 pub datetime: DateTime,
38 pub precision: TimePrecision,
39 pub fraction: &'a [u8],
40 pub zone: TimeZone,
41}
42
43impl<'a> DeepView for GeneralizedTime<'a> {
44 type V = GeneralizedTimeSpec;
45
46 closed spec fn deep_view(&self) -> Self::V {
47 GeneralizedTimeSpec {
48 datetime: self.datetime,
49 precision: self.precision,
50 fraction: self.fraction@,
51 zone: self.zone,
52 }
53 }
54}
55
56impl GeneralizedTimeSpec {
57 pub open spec fn wf(&self) -> bool {
60 &&& datetime_wf(self.datetime)
61 &&& self.datetime.year <= 9999
62 &&& digits(self.fraction, 0, self.fraction.len() as int)
63 &&& (self.precision == TimePrecision::Hour || self.precision == TimePrecision::Minute
64 || self.precision == TimePrecision::Second)
65 &&& (self.precision != TimePrecision::Hour || (self.datetime.minute == 0
66 && self.datetime.second == 0))
67 &&& (self.precision != TimePrecision::Minute || self.datetime.second == 0)
68 }
69
70 pub open spec fn der_wf(&self) -> bool {
75 &&& self.wf()
76 &&& self.zone == TimeZone::Utc
77 &&& self.precision == TimePrecision::Second
78 &&& (self.fraction.len() == 0 || self.fraction.last() != 0x30)
79 }
80}
81
82impl<'a> GeneralizedTime<'a> {
83 pub fn new(
84 datetime: DateTime,
85 precision: TimePrecision,
86 fraction: &'a [u8],
87 zone: TimeZone,
88 ) -> (value: Self)
89 requires
90 (GeneralizedTimeSpec { datetime, precision, fraction: fraction@, zone }).wf(),
91 ensures
92 value.deep_view() == (GeneralizedTimeSpec {
93 datetime,
94 precision,
95 fraction: fraction@,
96 zone,
97 }),
98 {
99 GeneralizedTime { datetime, precision, fraction, zone }
100 }
101
102 pub fn fraction(&self) -> (fraction: &'a [u8])
103 ensures
104 fraction.deep_view() == self.deep_view().fraction,
105 {
106 self.fraction
107 }
108}
109
110pub open spec fn generalized_fields_wf(bytes: Seq<u8>, main_end: usize) -> bool {
112 &&& (main_end == 10 || main_end == 12 || main_end == 14)
113 &&& digits(bytes, 0, main_end as int)
114 &&& datetime_wf(
115 DateTime {
116 year: decimal4(bytes, 0),
117 month: decimal2(bytes, 4),
118 day: decimal2(bytes, 6),
119 hour: decimal2(bytes, 8),
120 minute: if main_end >= 12 {
121 decimal2(bytes, 10)
122 } else {
123 0
124 },
125 second: if main_end == 14 {
126 decimal2(bytes, 12)
127 } else {
128 0
129 },
130 },
131 )
132}
133
134pub fn generalized_fields_valid(bytes: &[u8], main_end: usize) -> bool
136 requires
137 main_end <= bytes.len(),
138 returns
139 generalized_fields_wf(bytes@, main_end),
140{
141 if main_end != 10 && main_end != 12 && main_end != 14 {
142 return false;
143 }
144 if !is_digits(bytes, 0, main_end) {
145 return false;
146 }
147 let value = DateTime {
148 year: decimal_4(bytes, 0),
149 month: decimal_2(bytes, 4),
150 day: decimal_2(bytes, 6),
151 hour: decimal_2(bytes, 8),
152 minute: if main_end >= 12 {
153 decimal_2(bytes, 10)
154 } else {
155 0
156 },
157 second: if main_end == 14 {
158 decimal_2(bytes, 12)
159 } else {
160 0
161 },
162 };
163 datetime_wf(value)
164}
165
166pub open spec fn generalized_fraction_wf<const DER: bool>(
172 bytes: Seq<u8>,
173 main_end: usize,
174 zone_start: usize,
175) -> bool {
176 if zone_start == main_end {
177 true
178 } else {
179 &&& main_end + 1 < zone_start
180 &&& (bytes[main_end as int] == 0x2e || (!DER && bytes[main_end as int] == 0x2c))
181 &&& digits(bytes, main_end as int + 1, zone_start as int)
182 &&& (!DER || bytes[zone_start as int - 1] != 0x30)
183 }
184}
185
186pub fn generalized_fraction_valid<const DER: bool>(
188 bytes: &[u8],
189 main_end: usize,
190 zone_start: usize,
191) -> bool
192 requires
193 main_end <= zone_start <= bytes.len(),
194 returns
195 generalized_fraction_wf::<DER>(bytes@, main_end, zone_start),
196{
197 if zone_start == main_end {
198 return true;
199 }
200 if main_end + 1 >= zone_start {
201 return false;
202 }
203 if bytes[main_end] != 0x2e && (DER || bytes[main_end] != 0x2c) {
204 return false;
205 }
206 is_digits(bytes, main_end + 1, zone_start) && (!DER || bytes[zone_start - 1] != 0x30)
207}
208
209pub open spec fn generalized_zone_wf<const DER: bool>(bytes: Seq<u8>, zone_start: usize) -> bool {
213 if DER {
214 zone_start + 1 == bytes.len() && bytes[zone_start as int] == 0x5a
215 } else {
216 ||| zone_start == bytes.len()
217 ||| zone_start + 1 == bytes.len() && bytes[zone_start as int] == 0x5a
218 ||| zone_start + 3 == bytes.len() && (bytes[zone_start as int] == 0x2b
219 || bytes[zone_start as int] == 0x2d) && digits(
220 bytes,
221 zone_start as int + 1,
222 zone_start as int + 3,
223 ) && decimal2(bytes, (zone_start as int + 1) as usize) <= 23
224 ||| zone_start + 5 == bytes.len() && (bytes[zone_start as int] == 0x2b
225 || bytes[zone_start as int] == 0x2d) && digits(
226 bytes,
227 zone_start as int + 1,
228 zone_start as int + 5,
229 ) && decimal2(bytes, (zone_start as int + 1) as usize) <= 23 && decimal2(
230 bytes,
231 (zone_start as int + 3) as usize,
232 ) <= 59
233 }
234}
235
236pub fn generalized_zone_valid<const DER: bool>(bytes: &[u8], zone_start: usize) -> bool
238 requires
239 zone_start <= bytes.len(),
240 returns
241 generalized_zone_wf::<DER>(bytes@, zone_start),
242{
243 if DER {
244 return bytes.len() - zone_start == 1 && bytes[zone_start] == 0x5a;
245 }
246 if zone_start == bytes.len() {
247 return true;
248 }
249 if bytes.len() - zone_start == 1 {
250 return bytes[zone_start] == 0x5a;
251 }
252 if bytes[zone_start] != 0x2b && bytes[zone_start] != 0x2d {
253 return false;
254 }
255 if bytes.len() - zone_start == 3 {
256 return is_digits(bytes, zone_start + 1, zone_start + 3) && decimal_2(bytes, zone_start + 1)
257 <= 23;
258 }
259 bytes.len() - zone_start == 5 && is_digits(bytes, zone_start + 1, zone_start + 5) && decimal_2(
260 bytes,
261 zone_start + 1,
262 ) <= 23 && decimal_2(bytes, zone_start + 3) <= 59
263}
264
265pub open spec fn generalized_candidate_wf<const DER: bool>(
266 bytes: Seq<u8>,
267 main_end: usize,
268 zone_start: usize,
269) -> bool {
270 &&& main_end <= zone_start <= bytes.len()
271 &&& generalized_fields_wf(bytes, main_end)
272 &&& generalized_fraction_wf::<DER>(bytes, main_end, zone_start)
273 &&& generalized_zone_wf::<DER>(bytes, zone_start)
274}
275
276pub fn generalized_candidate_valid<const DER: bool>(
278 bytes: &[u8],
279 main_end: usize,
280 zone_start: usize,
281) -> bool
282 requires
283 zone_start <= bytes.len(),
284 returns
285 generalized_candidate_wf::<DER>(bytes@, main_end, zone_start),
286{
287 main_end <= zone_start && generalized_fields_valid(bytes, main_end)
288 && generalized_fraction_valid::<DER>(bytes, main_end, zone_start)
289 && generalized_zone_valid::<DER>(bytes, zone_start)
290}
291
292pub open spec fn generalized_time_bytes_wf<const DER: bool>(bytes: Seq<u8>) -> bool {
296 let zone_start = generalized_zone_start(bytes);
297 if DER {
298 generalized_candidate_wf::<true>(bytes, 14, zone_start)
299 } else {
300 ||| generalized_candidate_wf::<false>(bytes, 10, zone_start)
301 ||| generalized_candidate_wf::<false>(bytes, 12, zone_start)
302 ||| generalized_candidate_wf::<false>(bytes, 14, zone_start)
303 }
304}
305
306pub fn generalized_time_bytes_valid<const DER: bool>(bytes: &[u8]) -> bool
308 returns
309 generalized_time_bytes_wf::<DER>(bytes@),
310{
311 let zone_start = generalized_zonestart(bytes);
312 if DER {
313 generalized_candidate_valid::<true>(bytes, 14, zone_start)
314 } else {
315 generalized_candidate_valid::<false>(bytes, 10, zone_start)
316 || generalized_candidate_valid::<false>(bytes, 12, zone_start)
317 || generalized_candidate_valid::<false>(bytes, 14, zone_start)
318 }
319}
320
321pub open spec fn generalized_zone_start(bytes: Seq<u8>) -> usize {
324 let len = bytes.len();
325 if len > 0 && bytes[len - 1] == 0x5a {
326 (len - 1) as usize
327 } else if len >= 3 && (bytes[len - 3] == 0x2b || bytes[len - 3] == 0x2d) {
328 (len - 3) as usize
329 } else if len >= 5 && (bytes[len - 5] == 0x2b || bytes[len - 5] == 0x2d) {
330 (len - 5) as usize
331 } else {
332 len as usize
333 }
334}
335
336pub fn generalized_zonestart(bytes: &[u8]) -> (zone_start: usize)
338 ensures
339 zone_start <= bytes.len(),
340 returns
341 generalized_zone_start(bytes@),
342{
343 let len = bytes.len();
344 if len > 0 && bytes[len - 1] == 0x5a {
345 len - 1
346 } else if len >= 3 && (bytes[len - 3] == 0x2b || bytes[len - 3] == 0x2d) {
347 len - 3
348 } else if len >= 5 && (bytes[len - 5] == 0x2b || bytes[len - 5] == 0x2d) {
349 len - 5
350 } else {
351 len
352 }
353}
354
355pub open spec fn generalized_main_end(bytes: Seq<u8>, zone_start: usize) -> usize {
357 if generalized_candidate_wf::<false>(bytes, 10, zone_start) {
358 10
359 } else if generalized_candidate_wf::<false>(bytes, 12, zone_start) {
360 12
361 } else {
362 14
363 }
364}
365
366pub fn generalized_mainend(bytes: &[u8], zone_start: usize) -> usize
368 requires
369 generalized_time_bytes_wf::<false>(bytes@),
370 zone_start == generalized_zone_start(bytes@),
371 returns
372 generalized_main_end(bytes@, zone_start),
373{
374 if generalized_candidate_valid::<false>(bytes, 10, zone_start) {
375 10
376 } else if generalized_candidate_valid::<false>(bytes, 12, zone_start) {
377 12
378 } else {
379 14
380 }
381}
382
383pub open spec fn generalized_time_value(bytes: Seq<u8>) -> Option<GeneralizedTimeSpec> {
386 let zone_start = generalized_zone_start(bytes);
387 let main_end = generalized_main_end(bytes, zone_start);
388 let local = DateTime {
389 year: decimal4(bytes, 0),
390 month: decimal2(bytes, 4),
391 day: decimal2(bytes, 6),
392 hour: decimal2(bytes, 8),
393 minute: if main_end >= 12 {
394 decimal2(bytes, 10)
395 } else {
396 0
397 },
398 second: if main_end == 14 {
399 decimal2(bytes, 12)
400 } else {
401 0
402 },
403 };
404 let precision = if main_end == 10 {
405 TimePrecision::Hour
406 } else if main_end == 12 {
407 TimePrecision::Minute
408 } else {
409 TimePrecision::Second
410 };
411 let fraction = if main_end == zone_start {
412 Seq::empty()
413 } else {
414 bytes.subrange(main_end as int + 1, zone_start as int)
415 };
416 if zone_start == bytes.len() || bytes[zone_start as int] == 0x5a {
417 Some(
418 GeneralizedTimeSpec {
419 datetime: local,
420 precision,
421 fraction,
422 zone: if zone_start == bytes.len() {
423 TimeZone::Local
424 } else {
425 TimeZone::Utc
426 },
427 },
428 )
429 } else {
430 let offset_minute = if bytes.len() - zone_start == 5 {
431 decimal2(bytes, (zone_start as int + 3) as usize)
432 } else {
433 0
434 };
435 match normalize_offset(
436 local,
437 bytes[zone_start as int] == 0x2b,
438 decimal2(bytes, (zone_start as int + 1) as usize),
439 offset_minute,
440 ) {
441 Some(datetime) => Some(
442 GeneralizedTimeSpec { datetime, precision, fraction, zone: TimeZone::Utc },
443 ),
444 None => None,
445 }
446 }
447}
448
449#[verifier::rlimit(20)]
452pub fn generalized_timevalue<'a>(bytes: &'a [u8]) -> (res: Option<GeneralizedTime<'a>>)
453 requires
454 generalized_time_bytes_wf::<false>(bytes@),
455 ensures
456 res.deep_view() == generalized_time_value(bytes@),
457{
458 let zone_start = generalized_zonestart(bytes);
459 let main_end = generalized_mainend(bytes, zone_start);
460 let local = DateTime {
461 year: decimal_4(bytes, 0),
462 month: decimal_2(bytes, 4),
463 day: decimal_2(bytes, 6),
464 hour: decimal_2(bytes, 8),
465 minute: if main_end >= 12 {
466 decimal_2(bytes, 10)
467 } else {
468 0
469 },
470 second: if main_end == 14 {
471 decimal_2(bytes, 12)
472 } else {
473 0
474 },
475 };
476 let precision = if main_end == 10 {
477 TimePrecision::Hour
478 } else if main_end == 12 {
479 TimePrecision::Minute
480 } else {
481 TimePrecision::Second
482 };
483 let fraction = if main_end == zone_start {
484 &bytes[main_end..main_end]
485 } else {
486 &bytes[main_end + 1..zone_start]
487 };
488 if zone_start == bytes.len() || bytes[zone_start] == 0x5a {
489 Some(
490 GeneralizedTime {
491 datetime: local,
492 precision,
493 fraction,
494 zone: if zone_start == bytes.len() {
495 TimeZone::Local
496 } else {
497 TimeZone::Utc
498 },
499 },
500 )
501 } else {
502 let offset_minute = if bytes.len() - zone_start == 5 {
503 decimal_2(bytes, zone_start + 3)
504 } else {
505 0
506 };
507 match normalize_offset(
508 local,
509 bytes[zone_start] == 0x2b,
510 decimal_2(bytes, zone_start + 1),
511 offset_minute,
512 ) {
513 Some(datetime) => Some(
514 GeneralizedTime { datetime, precision, fraction, zone: TimeZone::Utc },
515 ),
516 None => None,
517 }
518 }
519}
520
521pub open spec fn generalized_time_wf<const DER: bool>(bytes: Seq<u8>) -> bool {
523 &&& generalized_time_bytes_wf::<DER>(bytes)
524 &&& generalized_time_value(bytes) matches Some(value) ==> value.wf()
525 &&& generalized_time_value(bytes).is_some()
526 &&& bytes.len() <= usize::MAX
527}
528
529pub fn generalized_time_valid<const DER: bool>(bytes: &[u8]) -> bool
531 returns
532 generalized_time_wf::<DER>(bytes@),
533{
534 assert(bytes@.len() == bytes.len());
535 assert(bytes@.len() <= usize::MAX);
536 if !generalized_time_bytes_valid::<DER>(bytes) {
537 return false;
538 }
539 match generalized_timevalue(bytes) {
540 Some(value) => generalized_value_wf::<false>(&value),
541 None => false,
542 }
543}
544
545pub open spec fn generalized_time_prefix(value: GeneralizedTimeSpec) -> Seq<u8> {
546 decimal4_bytes(value.datetime.year)@ + decimal2_bytes(value.datetime.month)@ + decimal2_bytes(
547 value.datetime.day,
548 )@ + decimal2_bytes(value.datetime.hour)@ + if value.precision == TimePrecision::Hour {
549 Seq::empty()
550 } else {
551 decimal2_bytes(value.datetime.minute)@ + if value.precision == TimePrecision::Second {
552 decimal2_bytes(value.datetime.second)@
553 } else {
554 Seq::empty()
555 }
556 }
557}
558
559pub open spec fn generalized_time_fraction(value: GeneralizedTimeSpec) -> Seq<u8> {
560 if value.fraction.len() == 0 {
561 Seq::empty()
562 } else {
563 seq![0x2eu8] + value.fraction
564 }
565}
566
567pub open spec fn generalized_time_suffix(value: GeneralizedTimeSpec) -> Seq<u8> {
568 if value.zone == TimeZone::Utc {
569 seq![0x5au8]
570 } else {
571 Seq::empty()
572 }
573}
574
575pub open spec fn generalized_time_bytes(value: GeneralizedTimeSpec) -> Seq<u8> {
576 generalized_time_prefix(value) + generalized_time_fraction(value) + generalized_time_suffix(
577 value,
578 )
579}
580
581pub(crate) fn generalized_time_der_prefix_bytes<'a>(value: &GeneralizedTime<'a>) -> (bytes:
582 [u8; 14])
583 requires
584 value.deep_view().der_wf(),
585 ensures
586 bytes@ == generalized_time_prefix(value.deep_view()),
587{
588 let year = decimal4_bytes(value.datetime.year);
589 let month = decimal2_bytes(value.datetime.month);
590 let day = decimal2_bytes(value.datetime.day);
591 let hour = decimal2_bytes(value.datetime.hour);
592 let minute = decimal2_bytes(value.datetime.minute);
593 let second = decimal2_bytes(value.datetime.second);
594 [
595 year[0],
596 year[1],
597 year[2],
598 year[3],
599 month[0],
600 month[1],
601 day[0],
602 day[1],
603 hour[0],
604 hour[1],
605 minute[0],
606 minute[1],
607 second[0],
608 second[1],
609 ]
610}
611
612pub fn generalized_time_to_bytes<'a, Output: OutputBuf>(
614 value: &GeneralizedTime<'a>,
615 obuf: &mut Output,
616)
617 requires
618 value.deep_view().wf(),
619 old(obuf).fits(generalized_time_bytes(value.deep_view()).len()),
620 ensures
621 final(obuf)@ == old(obuf)@ + generalized_time_bytes(value.deep_view()),
622 forall|n|
623 old(obuf).fits(generalized_time_bytes(value.deep_view()).len() + n)
624 <==> final(obuf).fits(n),
625 old(obuf).same_destination(final(obuf)),
626{
627 broadcast use crate::core::exec::output::outbuf_lemmas;
628
629 let year = decimal4_bytes(value.datetime.year);
630 let month = decimal2_bytes(value.datetime.month);
631 let day = decimal2_bytes(value.datetime.day);
632 let hour = decimal2_bytes(value.datetime.hour);
633 obuf.write_bytes(&year);
634 obuf.write_bytes(&month);
635 obuf.write_bytes(&day);
636 obuf.write_bytes(&hour);
637 if value.precision != TimePrecision::Hour {
638 let minute = decimal2_bytes(value.datetime.minute);
639 obuf.write_bytes(&minute);
640 if value.precision == TimePrecision::Second {
641 let second = decimal2_bytes(value.datetime.second);
642 obuf.write_bytes(&second);
643 }
644 }
645 if value.fraction.len() > 0 {
646 obuf.write_byte(0x2e);
647 obuf.write_bytes(value.fraction);
648 }
649 if value.zone == TimeZone::Utc {
650 obuf.write_byte(0x5a);
651 }
652}
653
654#[verifier::allow_in_spec]
656pub fn generalized_value_wf<'a, const DER: bool>(value: &GeneralizedTime<'a>) -> bool
657 returns
658 if DER {
659 value.deep_view().der_wf()
660 } else {
661 value.deep_view().wf()
662 },
663{
664 let base = datetime_wf(value.datetime) && value.datetime.year <= 9999 && is_digits(
665 value.fraction,
666 0,
667 value.fraction.len(),
668 ) && (value.precision == TimePrecision::Hour || value.precision == TimePrecision::Minute
669 || value.precision == TimePrecision::Second) && (value.precision != TimePrecision::Hour || (
670 value.datetime.minute == 0 && value.datetime.second == 0)) && (value.precision
671 != TimePrecision::Minute || value.datetime.second == 0);
672 base && (!DER || (value.zone == TimeZone::Utc && value.precision == TimePrecision::Second && (
673 value.fraction.len() == 0 || value.fraction[value.fraction.len() - 1] != 0x30)))
674}
675
676pub open spec fn generalized_time_len(value: GeneralizedTimeSpec) -> nat {
677 10 as nat + if value.precision == TimePrecision::Hour {
678 0 as nat
679 } else {
680 2 as nat
681 } + if value.precision == TimePrecision::Second {
682 2 as nat
683 } else {
684 0 as nat
685 } + if value.fraction.len() == 0 {
686 0 as nat
687 } else {
688 1 + value.fraction.len()
689 } + if value.zone == TimeZone::Utc {
690 1 as nat
691 } else {
692 0 as nat
693 }
694}
695
696spec fn generalized_time_main_end(value: GeneralizedTimeSpec) -> usize {
697 if value.precision == TimePrecision::Hour {
698 10
699 } else if value.precision == TimePrecision::Minute {
700 12
701 } else {
702 14
703 }
704}
705
706spec fn generalized_time_zone_start(value: GeneralizedTimeSpec) -> usize {
707 (generalized_time_main_end(value) + generalized_time_fraction(value).len()) as usize
708}
709
710#[verifier::rlimit(20)]
712proof fn lemma_generalized_time_encoded_prefix(value: GeneralizedTimeSpec)
713 requires
714 value.wf(),
715 generalized_time_len(value) <= usize::MAX,
716 ensures
717 generalized_time_prefix(value).len() == generalized_time_main_end(value),
718 generalized_fields_wf(generalized_time_bytes(value), generalized_time_main_end(value)),
719 decimal4(generalized_time_bytes(value), 0) == value.datetime.year,
720 decimal2(generalized_time_bytes(value), 4) == value.datetime.month,
721 decimal2(generalized_time_bytes(value), 6) == value.datetime.day,
722 decimal2(generalized_time_bytes(value), 8) == value.datetime.hour,
723 value.precision != TimePrecision::Hour ==> decimal2(generalized_time_bytes(value), 10)
724 == value.datetime.minute,
725 value.precision == TimePrecision::Second ==> decimal2(generalized_time_bytes(value), 12)
726 == value.datetime.second,
727{
728 lemma_decimal4_roundtrip(value.datetime.year);
729 lemma_decimal2_roundtrip(value.datetime.month);
730 lemma_decimal2_roundtrip(value.datetime.day);
731 lemma_decimal2_roundtrip(value.datetime.hour);
732 lemma_decimal2_roundtrip(value.datetime.minute);
733 lemma_decimal2_roundtrip(value.datetime.second);
734}
735
736#[verifier::rlimit(20)]
737proof fn lemma_generalized_time_encoded_fraction<const DER: bool>(value: GeneralizedTimeSpec)
738 requires
739 if DER {
740 value.der_wf()
741 } else {
742 value.wf()
743 },
744 generalized_time_len(value) <= usize::MAX,
745 ensures
746 generalized_fraction_wf::<DER>(
747 generalized_time_bytes(value),
748 generalized_time_main_end(value),
749 generalized_time_zone_start(value),
750 ),
751 if generalized_time_main_end(value) == generalized_time_zone_start(value) {
752 value.fraction.len() == 0
753 } else {
754 generalized_time_bytes(value).subrange(
755 generalized_time_main_end(value) as int + 1,
756 generalized_time_zone_start(value) as int,
757 ) == value.fraction
758 },
759{
760 let prefix = generalized_time_prefix(value);
761 let fraction = generalized_time_fraction(value);
762 let suffix = generalized_time_suffix(value);
763 let bytes = prefix + fraction + suffix;
764 let main_end = generalized_time_main_end(value);
765 let zone_start: usize = (main_end + fraction.len()) as usize;
766
767 if value.fraction.len() == 0 {
768 } else {
769 assert(fraction == seq![0x2eu8] + value.fraction);
770 assert_seqs_equal!(bytes.subrange(main_end as int + 1, zone_start as int) == value.fraction, i => {
771 assert(bytes[main_end as int + 1 + i] == fraction[1 + i]);
772 assert(fraction[1 + i] == value.fraction[i]);
773 });
774 assert(digits(bytes, main_end as int + 1, zone_start as int)) by {
775 assert forall|i: int| main_end <= i < zone_start - 1 implies ascii_digit(
776 #[trigger] bytes[i + 1],
777 ) by {
778 assert(bytes[i + 1] == value.fraction[i - main_end]);
779 }
780 }
781 assert(generalized_fraction_wf::<DER>(bytes, main_end, zone_start));
782 }
783}
784
785#[verifier::rlimit(20)]
786proof fn lemma_generalized_time_encoded_zone<const DER: bool>(value: GeneralizedTimeSpec)
787 requires
788 if DER {
789 value.der_wf()
790 } else {
791 value.wf()
792 },
793 generalized_time_len(value) <= usize::MAX,
794 ensures
795 generalized_zone_start(generalized_time_bytes(value)) == generalized_time_zone_start(value),
796 generalized_zone_wf::<DER>(
797 generalized_time_bytes(value),
798 generalized_time_zone_start(value),
799 ),
800 (generalized_time_zone_start(value) == generalized_time_bytes(value).len()) <==> value.zone
801 == TimeZone::Local,
802 value.zone == TimeZone::Utc ==> generalized_time_bytes(value)[generalized_time_zone_start(
803 value,
804 ) as int] == 0x5a,
805{
806}
807
808#[verifier::rlimit(20)]
809proof fn lemma_generalized_time_encoded_layout<const DER: bool>(value: GeneralizedTimeSpec)
810 requires
811 if DER {
812 value.der_wf()
813 } else {
814 value.wf()
815 },
816 generalized_time_len(value) <= usize::MAX,
817 ensures
818 generalized_time_bytes_wf::<DER>(generalized_time_bytes(value)),
819 generalized_main_end(generalized_time_bytes(value), generalized_time_zone_start(value))
820 == generalized_time_main_end(value),
821{
822 lemma_generalized_time_encoded_prefix(value);
823 lemma_generalized_time_encoded_fraction::<DER>(value);
824 lemma_generalized_time_encoded_zone::<DER>(value);
825}
826
827#[verifier::rlimit(20)]
828pub proof fn lemma_generalized_time_encode_roundtrip<const DER: bool>(value: GeneralizedTimeSpec)
829 requires
830 if DER {
831 value.der_wf()
832 } else {
833 value.wf()
834 },
835 generalized_time_len(value) <= usize::MAX,
836 ensures
837 generalized_time_wf::<DER>(generalized_time_bytes(value)),
838 generalized_time_value(generalized_time_bytes(value)) == Some(value),
839 generalized_time_bytes(value).len() == generalized_time_len(value),
840{
841 lemma_generalized_time_encoded_layout::<DER>(value);
842 lemma_generalized_time_encoded_prefix(value);
843 lemma_generalized_time_encoded_fraction::<DER>(value);
844 lemma_generalized_time_encoded_zone::<DER>(value);
845}
846
847#[verifier::rlimit(100)]
848pub proof fn lemma_der_generalized_time_canonical(bytes: Seq<u8>)
849 requires
850 generalized_time_wf::<true>(bytes),
851 ensures
852 generalized_time_bytes(generalized_time_value(bytes).unwrap()) == bytes,
853{
854 lemma_decimal4_canonical(bytes, 0);
855 lemma_decimal2_canonical(bytes, 4);
856 lemma_decimal2_canonical(bytes, 6);
857 lemma_decimal2_canonical(bytes, 8);
858 lemma_decimal2_canonical(bytes, 10);
859 lemma_decimal2_canonical(bytes, 12);
860}
861
862mod derived_specs {
863 use super::*;
864
865 impl<const DER: bool> SpecParser for super::super::GeneralizedTimeFmt<DER> {
866 type PVal = GeneralizedTimeSpec;
867
868 open spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
869 if generalized_time_wf::<DER>(ibuf) {
870 Some((ibuf.len() as int, generalized_time_value(ibuf).unwrap()))
871 } else {
872 None
873 }
874 }
875 }
876
877 impl<const DER: bool> Consistency for super::super::GeneralizedTimeFmt<DER> {
878 type Val = GeneralizedTimeSpec;
879
880 open spec fn consistent(&self, value: Self::Val) -> bool {
881 (if DER {
882 value.der_wf()
883 } else {
884 value.wf()
885 }) && generalized_time_len(value) <= usize::MAX
886 }
887 }
888
889 impl<const DER: bool> SpecSerializerDps for super::super::GeneralizedTimeFmt<DER> {
890 type SValue = GeneralizedTimeSpec;
891
892 open spec fn spec_serialize_dps(&self, value: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
893 generalized_time_bytes(value)
894 }
895 }
896
897 impl<const DER: bool> SpecSerializer for super::super::GeneralizedTimeFmt<DER> {
898 type SVal = GeneralizedTimeSpec;
899
900 open spec fn spec_serialize(&self, value: Self::SVal) -> Seq<u8> {
901 generalized_time_bytes(value)
902 }
903 }
904
905 impl<const DER: bool> SpecByteLen for super::super::GeneralizedTimeFmt<DER> {
906 type T = GeneralizedTimeSpec;
907
908 open spec fn byte_len(&self, value: Self::T) -> nat {
909 generalized_time_len(value)
910 }
911 }
912
913}
914
915pub(crate) proof fn lemma_der_generalized_time_model(value: GeneralizedTimeSpec)
916 requires
917 super::GeneralizedTimeFmt::<true>.consistent(value),
918 ensures
919 value.der_wf(),
920 super::GeneralizedTimeFmt::<true>.spec_serialize(value) == generalized_time_prefix(value)
921 + generalized_time_fraction(value) + generalized_time_suffix(value),
922 super::GeneralizedTimeFmt::<true>.spec_serialize(value).len()
923 == super::GeneralizedTimeFmt::<true>.byte_len(value),
924 super::GeneralizedTimeFmt::<true>.spec_serialize(value).len() <= usize::MAX,
925 generalized_time_prefix(value).len() == 14,
926 generalized_time_suffix(value) == seq![0x5au8],
927{
928}
929
930pub(crate) proof fn lemma_der_generalized_time_layout(value: GeneralizedTimeSpec, pos: usize)
931 requires
932 super::GeneralizedTimeFmt::<true>.consistent(value),
933 ensures
934 super::GeneralizedTimeFmt::<true>.spec_serialize(value).len() == if value.fraction.len()
935 == 0 {
936 15
937 } else {
938 value.fraction.len() + 16
939 },
940 pos < super::GeneralizedTimeFmt::<true>.spec_serialize(value).len() ==> {
941 super::GeneralizedTimeFmt::<true>.spec_serialize(value)[pos as int] == if pos < 14 {
942 generalized_time_prefix(value)[pos as int]
943 } else if value.fraction.len() == 0 {
944 0x5au8
945 } else if pos == 14 {
946 0x2eu8
947 } else if (pos as nat) < value.fraction.len() + 15 {
948 value.fraction[pos as int - 15]
949 } else {
950 0x5au8
951 }
952 },
953{
954 lemma_der_generalized_time_model(value);
955}
956
957mod derived_proofs {
958 use super::*;
959
960 impl<const DER: bool> SafeParser for super::super::GeneralizedTimeFmt<DER> {
961 proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
962 }
963 }
964
965 impl<const DER: bool> Productive for super::super::GeneralizedTimeFmt<DER> {
966 proof fn lemma_productive(&self, ibuf: Seq<u8>) {
967 if let Some((n, _)) = self.spec_parse(ibuf) {
968 assert(n == ibuf.len());
969 assert(n >= 10);
970 }
971 }
972 }
973
974 impl<const DER: bool> GoodSerializer for super::super::GeneralizedTimeFmt<DER> {
975 proof fn lemma_serialize_len(&self, value: Self::SVal) {
976 }
977 }
978
979 impl<const DER: bool> EquivSerializers for super::super::GeneralizedTimeFmt<DER> {
980 proof fn lemma_serialize_equiv_on_empty(&self, value: Self::SVal) {
981 }
982 }
983
984 impl<const DER: bool> SPRoundTripDps for super::super::GeneralizedTimeFmt<DER> {
985 proof fn theorem_serialize_dps_parse_roundtrip(&self, value: Self::T, obuf: Seq<u8>) {
986 lemma_generalized_time_encode_roundtrip::<DER>(value);
987 }
988 }
989
990 impl SoundParser for super::super::GeneralizedTimeFmt<true> {
991 proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
992 if let Some((n, value)) = self.spec_parse(ibuf) {
993 lemma_der_generalized_time_canonical(ibuf);
994 assert(generalized_time_bytes(value) == ibuf);
995 }
996 }
997
998 proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
999 if let Some((_, value)) = self.spec_parse(ibuf) {
1000 lemma_der_generalized_time_canonical(ibuf);
1001 assert(value.der_wf());
1002 assert(generalized_time_len(value) <= usize::MAX);
1003 }
1004 }
1005 }
1006
1007 impl NonMalleable for super::super::GeneralizedTimeFmt<true> {
1008 proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
1009 if let (Some((n1, value1)), Some((n2, value2))) = (
1010 self.spec_parse(buf1),
1011 self.spec_parse(buf2),
1012 ) {
1013 lemma_der_generalized_time_canonical(buf1);
1014 lemma_der_generalized_time_canonical(buf2);
1015 if value1 == value2 {
1016 assert(buf1 == generalized_time_bytes(value1));
1017 assert(buf2 == generalized_time_bytes(value2));
1018 assert(buf1.take(n1) == buf1);
1019 assert(buf2.take(n2) == buf2);
1020 }
1021 }
1022 }
1023 }
1024
1025}
1026
1027impl<'i, const DER: bool> Parser<&'i [u8]> for super::GeneralizedTimeFmt<DER> {
1028 type PT = GeneralizedTime<'i>;
1029
1030 fn parse(&self, ibuf: &&'i [u8]) -> PResult<Self::PT> {
1031 let (n, bytes) = Tail.parse(ibuf)?;
1032 assert(bytes@ == bytes.deep_view());
1033 if !generalized_time_valid::<DER>(bytes) {
1034 return Err(ParseError::custom("Invalid GeneralizedTime"));
1035 }
1036 match generalized_timevalue(bytes) {
1037 Some(value) => Ok((n, value)),
1038 None => Err(ParseError::custom("GeneralizedTime offset out of range")),
1039 }
1040 }
1041}
1042
1043impl<Output: OutputBuf, 'i, const DER: bool> Serializer<
1044 Output,
1045 GeneralizedTime<'i>,
1046> for super::GeneralizedTimeFmt<DER> {
1047 fn serialize_into(&self, value: &GeneralizedTime<'i>, obuf: &mut Output) {
1048 generalized_time_to_bytes(value, obuf);
1049 }
1050}
1051
1052impl<'i, const DER: bool> Prepare<GeneralizedTime<'i>> for super::GeneralizedTimeFmt<DER> {
1053 fn prepare(&self, value: &GeneralizedTime<'i>) -> Result<usize, PreSerializeError> {
1054 if !generalized_value_wf::<DER>(value) {
1055 return Err(PreSerializeError::custom("Invalid GeneralizedTime value"));
1056 }
1057 if value.fraction.len() > usize::MAX - 16 {
1058 return Err(PreSerializeError::length_too_large());
1059 }
1060 Ok(
1061 10 + if value.precision == TimePrecision::Hour {
1062 0
1063 } else {
1064 2
1065 } + if value.precision == TimePrecision::Second {
1066 2
1067 } else {
1068 0
1069 } + if value.fraction.len() == 0 {
1070 0
1071 } else {
1072 1 + value.fraction.len()
1073 } + if value.zone == TimeZone::Utc {
1074 1
1075 } else {
1076 0
1077 },
1078 )
1079 }
1080}
1081
1082impl<'i, const DER: bool> ByteLen<GeneralizedTime<'i>> for super::GeneralizedTimeFmt<DER> {
1083 fn length(&self, value: &GeneralizedTime<'i>) -> usize {
1084 10 + if value.precision == TimePrecision::Hour {
1085 0
1086 } else {
1087 2
1088 } + if value.precision == TimePrecision::Second {
1089 2
1090 } else {
1091 0
1092 } + if value.fraction.len() == 0 {
1093 0
1094 } else {
1095 1 + value.fraction.len()
1096 } + if value.zone == TimeZone::Utc {
1097 1
1098 } else {
1099 0
1100 }
1101 }
1102}
1103
1104}