1use super::*;
2use vstd::prelude::*;
3
4verus! {
5
6#[derive(Debug, View, PolyfillClone)]
7pub enum UTCTimeZone {
8 UTC,
9 UTCPlus(u8, u8),
10 UTCMinus(u8, u8),
11}
12
13#[derive(Debug, View, PolyfillClone)]
14pub struct UTCTimeValueInner {
15 pub year: u16,
16 pub month: u8,
17 pub day: u8,
18 pub hour: u8,
19 pub minute: u8,
20 pub second: OptionDeep<u8>,
21 pub time_zone: UTCTimeZone,
22}
23
24pub type SpecUTCTimeValue = UTCTimeValueInner;
25pub type UTCTimeValue<'a> = UTCTimeValueInner;
26pub type UTCTimeValueOwned = UTCTimeValueInner;
27
28#[derive(Debug, View)]
36pub struct UTCTime;
37
38asn1_tagged!(UTCTime, tag_of!(UTC_TIME));
39
40impl SpecCombinator for UTCTime {
41 type SpecResult = UTCTimeValueInner;
42
43 closed spec fn spec_parse(&self, s: Seq<u8>) -> Result<(usize, Self::SpecResult), ()> {
44 LengthWrapped(UTCTimeInner).spec_parse(s)
45 }
46
47 proof fn spec_parse_wf(&self, s: Seq<u8>) {
48 LengthWrapped(UTCTimeInner).spec_parse_wf(s)
49 }
50
51 closed spec fn spec_serialize(&self, v: Self::SpecResult) -> Result<Seq<u8>, ()> {
52 LengthWrapped(UTCTimeInner).spec_serialize(v)
53 }
54}
55
56impl SecureSpecCombinator for UTCTime {
57 open spec fn is_prefix_secure() -> bool {
58 true
59 }
60
61 proof fn theorem_serialize_parse_roundtrip(&self, v: Self::SpecResult) {
62 LengthWrapped(UTCTimeInner).theorem_serialize_parse_roundtrip(v);
63 }
64
65 proof fn theorem_parse_serialize_roundtrip(&self, buf: Seq<u8>) {
66 LengthWrapped(UTCTimeInner).theorem_parse_serialize_roundtrip(buf);
67 }
68
69 proof fn lemma_prefix_secure(&self, s1: Seq<u8>, s2: Seq<u8>) {
70 LengthWrapped(UTCTimeInner).lemma_prefix_secure(s1, s2);
71 }
72}
73
74impl Combinator for UTCTime {
75 type Result<'a> = UTCTimeValueInner;
76 type Owned = UTCTimeValueInner;
77
78 closed spec fn spec_length(&self) -> Option<usize> {
79 None
80 }
81
82 fn length(&self) -> Option<usize> {
83 None
84 }
85
86 fn parse<'a>(&self, v: &'a [u8]) -> (res: Result<(usize, Self::Result<'a>), ParseError>) {
87 LengthWrapped(UTCTimeInner).parse(v)
88 }
89
90 fn serialize(&self, v: Self::Result<'_>, data: &mut Vec<u8>, pos: usize) -> (res: Result<usize, SerializeError>) {
91 LengthWrapped(UTCTimeInner).serialize(v, data, pos)
92 }
93}
94
95#[derive(Debug, View)]
96pub struct UTCTimeInner;
97
98#[allow(unused_macros)]
104macro_rules! spec_let_some {
105 ({ $body:expr }) => {
106 ::builtin_macros::verus_proof_expr! {
107 $body
108 }
109 };
110
111 ($var:ident = $opt:expr; $($rest_var:ident = $rest_opt:expr;)* { $body:expr }) => {
112 ::builtin_macros::verus_proof_expr! {
113 if let Some($var) = $opt {
114 spec_let_some!($($rest_var = $rest_opt;)* { $body })
115 } else {
116 Err(())
117 }
118 }
119 };
120}
121pub(super) use spec_let_some;
122
123#[allow(unused_macros)]
124macro_rules! let_some {
125 ($_:expr, { $body:expr }) => {
126 $body
127 };
128
129 ($err:expr, $var:ident = $opt:expr; $($rest_var:ident = $rest_opt:expr;)* { $body:expr }) => {
130 if let Some($var) = $opt {
131 let_some!($err, $($rest_var = $rest_opt;)* { $body })
132 } else {
133 Err($err)
134 }
135 };
136}
137pub(super) use let_some;
138
139impl SpecCombinator for UTCTimeInner {
140 type SpecResult = UTCTimeValueInner;
141
142 closed spec fn spec_parse(&self, v: Seq<u8>) -> Result<(usize, Self::SpecResult), ()> {
143 spec_let_some!(
144 year = two_chars_to_u8(v[0], v[1]);
145 month = two_chars_to_u8(v[2], v[3]);
146 day = two_chars_to_u8(v[4], v[5]);
147 hour = two_chars_to_u8(v[6], v[7]);
148 minute = two_chars_to_u8(v[8], v[9]);
149
150 {{
151 let year = if year >= 50 {
152 (1900 + year) as u16
153 } else {
154 (2000 + year) as u16
155 };
156
157 if v.len() == 11 && v[10] == 'Z' as u8 {
158 Ok((v.len() as usize, UTCTimeValueInner {
160 year: year,
161 month: month,
162 day: day,
163 hour: hour,
164 minute: minute,
165 second: OptionDeep::None,
166 time_zone: UTCTimeZone::UTC,
167 }))
168 } else if v.len() == 13 && v[12] == 'Z' as u8 {
169 spec_let_some!(
171 second = two_chars_to_u8(v[10], v[11]);
172 {{
173 Ok((v.len() as usize, UTCTimeValueInner {
174 year: year,
175 month: month,
176 day: day,
177 hour: hour,
178 minute: minute,
179 second: OptionDeep::Some(second),
180 time_zone: UTCTimeZone::UTC,
181 }))
182 }}
183 )
184 } else if v.len() == 15 && (v[10] == '+' as u8 || v[10] == '-' as u8) {
185 spec_let_some!(
189 offset_hour = two_chars_to_u8(v[11], v[12]);
190 offset_minute = two_chars_to_u8(v[13], v[14]);
191 {{
192 Ok((v.len() as usize, UTCTimeValueInner {
193 year: year,
194 month: month,
195 day: day,
196 hour: hour,
197 minute: minute,
198 second: OptionDeep::None,
199 time_zone: if v[10] == '+' as u8 {
200 UTCTimeZone::UTCPlus(offset_hour, offset_minute)
201 } else {
202 UTCTimeZone::UTCMinus(offset_hour, offset_minute)
203 },
204 }))
205 }}
206 )
207 } else if v.len() == 17 && (v[12] == '+' as u8 || v[12] == '-' as u8) {
208 spec_let_some!(
212 second = two_chars_to_u8(v[10], v[11]);
213 offset_hour = two_chars_to_u8(v[13], v[14]);
214 offset_minute = two_chars_to_u8(v[15], v[16]);
215 {{
216 Ok((v.len() as usize, UTCTimeValueInner {
217 year: year,
218 month: month,
219 day: day,
220 hour: hour,
221 minute: minute,
222 second: OptionDeep::Some(second),
223 time_zone: if v[12] == '+' as u8 {
224 UTCTimeZone::UTCPlus(offset_hour, offset_minute)
225 } else {
226 UTCTimeZone::UTCMinus(offset_hour, offset_minute)
227 },
228 }))
229 }}
230 )
231 } else {
232 Err(())
233 }
234 }}
235 )
236 }
237
238 proof fn spec_parse_wf(&self, s: Seq<u8>) {}
239
240 closed spec fn spec_serialize(&self, v: Self::SpecResult) -> Result<Seq<u8>, ()> {
241 spec_let_some!(
242 year = if 1950 <= v.year && v.year <= 2049 {
243 u8_to_two_chars((v.year % 100) as u8)
244 } else {
245 None
246 };
247 month = u8_to_two_chars(v.month);
248 day = u8_to_two_chars(v.day);
249 hour = u8_to_two_chars(v.hour);
250 minute = u8_to_two_chars(v.minute);
251
252 {{
253 match (v.second, v.time_zone) {
254 (OptionDeep::None, UTCTimeZone::UTC) =>
256 Ok(seq![
257 year.0, year.1,
258 month.0, month.1,
259 day.0, day.1,
260 hour.0, hour.1,
261 minute.0, minute.1,
262 'Z' as u8,
263 ]),
264
265 (OptionDeep::Some(second), UTCTimeZone::UTC) =>
267 spec_let_some!(
268 second = u8_to_two_chars(second);
269 {{ Ok(seq![
270 year.0, year.1,
271 month.0, month.1,
272 day.0, day.1,
273 hour.0, hour.1,
274 minute.0, minute.1,
275 second.0, second.1,
276 'Z' as u8
277 ])}}
278 ),
279
280 (OptionDeep::None, UTCTimeZone::UTCPlus(off_hour, off_minute)) |
283 (OptionDeep::None, UTCTimeZone::UTCMinus(off_hour, off_minute)) =>
284 spec_let_some!(
285 off_hour = u8_to_two_chars(off_hour);
286 off_minute = u8_to_two_chars(off_minute);
287 {{ Ok(seq![
288 year.0, year.1,
289 month.0, month.1,
290 day.0, day.1,
291 hour.0, hour.1,
292 minute.0, minute.1,
293 if let UTCTimeZone::UTCPlus(..) = v.time_zone { '+' as u8 } else { '-' as u8 },
294 off_hour.0, off_hour.1,
295 off_minute.0, off_minute.1
296 ])}}),
297
298 (OptionDeep::Some(second), UTCTimeZone::UTCPlus(off_hour, off_minute)) |
301 (OptionDeep::Some(second), UTCTimeZone::UTCMinus(off_hour, off_minute)) =>
302 spec_let_some!(
303 second = u8_to_two_chars(second);
304 off_hour = u8_to_two_chars(off_hour);
305 off_minute = u8_to_two_chars(off_minute);
306 {{ Ok(seq![
307 year.0, year.1,
308 month.0, month.1,
309 day.0, day.1,
310 hour.0, hour.1,
311 minute.0, minute.1,
312 second.0, second.1,
313 if let UTCTimeZone::UTCPlus(..) = v.time_zone { '+' as u8 } else { '-' as u8 },
314 off_hour.0, off_hour.1,
315 off_minute.0, off_minute.1
316 ])}}
317 ),
318 }
319 }}
320 )
321 }
322}
323
324impl SecureSpecCombinator for UTCTimeInner {
325 open spec fn is_prefix_secure() -> bool {
326 false
327 }
328
329 proof fn theorem_serialize_parse_roundtrip(&self, v: Self::SpecResult) {
330 if let Ok(buf) = self.spec_serialize(v) {
331 broadcast use lemma_two_chars_to_u8_iso, lemma_u8_to_two_chars_iso;
332 assert(self.spec_parse(buf).unwrap().1 =~= v);
333 }
334 }
335
336 proof fn theorem_parse_serialize_roundtrip(&self, buf: Seq<u8>) {
337 if let Ok((len, v)) = self.spec_parse(buf) {
338 broadcast use lemma_two_chars_to_u8_iso, lemma_u8_to_two_chars_iso;
339 assert(self.spec_serialize(v).unwrap() =~= buf);
340 assert(buf.subrange(0, len as int) =~= buf);
341 }
342 }
343
344 proof fn lemma_prefix_secure(&self, s1: Seq<u8>, s2: Seq<u8>) {}
345}
346
347impl Combinator for UTCTimeInner {
348 type Result<'a> = UTCTimeValueInner;
349 type Owned = UTCTimeValueInner;
350
351 closed spec fn spec_length(&self) -> Option<usize> {
352 None
353 }
354
355 fn length(&self) -> Option<usize> {
356 None
357 }
358
359 fn parse<'a>(&self, v: &'a [u8]) -> (res: Result<(usize, Self::Result<'a>), ParseError>) {
360 if v.len() < 10 {
361 return Err(ParseError::Other("Invalid UTCTime".to_string()));
362 }
363
364 let_some!(
365 ParseError::Other("Invalid UTCTime".to_string()),
366 year = two_chars_to_u8(v[0], v[1]);
367 month = two_chars_to_u8(v[2], v[3]);
368 day = two_chars_to_u8(v[4], v[5]);
369 hour = two_chars_to_u8(v[6], v[7]);
370 minute = two_chars_to_u8(v[8], v[9]);
371
372 {{
373 let year = if year >= 50 {
374 1900 + year as u16
375 } else {
376 2000 + year as u16
377 };
378
379 if v.len() == 11 && v[10] == 'Z' as u8 {
380 Ok((v.len(), UTCTimeValueInner {
382 year: year,
383 month: month,
384 day: day,
385 hour: hour,
386 minute: minute,
387 second: OptionDeep::None,
388 time_zone: UTCTimeZone::UTC,
389 }))
390 } else if v.len() == 13 && v[12] == 'Z' as u8 {
391 let_some!(
393 ParseError::Other("Invalid UTCTime".to_string()),
394 second = two_chars_to_u8(v[10], v[11]);
395 {{
396 Ok((v.len(), UTCTimeValueInner {
397 year: year,
398 month: month,
399 day: day,
400 hour: hour,
401 minute: minute,
402 second: OptionDeep::Some(second),
403 time_zone: UTCTimeZone::UTC,
404 }))
405 }}
406 )
407 } else if v.len() == 15 && (v[10] == '+' as u8 || v[10] == '-' as u8) {
408 let_some!(
412 ParseError::Other("Invalid UTCTime".to_string()),
413 offset_hour = two_chars_to_u8(v[11], v[12]);
414 offset_minute = two_chars_to_u8(v[13], v[14]);
415 {{
416 Ok((v.len(), UTCTimeValueInner {
417 year: year,
418 month: month,
419 day: day,
420 hour: hour,
421 minute: minute,
422 second: OptionDeep::None,
423 time_zone: if v[10] == '+' as u8 {
424 UTCTimeZone::UTCPlus(offset_hour, offset_minute)
425 } else {
426 UTCTimeZone::UTCMinus(offset_hour, offset_minute)
427 },
428 }))
429 }}
430 )
431 } else if v.len() == 17 && (v[12] == '+' as u8 || v[12] == '-' as u8) {
432 let_some!(
436 ParseError::Other("Invalid UTCTime".to_string()),
437 second = two_chars_to_u8(v[10], v[11]);
438 offset_hour = two_chars_to_u8(v[13], v[14]);
439 offset_minute = two_chars_to_u8(v[15], v[16]);
440 {{
441 Ok((v.len(), UTCTimeValueInner {
442 year: year,
443 month: month,
444 day: day,
445 hour: hour,
446 minute: minute,
447 second: OptionDeep::Some(second),
448 time_zone: if v[12] == '+' as u8 {
449 UTCTimeZone::UTCPlus(offset_hour, offset_minute)
450 } else {
451 UTCTimeZone::UTCMinus(offset_hour, offset_minute)
452 },
453 }))
454 }}
455 )
456 } else {
457 Err(ParseError::Other("Invalid UTCTime".to_string()))
458 }
459 }}
460 )
461 }
462
463 fn serialize(&self, v: Self::Result<'_>, data: &mut Vec<u8>, pos: usize) -> (res: Result<usize, SerializeError>) {
464 let res = let_some!(
465 SerializeError::Other("Invalid UTCTime".to_string()),
466 year = if 1950 <= v.year && v.year <= 2049 {
467 u8_to_two_chars((v.year % 100) as u8)
468 } else {
469 None
470 };
471 month = u8_to_two_chars(v.month);
472 day = u8_to_two_chars(v.day);
473 hour = u8_to_two_chars(v.hour);
474 minute = u8_to_two_chars(v.minute);
475
476 {{
477 match (v.second, &v.time_zone) {
478 (OptionDeep::None, UTCTimeZone::UTC) => {
480 if pos <= usize::MAX - 11 && pos + 11 <= data.len() {
481 data.set(pos, year.0); data.set(pos + 1, year.1);
482 data.set(pos + 2, month.0); data.set(pos + 3, month.1);
483 data.set(pos + 4, day.0); data.set(pos + 5, day.1);
484 data.set(pos + 6, hour.0); data.set(pos + 7, hour.1);
485 data.set(pos + 8, minute.0); data.set(pos + 9, minute.1);
486 data.set(pos + 10, 'Z' as u8);
487 Ok(11)
488 } else {
489 Err(SerializeError::InsufficientBuffer)
490 }
491 }
492
493 (OptionDeep::Some(second), UTCTimeZone::UTC) =>
495 let_some!(
496 SerializeError::Other("Invalid UTCTime".to_string()),
497 second = u8_to_two_chars(second);
498 {{
499 if pos <= usize::MAX - 13 && pos + 13 <= data.len() {
500 data.set(pos, year.0); data.set(pos + 1, year.1);
501 data.set(pos + 2, month.0); data.set(pos + 3, month.1);
502 data.set(pos + 4, day.0); data.set(pos + 5, day.1);
503 data.set(pos + 6, hour.0); data.set(pos + 7, hour.1);
504 data.set(pos + 8, minute.0); data.set(pos + 9, minute.1);
505 data.set(pos + 10, second.0); data.set(pos + 11, second.1);
506 data.set(pos + 12, 'Z' as u8);
507 Ok(13)
508 } else {
509 Err(SerializeError::InsufficientBuffer)
510 }
511 }}
512 ),
513
514 (OptionDeep::None, UTCTimeZone::UTCPlus(off_hour, off_minute)) |
517 (OptionDeep::None, UTCTimeZone::UTCMinus(off_hour, off_minute)) =>
518 let_some!(
519 SerializeError::Other("Invalid UTCTime".to_string()),
520 off_hour = u8_to_two_chars(*off_hour);
521 off_minute = u8_to_two_chars(*off_minute);
522 {{
523 if pos <= usize::MAX - 15 && pos + 15 <= data.len() {
524 data.set(pos, year.0); data.set(pos + 1, year.1);
525 data.set(pos + 2, month.0); data.set(pos + 3, month.1);
526 data.set(pos + 4, day.0); data.set(pos + 5, day.1);
527 data.set(pos + 6, hour.0); data.set(pos + 7, hour.1);
528 data.set(pos + 8, minute.0); data.set(pos + 9, minute.1);
529 data.set(pos + 10, if let UTCTimeZone::UTCPlus(..) = v.time_zone { '+' as u8 } else { '-' as u8 });
530 data.set(pos + 11, off_hour.0); data.set(pos + 12, off_hour.1);
531 data.set(pos + 13, off_minute.0); data.set(pos + 14, off_minute.1);
532 Ok(15)
533 } else {
534 Err(SerializeError::InsufficientBuffer)
535 }
536 }}),
537
538 (OptionDeep::Some(second), UTCTimeZone::UTCPlus(off_hour, off_minute)) |
541 (OptionDeep::Some(second), UTCTimeZone::UTCMinus(off_hour, off_minute)) =>
542 let_some!(
543 SerializeError::Other("Invalid UTCTime".to_string()),
544 second = u8_to_two_chars(second);
545 off_hour = u8_to_two_chars(*off_hour);
546 off_minute = u8_to_two_chars(*off_minute);
547 {{
548 if pos <= usize::MAX - 17 && pos + 17 <= data.len() {
549 data.set(pos, year.0); data.set(pos + 1, year.1);
550 data.set(pos + 2, month.0); data.set(pos + 3, month.1);
551 data.set(pos + 4, day.0); data.set(pos + 5, day.1);
552 data.set(pos + 6, hour.0); data.set(pos + 7, hour.1);
553 data.set(pos + 8, minute.0); data.set(pos + 9, minute.1);
554 data.set(pos + 10, second.0); data.set(pos + 11, second.1);
555 data.set(pos + 12, if let UTCTimeZone::UTCPlus(..) = v.time_zone { '+' as u8 } else { '-' as u8 });
556 data.set(pos + 13, off_hour.0); data.set(pos + 14, off_hour.1);
557 data.set(pos + 15, off_minute.0); data.set(pos + 16, off_minute.1);
558 Ok(17)
559 } else {
560 Err(SerializeError::InsufficientBuffer)
561 }
562 }}
563 ),
564 }
565 }}
566 );
567
568 if res.is_ok() {
569 assert(data@ =~= seq_splice(old(data)@, pos, self@.spec_serialize(v@).unwrap()));
570 }
571
572 res
573 }
574}
575
576macro_rules! zero_char { {} => { '0' as u8 }; }
577macro_rules! nine_char { {} => { '9' as u8 }; }
578
579#[verifier::opaque]
581pub closed spec fn spec_two_chars_to_u8(b1: u8, b2: u8) -> Option<u8> {
582 if b1 >= zero_char!() && b1 <= nine_char!() && b2 >= zero_char!() && b2 <= nine_char!() {
583 Some(((b1 - zero_char!()) * 10 + (b2 - zero_char!())) as u8)
584 } else {
585 None
586 }
587}
588
589#[verifier::opaque]
590pub closed spec fn spec_u8_to_two_chars(v: u8) -> Option<(u8, u8)> {
591 if v >= 100 {
592 None
593 } else {
594 let b1 = v / 10;
595 let b2 = v % 10;
596
597 Some(((b1 + zero_char!()) as u8, (b2 + zero_char!()) as u8))
598 }
599}
600
601pub broadcast proof fn lemma_two_chars_to_u8_iso(b1: u8, b2: u8)
602 ensures
603 #[trigger] spec_two_chars_to_u8(b1, b2) matches Some(v) ==> {
604 &&& 0 <= v < 100
605 &&& spec_u8_to_two_chars(v) matches Some(r)
606 &&& r == (b1, b2)
607 }
608{
609 reveal(spec_two_chars_to_u8);
610 reveal(spec_u8_to_two_chars);
611}
612
613pub broadcast proof fn lemma_u8_to_two_chars_iso(v: u8)
614 ensures
615 #[trigger] spec_u8_to_two_chars(v) matches Some((b1, b2)) ==> {
616 &&& spec_two_chars_to_u8(b1, b2) matches Some(a)
617 &&& a == v
618 },
619 spec_u8_to_two_chars(v).is_none() <==> v >= 100
620{
621 reveal(spec_two_chars_to_u8);
622 reveal(spec_u8_to_two_chars);
623}
624
625#[verifier::when_used_as_spec(spec_two_chars_to_u8)]
626pub fn two_chars_to_u8(b1: u8, b2: u8) -> (res: Option<u8>)
627 ensures
628 res matches Some(res) ==> {
629 &&& spec_two_chars_to_u8(b1, b2) matches Some(res2)
630 &&& res == res2
631 },
632 res.is_none() ==> spec_two_chars_to_u8(b1, b2).is_none()
633{
634 reveal(spec_two_chars_to_u8);
635 if b1 >= zero_char!() && b1 <= nine_char!() && b2 >= zero_char!() && b2 <= nine_char!() {
636 Some(((b1 - zero_char!()) * 10 + (b2 - zero_char!())) as u8)
637 } else {
638 None
639 }
640}
641
642#[verifier::when_used_as_spec(spec_u8_to_two_chars)]
643pub fn u8_to_two_chars(v: u8) -> (res: Option<(u8, u8)>)
644 ensures
645 res matches Some(res) ==> {
646 &&& spec_u8_to_two_chars(v) matches Some(res2)
647 &&& res2 == res
648 },
649 res.is_none() ==> spec_u8_to_two_chars(v).is_none(),
650{
651 reveal(spec_u8_to_two_chars);
652 if v >= 100 {
653 None
654 } else {
655 let b1 = v / 10;
656 let b2 = v % 10;
657
658 Some(((b1 + zero_char!()) as u8, (b2 + zero_char!()) as u8))
659 }
660}
661
662}