1 use std::cmp;
2 use std::convert::{TryFrom, TryInto};
3 use std::fmt;
4 use std::time::{SystemTime, Duration as SystemDuration, UNIX_EPOCH};
5 use std::u32;
6 
7 #[cfg(any(test, feature = "quickcheck"))]
8 use quickcheck::{Arbitrary, Gen};
9 
10 use crate::{
11     Error,
12     Result,
13 };
14 
15 /// A timestamp representable by OpenPGP.
16 ///
17 /// OpenPGP timestamps are represented as `u32` containing the number of seconds
18 /// elapsed since midnight, 1 January 1970 UTC ([Section 3.5 of RFC 4880]).
19 ///
20 /// They cannot express dates further than 7th February of 2106 or earlier than
21 /// the [UNIX epoch]. Unlike Unix's `time_t`, OpenPGP's timestamp is unsigned so
22 /// it rollsover in 2106, not 2038.
23 ///
24 /// # Examples
25 ///
26 /// Signature creation time is internally stored as a `Timestamp`:
27 ///
28 /// Note that this example retrieves raw packet value.
29 /// Use [`SubpacketArea::signature_creation_time`] to get the signature creation time.
30 ///
31 /// [`SubpacketArea::signature_creation_time`]: ../packet/signature/subpacket/struct.SubpacketArea.html#method.signature_creation_time
32 ///
33 /// ```
34 /// use sequoia_openpgp as openpgp;
35 /// # use openpgp::Result;
36 /// use std::convert::From;
37 /// use std::time::SystemTime;
38 /// use openpgp::cert::prelude::*;
39 /// use openpgp::policy::StandardPolicy;
40 /// use openpgp::packet::signature::subpacket::{SubpacketTag, SubpacketValue};
41 ///
42 /// # fn main() -> Result<()> {
43 /// let (cert, _) =
44 ///     CertBuilder::general_purpose(None, Some("alice@example.org"))
45 ///     .generate()?;
46 ///
47 /// let subkey = cert.keys().subkeys().next().unwrap();
48 /// let packets = subkey.bundle().self_signatures()[0].hashed_area();
49 ///
50 /// match packets.subpacket(SubpacketTag::SignatureCreationTime).unwrap().value() {
51 ///     SubpacketValue::SignatureCreationTime(ts) => assert!(u32::from(*ts) > 0),
52 ///     v => panic!("Unexpected subpacket: {:?}", v),
53 /// }
54 ///
55 /// let p = &StandardPolicy::new();
56 /// let now = SystemTime::now();
57 /// assert!(subkey.binding_signature(p, now)?.signature_creation_time().is_some());
58 /// # Ok(()) }
59 /// ```
60 ///
61 /// [Section 3.5 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-3.5
62 /// [UNIX epoch]: https://en.wikipedia.org/wiki/Unix_time
63 /// [`Timestamp::round_down`]: ../types/struct.Timestamp.html#method.round_down
64 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
65 pub struct Timestamp(u32);
66 
67 impl From<Timestamp> for u32 {
from(t: Timestamp) -> Self68     fn from(t: Timestamp) -> Self {
69         t.0
70     }
71 }
72 
73 impl From<u32> for Timestamp {
from(t: u32) -> Self74     fn from(t: u32) -> Self {
75         Timestamp(t)
76     }
77 }
78 
79 impl TryFrom<SystemTime> for Timestamp {
80     type Error = anyhow::Error;
81 
try_from(t: SystemTime) -> Result<Self>82     fn try_from(t: SystemTime) -> Result<Self> {
83         match t.duration_since(std::time::UNIX_EPOCH) {
84             Ok(d) if d.as_secs() <= std::u32::MAX as u64 =>
85                 Ok(Timestamp(d.as_secs() as u32)),
86             _ => Err(Error::InvalidArgument(
87                 format!("Time exceeds u32 epoch: {:?}", t))
88                      .into()),
89         }
90     }
91 }
92 
93 impl From<Timestamp> for SystemTime {
from(t: Timestamp) -> Self94     fn from(t: Timestamp) -> Self {
95         UNIX_EPOCH + SystemDuration::new(t.0 as u64, 0)
96     }
97 }
98 
99 impl From<Timestamp> for Option<SystemTime> {
from(t: Timestamp) -> Self100     fn from(t: Timestamp) -> Self {
101         Some(t.into())
102     }
103 }
104 
105 impl fmt::Display for Timestamp {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result106     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107         write!(f, "{}", crate::fmt::time(&SystemTime::from(*self)))
108     }
109 }
110 
111 impl fmt::Debug for Timestamp {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result112     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113         write!(f, "{}", self.0)
114     }
115 }
116 
117 impl Timestamp {
118     /// Returns the current time.
now() -> Timestamp119     pub fn now() -> Timestamp {
120         SystemTime::now().try_into()
121             .expect("representable for the next hundred years")
122     }
123 
124     /// Adds a duration to this timestamp.
125     ///
126     /// Returns `None` if the resulting timestamp is not
127     /// representable.
checked_add(&self, d: Duration) -> Option<Timestamp>128     pub fn checked_add(&self, d: Duration) -> Option<Timestamp> {
129         self.0.checked_add(d.0).map(|v| Self(v))
130     }
131 
132     /// Subtracts a duration from this timestamp.
133     ///
134     /// Returns `None` if the resulting timestamp is not
135     /// representable.
checked_sub(&self, d: Duration) -> Option<Timestamp>136     pub fn checked_sub(&self, d: Duration) -> Option<Timestamp> {
137         self.0.checked_sub(d.0).map(|v| Self(v))
138     }
139 
140     /// Rounds down to the given level of precision.
141     ///
142     /// This can be used to reduce the metadata leak resulting from
143     /// time stamps.  For example, a group of people attending a key
144     /// signing event could be identified by comparing the time stamps
145     /// of resulting certifications.  By rounding the creation time of
146     /// these signatures down, all of them, and others, fall into the
147     /// same bucket.
148     ///
149     /// The given level `p` determines the resulting resolution of
150     /// `2^p` seconds.  The default is `21`, which results in a
151     /// resolution of 24 days, or roughly a month.  `p` must be lower
152     /// than 32.
153     ///
154     /// The lower limit `floor` represents the earliest time the timestamp will be
155     /// rounded down to.
156     ///
157     /// See also [`Duration::round_up`](struct.Duration.html#method.round_up).
158     ///
159     /// # Important note
160     ///
161     /// If we create a signature, it is important that the signature's
162     /// creation time does not predate the signing keys creation time,
163     /// or otherwise violate the key's validity constraints.
164     /// This can be achieved by using the `floor` parameter.
165     ///
166     /// To ensure validity, use this function to round the time down,
167     /// using the latest known relevant timestamp as a floor.
168     /// Then, lookup all keys and other objects like userids using this
169     /// timestamp, and on success create the signature:
170     ///
171     /// ```rust
172     /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*};
173     /// use sequoia_openpgp::policy::StandardPolicy;
174     ///
175     /// # f().unwrap();
176     /// # fn f() -> Result<()> {
177     /// let policy = &StandardPolicy::new();
178     ///
179     /// // Let's fix a time.
180     /// let now = Timestamp::from(1583436160);
181     ///
182     /// let cert_creation_alice = now.checked_sub(Duration::weeks(2)?).unwrap();
183     /// let cert_creation_bob = now.checked_sub(Duration::weeks(1)?).unwrap();
184     ///
185     /// // Generate a Cert for Alice.
186     /// let (alice, _) = CertBuilder::new()
187     ///     .set_creation_time(cert_creation_alice)
188     ///     .set_primary_key_flags(KeyFlags::empty().set_certification())
189     ///     .add_userid("alice@example.org")
190     ///     .generate()?;
191     ///
192     /// // Generate a Cert for Bob.
193     /// let (bob, _) = CertBuilder::new()
194     ///     .set_creation_time(cert_creation_bob)
195     ///     .set_primary_key_flags(KeyFlags::empty().set_certification())
196     ///     .add_userid("bob@example.org")
197     ///     .generate()?;
198     ///
199     /// let sign_with_p = |p| -> Result<Signature> {
200     ///     // Round `now` down, then use `t` for all lookups.
201     ///     // Use the creation time of Bob's Cert as lower bound for rounding.
202     ///     let t: std::time::SystemTime = now.round_down(p, cert_creation_bob)?.into();
203     ///
204     ///     // First, get the certification key.
205     ///     let mut keypair =
206     ///         alice.keys().with_policy(policy, t).secret().for_certification()
207     ///         .nth(0).ok_or_else(|| anyhow::anyhow!("no valid key at"))?
208     ///         .key().clone().into_keypair()?;
209     ///
210     ///     // Then, lookup the binding between `bob@example.org` and
211     ///     // `bob` at `t`.
212     ///     let ca = bob.userids().with_policy(policy, t)
213     ///         .filter(|ca| ca.userid().value() == b"bob@example.org")
214     ///         .nth(0).ok_or_else(|| anyhow::anyhow!("no valid userid"))?;
215     ///
216     ///     // Finally, Alice certifies the binding between
217     ///     // `bob@example.org` and `bob` at `t`.
218     ///     ca.userid().certify(&mut keypair, &bob,
219     ///                         SignatureType::PositiveCertification, None, t)
220     /// };
221     ///
222     /// assert!(sign_with_p(21).is_ok());
223     /// assert!(sign_with_p(22).is_ok());  // Rounded to bob's cert's creation time.
224     /// assert!(sign_with_p(32).is_err()); // Invalid precision
225     /// # Ok(()) }
226     /// ```
round_down<P, F>(&self, precision: P, floor: F) -> Result<Timestamp> where P: Into<Option<u8>>, F: Into<Option<SystemTime>>227     pub fn round_down<P, F>(&self, precision: P, floor: F) -> Result<Timestamp>
228         where P: Into<Option<u8>>,
229               F: Into<Option<SystemTime>>
230     {
231         let p = precision.into().unwrap_or(21) as u32;
232         if p < 32 {
233             let rounded = Self(self.0 & !((1 << p) - 1));
234             match floor.into() {
235                 Some(floor) => {
236                     Ok(cmp::max(rounded, floor.try_into()?))
237                 }
238                 None => { Ok(rounded) }
239             }
240         } else {
241             Err(Error::InvalidArgument(
242                 format!("Invalid precision {}", p)).into())
243         }
244     }
245 }
246 
247 #[cfg(any(test, feature = "quickcheck"))]
248 impl Arbitrary for Timestamp {
arbitrary<G: Gen>(g: &mut G) -> Self249     fn arbitrary<G: Gen>(g: &mut G) -> Self {
250         Timestamp(u32::arbitrary(g))
251     }
252 }
253 
254 /// A duration representable by OpenPGP.
255 ///
256 /// # Examples
257 ///
258 /// ```
259 /// use sequoia_openpgp as openpgp;
260 /// # use openpgp::Result;
261 /// use openpgp::cert::prelude::*;
262 /// use openpgp::policy::StandardPolicy;
263 /// use openpgp::packet::signature::subpacket::{SubpacketTag, SubpacketValue};
264 /// use openpgp::types::{Timestamp, Duration};
265 ///
266 /// # fn main() -> Result<()> {
267 /// let p = &StandardPolicy::new();
268 ///
269 /// let now = Timestamp::now();
270 /// let validity_period = Duration::days(365)?;
271 ///
272 /// let (cert,_) = CertBuilder::new()
273 ///     .set_creation_time(now)
274 ///     .set_validity_period(validity_period)
275 ///     .generate()?;
276 ///
277 /// let vc = cert.with_policy(p, now)?;
278 /// assert!(vc.alive().is_ok());
279 /// # Ok(()) }
280 /// ```
281 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
282 pub struct Duration(u32);
283 
284 impl From<Duration> for u32 {
from(d: Duration) -> Self285     fn from(d: Duration) -> Self {
286         d.0
287     }
288 }
289 
290 impl From<u32> for Duration {
from(d: u32) -> Self291     fn from(d: u32) -> Self {
292         Duration(d)
293     }
294 }
295 
296 impl TryFrom<SystemDuration> for Duration {
297     type Error = anyhow::Error;
298 
try_from(d: SystemDuration) -> Result<Self>299     fn try_from(d: SystemDuration) -> Result<Self> {
300         if d.as_secs() <= std::u32::MAX as u64 {
301             Ok(Duration(d.as_secs() as u32))
302         } else {
303             Err(Error::InvalidArgument(
304                 format!("Duration exceeds u32: {:?}", d))
305                      .into())
306         }
307     }
308 }
309 
310 impl From<Duration> for SystemDuration {
from(d: Duration) -> Self311     fn from(d: Duration) -> Self {
312         SystemDuration::new(d.0 as u64, 0)
313     }
314 }
315 
316 impl From<Duration> for Option<SystemDuration> {
from(d: Duration) -> Self317     fn from(d: Duration) -> Self {
318         Some(d.into())
319     }
320 }
321 
322 impl fmt::Debug for Duration {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result323     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
324         write!(f, "{:?}", SystemDuration::from(*self))
325     }
326 }
327 
328 impl Duration {
329     /// Returns a `Duration` with the given number of seconds.
seconds(n: u32) -> Duration330     pub fn seconds(n: u32) -> Duration {
331         n.into()
332     }
333 
334     /// Returns a `Duration` with the given number of minutes, if
335     /// representable.
minutes(n: u32) -> Result<Duration>336     pub fn minutes(n: u32) -> Result<Duration> {
337         60u32.checked_mul(n).ok_or(())
338             .map(Self::seconds)
339             .map_err(|_| Error::InvalidArgument(
340                 format!("Not representable: {} minutes in seconds exceeds u32",
341                         n)).into())
342     }
343 
344     /// Returns a `Duration` with the given number of hours, if
345     /// representable.
hours(n: u32) -> Result<Duration>346     pub fn hours(n: u32) -> Result<Duration> {
347         60u32.checked_mul(n)
348             .ok_or(Error::InvalidArgument("".into()).into())
349             .and_then(Self::minutes)
350             .map_err(|_| Error::InvalidArgument(
351                 format!("Not representable: {} hours in seconds exceeds u32",
352                         n)).into())
353     }
354 
355     /// Returns a `Duration` with the given number of days, if
356     /// representable.
days(n: u32) -> Result<Duration>357     pub fn days(n: u32) -> Result<Duration> {
358         24u32.checked_mul(n)
359             .ok_or(Error::InvalidArgument("".into()).into())
360             .and_then(Self::hours)
361             .map_err(|_| Error::InvalidArgument(
362                 format!("Not representable: {} days in seconds exceeds u32",
363                         n)).into())
364     }
365 
366     /// Returns a `Duration` with the given number of weeks, if
367     /// representable.
weeks(n: u32) -> Result<Duration>368     pub fn weeks(n: u32) -> Result<Duration> {
369         7u32.checked_mul(n)
370             .ok_or(Error::InvalidArgument("".into()).into())
371             .and_then(Self::days)
372             .map_err(|_| Error::InvalidArgument(
373                 format!("Not representable: {} weeks in seconds exceeds u32",
374                         n)).into())
375     }
376 
377     /// Returns the duration as seconds.
as_secs(self) -> u64378     pub fn as_secs(self) -> u64 {
379         self.0 as u64
380     }
381 
382     /// Rounds up to the given level of precision.
383     ///
384     /// If [`Timestamp::round_down`] is used to round the creation
385     /// timestamp of a key or signature down, then this function may
386     /// be used to round the corresponding expiration time up.  This
387     /// ensures validity during the originally intended lifetime,
388     /// while avoiding the metadata leak associated with preserving
389     /// the originally intended expiration time.
390     ///
391     ///   [`Timestamp::round_down`]: struct.Timestamp.html#method.round_down
392     ///
393     /// The given level `p` determines the resulting resolution of
394     /// `2^p` seconds.  The default is `21`, which results in a
395     /// resolution of 24 days, or roughly a month.  `p` must be lower
396     /// than 32.
397     ///
398     /// The upper limit `ceil` represents the maximum time to round up to.
round_up<P, C>(&self, precision: P, ceil: C) -> Result<Duration> where P: Into<Option<u8>>, C: Into<Option<SystemDuration>>399     pub fn round_up<P, C>(&self, precision: P, ceil: C) -> Result<Duration>
400         where P: Into<Option<u8>>,
401               C: Into<Option<SystemDuration>>
402     {
403         let p = precision.into().unwrap_or(21) as u32;
404         if p < 32 {
405             if let Some(sum) = self.0.checked_add((1 << p) - 1) {
406                 let rounded = Self(sum & !((1 << p) - 1));
407                 match ceil.into() {
408                     Some(ceil) => {
409                         Ok(cmp::min(rounded, ceil.try_into()?))
410                     },
411                     None => Ok(rounded)
412                 }
413             } else {
414                 Ok(Self(std::u32::MAX))
415             }
416         } else {
417             Err(Error::InvalidArgument(
418                 format!("Invalid precision {}", p)).into())
419         }
420     }
421 }
422 
423 #[allow(unused)]
424 impl Timestamp {
425     pub(crate) const UNIX_EPOCH : Timestamp = Timestamp(0);
426     pub(crate) const MAX : Timestamp = Timestamp(u32::MAX);
427 
428     // for y in $(seq 1970 2106); do echo "    const Y$y : Timestamp = Timestamp($(date -u --date="Jan. 1, $y" '+%s'));"; done
429     pub(crate) const Y1970 : Timestamp = Timestamp(0);
430     pub(crate) const Y1971 : Timestamp = Timestamp(31536000);
431     pub(crate) const Y1972 : Timestamp = Timestamp(63072000);
432     pub(crate) const Y1973 : Timestamp = Timestamp(94694400);
433     pub(crate) const Y1974 : Timestamp = Timestamp(126230400);
434     pub(crate) const Y1975 : Timestamp = Timestamp(157766400);
435     pub(crate) const Y1976 : Timestamp = Timestamp(189302400);
436     pub(crate) const Y1977 : Timestamp = Timestamp(220924800);
437     pub(crate) const Y1978 : Timestamp = Timestamp(252460800);
438     pub(crate) const Y1979 : Timestamp = Timestamp(283996800);
439     pub(crate) const Y1980 : Timestamp = Timestamp(315532800);
440     pub(crate) const Y1981 : Timestamp = Timestamp(347155200);
441     pub(crate) const Y1982 : Timestamp = Timestamp(378691200);
442     pub(crate) const Y1983 : Timestamp = Timestamp(410227200);
443     pub(crate) const Y1984 : Timestamp = Timestamp(441763200);
444     pub(crate) const Y1985 : Timestamp = Timestamp(473385600);
445     pub(crate) const Y1986 : Timestamp = Timestamp(504921600);
446     pub(crate) const Y1987 : Timestamp = Timestamp(536457600);
447     pub(crate) const Y1988 : Timestamp = Timestamp(567993600);
448     pub(crate) const Y1989 : Timestamp = Timestamp(599616000);
449     pub(crate) const Y1990 : Timestamp = Timestamp(631152000);
450     pub(crate) const Y1991 : Timestamp = Timestamp(662688000);
451     pub(crate) const Y1992 : Timestamp = Timestamp(694224000);
452     pub(crate) const Y1993 : Timestamp = Timestamp(725846400);
453     pub(crate) const Y1994 : Timestamp = Timestamp(757382400);
454     pub(crate) const Y1995 : Timestamp = Timestamp(788918400);
455     pub(crate) const Y1996 : Timestamp = Timestamp(820454400);
456     pub(crate) const Y1997 : Timestamp = Timestamp(852076800);
457     pub(crate) const Y1998 : Timestamp = Timestamp(883612800);
458     pub(crate) const Y1999 : Timestamp = Timestamp(915148800);
459     pub(crate) const Y2000 : Timestamp = Timestamp(946684800);
460     pub(crate) const Y2001 : Timestamp = Timestamp(978307200);
461     pub(crate) const Y2002 : Timestamp = Timestamp(1009843200);
462     pub(crate) const Y2003 : Timestamp = Timestamp(1041379200);
463     pub(crate) const Y2004 : Timestamp = Timestamp(1072915200);
464     pub(crate) const Y2005 : Timestamp = Timestamp(1104537600);
465     pub(crate) const Y2006 : Timestamp = Timestamp(1136073600);
466     pub(crate) const Y2007 : Timestamp = Timestamp(1167609600);
467     pub(crate) const Y2008 : Timestamp = Timestamp(1199145600);
468     pub(crate) const Y2009 : Timestamp = Timestamp(1230768000);
469     pub(crate) const Y2010 : Timestamp = Timestamp(1262304000);
470     pub(crate) const Y2011 : Timestamp = Timestamp(1293840000);
471     pub(crate) const Y2012 : Timestamp = Timestamp(1325376000);
472     pub(crate) const Y2013 : Timestamp = Timestamp(1356998400);
473     pub(crate) const Y2014 : Timestamp = Timestamp(1388534400);
474     pub(crate) const Y2015 : Timestamp = Timestamp(1420070400);
475     pub(crate) const Y2016 : Timestamp = Timestamp(1451606400);
476     pub(crate) const Y2017 : Timestamp = Timestamp(1483228800);
477     pub(crate) const Y2018 : Timestamp = Timestamp(1514764800);
478     pub(crate) const Y2019 : Timestamp = Timestamp(1546300800);
479     pub(crate) const Y2020 : Timestamp = Timestamp(1577836800);
480     pub(crate) const Y2021 : Timestamp = Timestamp(1609459200);
481     pub(crate) const Y2022 : Timestamp = Timestamp(1640995200);
482     pub(crate) const Y2023 : Timestamp = Timestamp(1672531200);
483     pub(crate) const Y2024 : Timestamp = Timestamp(1704067200);
484     pub(crate) const Y2025 : Timestamp = Timestamp(1735689600);
485     pub(crate) const Y2026 : Timestamp = Timestamp(1767225600);
486     pub(crate) const Y2027 : Timestamp = Timestamp(1798761600);
487     pub(crate) const Y2028 : Timestamp = Timestamp(1830297600);
488     pub(crate) const Y2029 : Timestamp = Timestamp(1861920000);
489     pub(crate) const Y2030 : Timestamp = Timestamp(1893456000);
490     pub(crate) const Y2031 : Timestamp = Timestamp(1924992000);
491     pub(crate) const Y2032 : Timestamp = Timestamp(1956528000);
492     pub(crate) const Y2033 : Timestamp = Timestamp(1988150400);
493     pub(crate) const Y2034 : Timestamp = Timestamp(2019686400);
494     pub(crate) const Y2035 : Timestamp = Timestamp(2051222400);
495     pub(crate) const Y2036 : Timestamp = Timestamp(2082758400);
496     pub(crate) const Y2037 : Timestamp = Timestamp(2114380800);
497     pub(crate) const Y2038 : Timestamp = Timestamp(2145916800);
498     pub(crate) const Y2039 : Timestamp = Timestamp(2177452800);
499     pub(crate) const Y2040 : Timestamp = Timestamp(2208988800);
500     pub(crate) const Y2041 : Timestamp = Timestamp(2240611200);
501     pub(crate) const Y2042 : Timestamp = Timestamp(2272147200);
502     pub(crate) const Y2043 : Timestamp = Timestamp(2303683200);
503     pub(crate) const Y2044 : Timestamp = Timestamp(2335219200);
504     pub(crate) const Y2045 : Timestamp = Timestamp(2366841600);
505     pub(crate) const Y2046 : Timestamp = Timestamp(2398377600);
506     pub(crate) const Y2047 : Timestamp = Timestamp(2429913600);
507     pub(crate) const Y2048 : Timestamp = Timestamp(2461449600);
508     pub(crate) const Y2049 : Timestamp = Timestamp(2493072000);
509     pub(crate) const Y2050 : Timestamp = Timestamp(2524608000);
510     pub(crate) const Y2051 : Timestamp = Timestamp(2556144000);
511     pub(crate) const Y2052 : Timestamp = Timestamp(2587680000);
512     pub(crate) const Y2053 : Timestamp = Timestamp(2619302400);
513     pub(crate) const Y2054 : Timestamp = Timestamp(2650838400);
514     pub(crate) const Y2055 : Timestamp = Timestamp(2682374400);
515     pub(crate) const Y2056 : Timestamp = Timestamp(2713910400);
516     pub(crate) const Y2057 : Timestamp = Timestamp(2745532800);
517     pub(crate) const Y2058 : Timestamp = Timestamp(2777068800);
518     pub(crate) const Y2059 : Timestamp = Timestamp(2808604800);
519     pub(crate) const Y2060 : Timestamp = Timestamp(2840140800);
520     pub(crate) const Y2061 : Timestamp = Timestamp(2871763200);
521     pub(crate) const Y2062 : Timestamp = Timestamp(2903299200);
522     pub(crate) const Y2063 : Timestamp = Timestamp(2934835200);
523     pub(crate) const Y2064 : Timestamp = Timestamp(2966371200);
524     pub(crate) const Y2065 : Timestamp = Timestamp(2997993600);
525     pub(crate) const Y2066 : Timestamp = Timestamp(3029529600);
526     pub(crate) const Y2067 : Timestamp = Timestamp(3061065600);
527     pub(crate) const Y2068 : Timestamp = Timestamp(3092601600);
528     pub(crate) const Y2069 : Timestamp = Timestamp(3124224000);
529     pub(crate) const Y2070 : Timestamp = Timestamp(3155760000);
530     pub(crate) const Y2071 : Timestamp = Timestamp(3187296000);
531     pub(crate) const Y2072 : Timestamp = Timestamp(3218832000);
532     pub(crate) const Y2073 : Timestamp = Timestamp(3250454400);
533     pub(crate) const Y2074 : Timestamp = Timestamp(3281990400);
534     pub(crate) const Y2075 : Timestamp = Timestamp(3313526400);
535     pub(crate) const Y2076 : Timestamp = Timestamp(3345062400);
536     pub(crate) const Y2077 : Timestamp = Timestamp(3376684800);
537     pub(crate) const Y2078 : Timestamp = Timestamp(3408220800);
538     pub(crate) const Y2079 : Timestamp = Timestamp(3439756800);
539     pub(crate) const Y2080 : Timestamp = Timestamp(3471292800);
540     pub(crate) const Y2081 : Timestamp = Timestamp(3502915200);
541     pub(crate) const Y2082 : Timestamp = Timestamp(3534451200);
542     pub(crate) const Y2083 : Timestamp = Timestamp(3565987200);
543     pub(crate) const Y2084 : Timestamp = Timestamp(3597523200);
544     pub(crate) const Y2085 : Timestamp = Timestamp(3629145600);
545     pub(crate) const Y2086 : Timestamp = Timestamp(3660681600);
546     pub(crate) const Y2087 : Timestamp = Timestamp(3692217600);
547     pub(crate) const Y2088 : Timestamp = Timestamp(3723753600);
548     pub(crate) const Y2089 : Timestamp = Timestamp(3755376000);
549     pub(crate) const Y2090 : Timestamp = Timestamp(3786912000);
550     pub(crate) const Y2091 : Timestamp = Timestamp(3818448000);
551     pub(crate) const Y2092 : Timestamp = Timestamp(3849984000);
552     pub(crate) const Y2093 : Timestamp = Timestamp(3881606400);
553     pub(crate) const Y2094 : Timestamp = Timestamp(3913142400);
554     pub(crate) const Y2095 : Timestamp = Timestamp(3944678400);
555     pub(crate) const Y2096 : Timestamp = Timestamp(3976214400);
556     pub(crate) const Y2097 : Timestamp = Timestamp(4007836800);
557     pub(crate) const Y2098 : Timestamp = Timestamp(4039372800);
558     pub(crate) const Y2099 : Timestamp = Timestamp(4070908800);
559     pub(crate) const Y2100 : Timestamp = Timestamp(4102444800);
560     pub(crate) const Y2101 : Timestamp = Timestamp(4133980800);
561     pub(crate) const Y2102 : Timestamp = Timestamp(4165516800);
562     pub(crate) const Y2103 : Timestamp = Timestamp(4197052800);
563     pub(crate) const Y2104 : Timestamp = Timestamp(4228588800);
564     pub(crate) const Y2105 : Timestamp = Timestamp(4260211200);
565     pub(crate) const Y2106 : Timestamp = Timestamp(4291747200);
566 }
567 
568 #[cfg(any(test, feature = "quickcheck"))]
569 impl Arbitrary for Duration {
arbitrary<G: Gen>(g: &mut G) -> Self570     fn arbitrary<G: Gen>(g: &mut G) -> Self {
571         Duration(u32::arbitrary(g))
572     }
573 }
574 
575 /// Normalizes the given SystemTime to the resolution OpenPGP
576 /// supports.
normalize_systemtime(t: SystemTime) -> SystemTime577 pub(crate) fn normalize_systemtime(t: SystemTime) -> SystemTime {
578     UNIX_EPOCH + SystemDuration::new(
579         t.duration_since(UNIX_EPOCH).unwrap().as_secs(), 0)
580 }
581 
582 #[cfg(test)]
583 mod tests {
584     use super::*;
585 
586     quickcheck! {
587         fn timestamp_round_down(t: Timestamp) -> bool {
588             let u = t.round_down(None, None).unwrap();
589             assert!(u <= t);
590             assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
591             assert!(u32::from(t) - u32::from(u) < 2_u32.pow(21));
592             true
593         }
594     }
595 
596     #[test]
timestamp_round_down_floor() -> Result<()>597     fn timestamp_round_down_floor() -> Result<()> {
598         let t = Timestamp(1585753307);
599         let floor = t.checked_sub(Duration::weeks(1).unwrap()).unwrap();
600 
601         let u = t.round_down(21, floor).unwrap();
602         assert!(u < t);
603         assert!(floor < u);
604         assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
605 
606         let floor = t.checked_sub(Duration::days(1).unwrap()).unwrap();
607 
608         let u = t.round_down(21, floor).unwrap();
609         assert_eq!(u, floor);
610         Ok(())
611     }
612 
613     quickcheck! {
614         fn duration_round_up(d: Duration) -> bool {
615             let u = d.round_up(None, None).unwrap();
616             assert!(d <= u);
617             assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
618             assert!(u32::from(u) - u32::from(d) < 2_u32.pow(21));
619             true
620         }
621     }
622 
623     #[test]
duration_round_up_ceil() -> Result<()>624     fn duration_round_up_ceil() -> Result<()> {
625         let d = Duration(123);
626 
627         let ceil = Duration(2_u32.pow(23));
628 
629         let u = d.round_up(21, ceil)?;
630         assert!(d < u);
631         assert!(u < ceil);
632         assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111_1111, 0);
633 
634         let ceil = Duration::days(1).unwrap();
635 
636         let u = d.round_up(21, ceil)?;
637         assert!(d < u);
638         assert_eq!(u, ceil);
639 
640         Ok(())
641     }
642 }
643