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::{mapped::spec::FnSpecMapper, Mapped, Refined, Tail},
11 core::{proof::*, spec::*},
12};
13use vstd::assert_seqs_equal;
14use vstd::prelude::*;
15use OutputBuf;
16
17use super::datetime::*;
18
19verus! {
20
21pub const ASCII_PLUS: u8 = 0x2b;
23
24pub const ASCII_MINUS: u8 = 0x2d;
26
27pub const ASCII_Z: u8 = 0x5a;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, StructuralEq)]
34pub struct UtcTime {
35 pub datetime: DateTime,
36 pub precision: TimePrecision,
37}
38
39impl DeepView for UtcTime {
40 type V = UtcTime;
41
42 closed spec fn deep_view(&self) -> Self::V {
43 *self
44 }
45}
46
47pub(crate) proof fn lemma_utc_time_deep_view(value: &UtcTime)
48 ensures
49 value.deep_view() == *value,
50{
51}
52
53impl UtcTime {
54 pub open spec fn wf(&self) -> bool {
57 &&& datetime_wf(self.datetime)
58 &&& 1950 <= self.datetime.year <= 2049
59 &&& (self.precision == TimePrecision::Minute || self.precision == TimePrecision::Second)
60 &&& (self.precision == TimePrecision::Second || self.datetime.second == 0)
61 }
62}
63
64pub open spec fn utc_time_fields_wf(bytes: Seq<u8>, has_seconds: bool) -> bool {
66 let second_end = if has_seconds {
67 12
68 } else {
69 10
70 };
71 &&& digits(bytes, 0, second_end)
72 &&& datetime_wf(
73 DateTime {
74 year: utc_year(decimal2(bytes, 0)),
75 month: decimal2(bytes, 2),
76 day: decimal2(bytes, 4),
77 hour: decimal2(bytes, 6),
78 minute: decimal2(bytes, 8),
79 second: if has_seconds {
80 decimal2(bytes, 10)
81 } else {
82 0
83 },
84 },
85 )
86}
87
88pub fn utc_time_fields_valid(bytes: &[u8], has_seconds: bool) -> bool
90 requires
91 if has_seconds {
92 12 <= bytes.len()
93 } else {
94 10 <= bytes.len()
95 },
96 returns
97 utc_time_fields_wf(bytes@, has_seconds),
98{
99 let end = if has_seconds {
100 12
101 } else {
102 10
103 };
104 if !is_digits(bytes, 0, end) {
105 return false;
106 }
107 let value = DateTime {
108 year: utc_year(decimal_2(bytes, 0)),
109 month: decimal_2(bytes, 2),
110 day: decimal_2(bytes, 4),
111 hour: decimal_2(bytes, 6),
112 minute: decimal_2(bytes, 8),
113 second: if has_seconds {
114 decimal_2(bytes, 10)
115 } else {
116 0
117 },
118 };
119 datetime_wf(value)
120}
121
122pub open spec fn utc_offset_wf(bytes: Seq<u8>, pos: usize) -> bool {
124 &&& pos + 5 == bytes.len()
125 &&& (bytes[pos as int] == ASCII_PLUS || bytes[pos as int] == ASCII_MINUS)
126 &&& digits(bytes, pos as int + 1, pos as int + 5)
127 &&& decimal2(bytes, (pos as int + 1) as usize) <= 23
128 &&& decimal2(bytes, (pos as int + 3) as usize) <= 59
129}
130
131pub fn utc_offset_valid(bytes: &[u8], pos: usize) -> bool
133 requires
134 pos + 5 == bytes.len(),
135 returns
136 utc_offset_wf(bytes@, pos),
137{
138 if bytes[pos] != ASCII_PLUS && bytes[pos] != ASCII_MINUS {
139 return false;
140 }
141 if !is_digits(bytes, pos + 1, pos + 5) {
142 return false;
143 }
144 decimal_2(bytes, pos + 1) <= 23 && decimal_2(bytes, pos + 3) <= 59
145}
146
147pub open spec fn utc_time_lexical_wf<const DER: bool>(bytes: Seq<u8>) -> bool {
151 if DER {
152 bytes.len() == 13 && bytes[12] == ASCII_Z && utc_time_fields_wf(bytes, true)
153 } else {
154 ||| bytes.len() == 11 && bytes[10] == ASCII_Z && utc_time_fields_wf(bytes, false)
155 ||| bytes.len() == 13 && bytes[12] == ASCII_Z && utc_time_fields_wf(bytes, true)
156 ||| bytes.len() == 15 && utc_time_fields_wf(bytes, false) && utc_offset_wf(bytes, 10)
157 ||| bytes.len() == 17 && utc_time_fields_wf(bytes, true) && utc_offset_wf(bytes, 12)
158 }
159}
160
161pub fn utc_time_bytes_valid<const DER: bool>(bytes: &[u8]) -> bool
163 returns
164 utc_time_lexical_wf::<DER>(bytes@),
165{
166 if DER {
167 bytes.len() == 13 && bytes[12] == ASCII_Z && utc_time_fields_valid(bytes, true)
168 } else if bytes.len() == 11 {
169 bytes[10] == ASCII_Z && utc_time_fields_valid(bytes, false)
170 } else if bytes.len() == 13 {
171 bytes[12] == ASCII_Z && utc_time_fields_valid(bytes, true)
172 } else if bytes.len() == 15 {
173 utc_time_fields_valid(bytes, false) && utc_offset_valid(bytes, 10)
174 } else if bytes.len() == 17 {
175 utc_time_fields_valid(bytes, true) && utc_offset_valid(bytes, 12)
176 } else {
177 false
178 }
179}
180
181pub open spec fn utc_time_value(bytes: Seq<u8>) -> Option<UtcTime> {
184 let has_seconds = bytes.len() == 13 || bytes.len() == 17;
185 let local = DateTime {
186 year: utc_year(decimal2(bytes, 0)),
187 month: decimal2(bytes, 2),
188 day: decimal2(bytes, 4),
189 hour: decimal2(bytes, 6),
190 minute: decimal2(bytes, 8),
191 second: if has_seconds {
192 decimal2(bytes, 10)
193 } else {
194 0
195 },
196 };
197 let precision = if has_seconds {
198 TimePrecision::Second
199 } else {
200 TimePrecision::Minute
201 };
202 if bytes.len() == 11 || bytes.len() == 13 {
203 Some(UtcTime { datetime: local, precision })
204 } else {
205 let pos: usize = if has_seconds {
206 12
207 } else {
208 10
209 };
210 match normalize_offset(
211 local,
212 bytes[pos as int] == ASCII_PLUS,
213 decimal2(bytes, (pos as int + 1) as usize),
214 decimal2(bytes, (pos as int + 3) as usize),
215 ) {
216 Some(datetime) => Some(UtcTime { datetime, precision }),
217 None => None,
218 }
219 }
220}
221
222pub fn utctime_value(bytes: &[u8]) -> Option<UtcTime>
224 requires
225 utc_time_lexical_wf::<false>(bytes@),
226 returns
227 utc_time_value(bytes@),
228{
229 let has_seconds = bytes.len() == 13 || bytes.len() == 17;
230 let local = DateTime {
231 year: utc_year(decimal_2(bytes, 0)),
232 month: decimal_2(bytes, 2),
233 day: decimal_2(bytes, 4),
234 hour: decimal_2(bytes, 6),
235 minute: decimal_2(bytes, 8),
236 second: if has_seconds {
237 decimal_2(bytes, 10)
238 } else {
239 0
240 },
241 };
242 let precision = if has_seconds {
243 TimePrecision::Second
244 } else {
245 TimePrecision::Minute
246 };
247 if bytes.len() == 11 || bytes.len() == 13 {
248 Some(UtcTime { datetime: local, precision })
249 } else {
250 let pos = if has_seconds {
251 12
252 } else {
253 10
254 };
255 match normalize_offset(
256 local,
257 bytes[pos] == ASCII_PLUS,
258 decimal_2(bytes, pos + 1),
259 decimal_2(bytes, pos + 3),
260 ) {
261 Some(datetime) => Some(UtcTime { datetime, precision }),
262 None => None,
263 }
264 }
265}
266
267pub open spec fn utc_time_bytes_wf<const DER: bool>(bytes: Seq<u8>) -> bool {
270 &&& utc_time_lexical_wf::<DER>(bytes)
271 &&& utc_time_value(bytes) matches Some(value) ==> value.wf()
272 &&& utc_time_value(bytes).is_some()
273}
274
275pub fn utc_time_valid<const DER: bool>(bytes: &[u8]) -> bool
277 returns
278 utc_time_bytes_wf::<DER>(bytes@),
279{
280 if !utc_time_bytes_valid::<DER>(bytes) {
281 return false;
282 }
283 let value = utctime_value(bytes);
284 match value {
285 Some(value) => {
286 datetime_wf(value.datetime) && 1950 <= value.datetime.year && value.datetime.year
287 <= 2049 && (value.precision == TimePrecision::Minute || value.precision
288 == TimePrecision::Second) && (value.precision == TimePrecision::Second
289 || value.datetime.second == 0)
290 },
291 None => false,
292 }
293}
294
295#[verusfmt::skip]
297pub open spec fn utc_time_bytes(value: UtcTime) -> Seq<u8> {
298 let year = (value.datetime.year as int % 100) as u8;
299 decimal2_bytes(year)@
300 + decimal2_bytes(value.datetime.month)@
301 + decimal2_bytes(value.datetime.day)@
302 + decimal2_bytes(value.datetime.hour)@
303 + decimal2_bytes(value.datetime.minute)@
304 + if value.precision == TimePrecision::Second {
305 decimal2_bytes(value.datetime.second)@ + seq![ASCII_Z]
306 } else {
307 seq![ASCII_Z]
308 }
309}
310
311pub fn utc_time_to_bytes<Output: OutputBuf>(value: &UtcTime, obuf: &mut Output)
313 requires
314 value.wf(),
315 old(obuf).fits(utc_time_bytes(*value).len()),
316 ensures
317 final(obuf)@ == old(obuf)@ + utc_time_bytes(*value),
318 forall|n| old(obuf).fits(utc_time_bytes(*value).len() + n) <==> final(obuf).fits(n),
319 old(obuf).same_destination(final(obuf)),
320{
321 broadcast use crate::core::exec::output::outbuf_lemmas;
322
323 let short_year = (value.datetime.year % 100) as u8;
324 let year = decimal2_bytes(short_year);
325 let month = decimal2_bytes(value.datetime.month);
326 let day = decimal2_bytes(value.datetime.day);
327 let hour = decimal2_bytes(value.datetime.hour);
328 let minute = decimal2_bytes(value.datetime.minute);
329 obuf.write_bytes(&year);
330 obuf.write_bytes(&month);
331 obuf.write_bytes(&day);
332 obuf.write_bytes(&hour);
333 obuf.write_bytes(&minute);
334 if value.precision == TimePrecision::Second {
335 let second = decimal2_bytes(value.datetime.second);
336 obuf.write_bytes(&second);
337 }
338 obuf.write_byte(ASCII_Z);
339}
340
341proof fn lemma_utc_year_roundtrip(year: u16)
343 requires
344 1950 <= year <= 2049,
345 ensures
346 (year as int % 100) as u8 <= 99,
347 utc_year((year as int % 100) as u8) == year,
348{
349}
350
351proof fn lemma_utc_time_bytes_layout(value: UtcTime)
354 requires
355 value.wf(),
356 ensures
357 ({
358 let bytes = utc_time_bytes(value);
359 let end = if value.precision == TimePrecision::Second {
360 12int
361 } else {
362 10int
363 };
364 &&& bytes.len() == end + 1
365 &&& bytes[end] == ASCII_Z
366 &&& digits(bytes, 0, end)
367 &&& utc_year(decimal2(bytes, 0)) == value.datetime.year
368 &&& decimal2(bytes, 2) == value.datetime.month
369 &&& decimal2(bytes, 4) == value.datetime.day
370 &&& decimal2(bytes, 6) == value.datetime.hour
371 &&& decimal2(bytes, 8) == value.datetime.minute
372 &&& value.precision == TimePrecision::Second ==> decimal2(bytes, 10)
373 == value.datetime.second
374 }),
375{
376 let short_year = (value.datetime.year as int % 100) as u8;
377 lemma_utc_year_roundtrip(value.datetime.year);
378 lemma_decimal2_roundtrip(short_year);
379 lemma_decimal2_roundtrip(value.datetime.month);
380 lemma_decimal2_roundtrip(value.datetime.day);
381 lemma_decimal2_roundtrip(value.datetime.hour);
382 lemma_decimal2_roundtrip(value.datetime.minute);
383 lemma_decimal2_roundtrip(value.datetime.second);
384}
385
386pub proof fn lemma_utc_time_encode_wf<const DER: bool>(value: UtcTime)
387 requires
388 value.wf(),
389 DER ==> value.precision == TimePrecision::Second,
390 ensures
391 utc_time_bytes_wf::<DER>(utc_time_bytes(value)),
392 utc_time_value(utc_time_bytes(value)) == Some(value),
393{
394 lemma_utc_time_bytes_layout(value);
395}
396
397#[verifier::rlimit(100)]
398pub proof fn lemma_der_utc_time_canonical(bytes: Seq<u8>)
399 requires
400 utc_time_bytes_wf::<true>(bytes),
401 ensures
402 utc_time_bytes(utc_time_value(bytes)->0) == bytes,
403{
404 assert(digits(bytes, 0, 12));
405 assert(ascii_digit(bytes[0]));
406 assert(ascii_digit(bytes[1]));
407 lemma_decimal2_canonical(bytes, 0);
408 lemma_decimal2_canonical(bytes, 2);
409 lemma_decimal2_canonical(bytes, 4);
410 lemma_decimal2_canonical(bytes, 6);
411 lemma_decimal2_canonical(bytes, 8);
412 lemma_decimal2_canonical(bytes, 10);
413}
414
415type UtcTimeInnerFmt<const DER: bool> = Mapped<
416 Refined<Tail, PredFnSpec<Seq<u8>>>,
417 FnSpecMapper<Seq<u8>, UtcTime>,
418>;
419
420pub open spec fn utc_time_fmt<const DER: bool>() -> UtcTimeInnerFmt<DER> {
421 Mapped {
422 inner: Refined(Tail, |bytes: Seq<u8>| utc_time_bytes_wf::<DER>(bytes)),
423 mapper: (|bytes: Seq<u8>| utc_time_value(bytes)->0, |value: UtcTime| utc_time_bytes(value)),
424 }
425}
426
427proof fn lemma_der_utc_time_fmt_sound_nonmal()
428 ensures
429 utc_time_fmt::<true>().sound_inv(),
430 utc_time_fmt::<true>().nonmal_inv(),
431{
432 assert forall|bytes: Seq<u8>| #[trigger]
433 utc_time_fmt::<true>().inner.consistent(bytes) implies (utc_time_fmt::<true>().mapper.1)(
434 (utc_time_fmt::<true>().mapper.0)(bytes),
435 ) == bytes by {
436 lemma_der_utc_time_canonical(bytes);
437 }
438}
439
440mod derived_specs {
441 use super::*;
442
443 impl<const DER: bool> SpecParser for super::super::UtcTimeFmt<DER> {
444 type PVal = UtcTime;
445
446 open spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
447 utc_time_fmt::<DER>().spec_parse(ibuf)
448 }
449 }
450
451 impl<const DER: bool> Consistency for super::super::UtcTimeFmt<DER> {
452 type Val = UtcTime;
453
454 open spec fn consistent(&self, value: Self::Val) -> bool {
455 value.wf() && (DER ==> value.precision == TimePrecision::Second)
456 }
457 }
458
459 impl<const DER: bool> SpecSerializerDps for super::super::UtcTimeFmt<DER> {
460 type SValue = UtcTime;
461
462 open spec fn spec_serialize_dps(&self, value: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
463 utc_time_bytes(value)
464 }
465 }
466
467 impl<const DER: bool> SpecSerializer for super::super::UtcTimeFmt<DER> {
468 type SVal = UtcTime;
469
470 open spec fn spec_serialize(&self, value: Self::SVal) -> Seq<u8> {
471 utc_time_bytes(value)
472 }
473 }
474
475 impl<const DER: bool> SpecByteLen for super::super::UtcTimeFmt<DER> {
476 type T = UtcTime;
477
478 open spec fn byte_len(&self, value: Self::T) -> nat {
479 utc_time_bytes(value).len()
480 }
481 }
482
483}
484
485mod derived_proofs {
486 use super::*;
487
488 impl<const DER: bool> SafeParser for super::super::UtcTimeFmt<DER> {
489 proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
490 utc_time_fmt::<DER>().lemma_parse_safe(ibuf);
491 }
492 }
493
494 impl<const DER: bool> Productive for super::super::UtcTimeFmt<DER> {
495 proof fn lemma_productive(&self, ibuf: Seq<u8>) {
496 }
497 }
498
499 impl<const DER: bool> GoodSerializer for super::super::UtcTimeFmt<DER> {
500 proof fn lemma_serialize_len(&self, value: Self::SVal) {
501 }
502 }
503
504 impl<const DER: bool> EquivSerializers for super::super::UtcTimeFmt<DER> {
505 proof fn lemma_serialize_equiv_on_empty(&self, value: Self::SVal) {
506 }
507 }
508
509 impl<const DER: bool> SPRoundTripDps for super::super::UtcTimeFmt<DER> {
510 proof fn theorem_serialize_dps_parse_roundtrip(&self, value: Self::T, obuf: Seq<u8>) {
511 lemma_utc_time_encode_wf::<DER>(value);
512 }
513 }
514
515 impl SoundParser for super::super::UtcTimeFmt<true> {
516 proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
517 lemma_der_utc_time_fmt_sound_nonmal();
518 utc_time_fmt::<true>().lemma_parse_sound_consumption(ibuf);
519 }
520
521 proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
522 lemma_der_utc_time_fmt_sound_nonmal();
523 utc_time_fmt::<true>().lemma_parse_sound_value(ibuf);
524 }
525 }
526
527 impl NonMalleable for super::super::UtcTimeFmt<true> {
528 proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
529 lemma_der_utc_time_fmt_sound_nonmal();
530 utc_time_fmt::<true>().lemma_parse_non_malleable(buf1, buf2);
531 }
532 }
533
534}
535
536impl<'i, const DER: bool> Parser<&'i [u8]> for super::UtcTimeFmt<DER> {
537 type PT = UtcTime;
538
539 fn parse(&self, ibuf: &&'i [u8]) -> PResult<Self::PT> {
540 let (n, bytes) = Tail.parse(ibuf)?;
541 assert(bytes@ == bytes.deep_view());
542 if !utc_time_valid::<DER>(bytes) {
543 return Err(ParseError::custom("Invalid UTCTime"));
544 }
545 match utctime_value(bytes) {
546 Some(value) => Ok((n, value)),
547 None => Err(ParseError::custom("UTCTime offset out of range")),
548 }
549 }
550}
551
552impl<Output: OutputBuf, const DER: bool> Serializer<Output, UtcTime> for super::UtcTimeFmt<DER> {
553 fn serialize_into(&self, value: &UtcTime, obuf: &mut Output) {
554 proof {
555 assert(value.wf());
556 assert(DER ==> value.precision == TimePrecision::Second);
557 lemma_utc_time_encode_wf::<DER>(*value);
558 }
559 utc_time_to_bytes(value, obuf);
560 }
561}
562
563impl<const DER: bool> Prepare<UtcTime> for super::UtcTimeFmt<DER> {
564 fn prepare(&self, value: &UtcTime) -> Result<usize, PreSerializeError> {
565 if !datetime_wf(value.datetime) || value.datetime.year < 1950 || value.datetime.year > 2049
566 || (value.precision != TimePrecision::Minute && value.precision
567 != TimePrecision::Second) || (value.precision == TimePrecision::Minute
568 && value.datetime.second != 0) || (DER && value.precision != TimePrecision::Second) {
569 return Err(PreSerializeError::custom("Invalid UTCTime value"));
570 }
571 proof {
572 assert(value.wf());
573 assert(DER ==> value.precision == TimePrecision::Second);
574 lemma_utc_time_encode_wf::<DER>(*value);
575 }
576 Ok(
577 if value.precision == TimePrecision::Second {
578 13
579 } else {
580 11
581 },
582 )
583 }
584}
585
586impl<const DER: bool> ByteLen<UtcTime> for super::UtcTimeFmt<DER> {
587 fn length(&self, value: &UtcTime) -> usize {
588 if value.precision == TimePrecision::Second {
589 13
590 } else {
591 11
592 }
593 }
594}
595
596}