Coverage Report

Created: 2025-09-27 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-webpki-0.103.5/src/time.rs
Line
Count
Source
1
// Copyright 2015-2016 Brian Smith.
2
//
3
// Permission to use, copy, modify, and/or distribute this software for any
4
// purpose with or without fee is hereby granted, provided that the above
5
// copyright notice and this permission notice appear in all copies.
6
//
7
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
10
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15
//! Conversions into the library's time type.
16
17
use core::time::Duration;
18
19
use pki_types::UnixTime;
20
21
use crate::der::{self, FromDer, Tag};
22
use crate::error::{DerTypeId, Error};
23
24
impl<'a> FromDer<'a> for UnixTime {
25
3.61k
    fn from_der(input: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
26
3.61k
        let is_utc_time = input.peek(Tag::UTCTime.into());
27
3.61k
        let expected_tag = if is_utc_time {
28
1.84k
            Tag::UTCTime
29
        } else {
30
1.77k
            Tag::GeneralizedTime
31
        };
32
33
45.3k
        fn read_digit(inner: &mut untrusted::Reader<'_>) -> Result<u64, Error> {
34
            const DIGIT: core::ops::RangeInclusive<u8> = b'0'..=b'9';
35
45.3k
            let b = inner.read_byte().map_err(|_| Error::BadDerTime)?;
36
45.2k
            if DIGIT.contains(&b) {
37
45.2k
                return Ok(u64::from(b - DIGIT.start()));
38
50
            }
39
50
            Err(Error::BadDerTime)
40
45.3k
        }
41
42
22.6k
        fn read_two_digits(
43
22.6k
            inner: &mut untrusted::Reader<'_>,
44
22.6k
            min: u64,
45
22.6k
            max: u64,
46
22.6k
        ) -> Result<u64, Error> {
47
22.6k
            let hi = read_digit(inner)?;
48
22.6k
            let lo = read_digit(inner)?;
49
22.5k
            let value = (hi * 10) + lo;
50
22.5k
            if value < min || value > max {
51
22
                return Err(Error::BadDerTime);
52
22.5k
            }
53
22.5k
            Ok(value)
54
22.6k
        }
55
56
3.61k
        der::nested(
57
3.61k
            input,
58
3.61k
            expected_tag,
59
3.61k
            Error::TrailingData(Self::TYPE_ID),
60
3.54k
            |value| {
61
3.54k
                let (year_hi, year_lo) = if is_utc_time {
62
1.82k
                    let lo = read_two_digits(value, 0, 99)?;
63
1.81k
                    let hi = if lo >= 50 { 19 } else { 20 };
64
1.81k
                    (hi, lo)
65
                } else {
66
1.71k
                    let hi = read_two_digits(value, 0, 99)?;
67
1.70k
                    let lo = read_two_digits(value, 0, 99)?;
68
1.70k
                    (hi, lo)
69
                };
70
71
3.51k
                let year = (year_hi * 100) + year_lo;
72
3.51k
                let month = read_two_digits(value, 1, 12)?;
73
3.50k
                let days_in_month = days_in_month(year, month);
74
3.50k
                let day_of_month = read_two_digits(value, 1, days_in_month)?;
75
3.48k
                let hours = read_two_digits(value, 0, 23)?;
76
3.46k
                let minutes = read_two_digits(value, 0, 59)?;
77
3.45k
                let seconds = read_two_digits(value, 0, 59)?;
78
79
3.43k
                let time_zone = value.read_byte().map_err(|_| Error::BadDerTime)?;
80
3.43k
                if time_zone != b'Z' {
81
9
                    return Err(Error::BadDerTime);
82
3.42k
                }
83
84
3.42k
                time_from_ymdhms_utc(year, month, day_of_month, hours, minutes, seconds)
85
3.54k
            },
86
        )
87
3.61k
    }
88
89
    const TYPE_ID: DerTypeId = DerTypeId::Time;
90
}
91
92
3.42k
pub(crate) fn time_from_ymdhms_utc(
93
3.42k
    year: u64,
94
3.42k
    month: u64,
95
3.42k
    day_of_month: u64,
96
3.42k
    hours: u64,
97
3.42k
    minutes: u64,
98
3.42k
    seconds: u64,
99
3.42k
) -> Result<UnixTime, Error> {
100
3.42k
    let days_before_year_since_unix_epoch = days_before_year_since_unix_epoch(year)?;
101
102
    const JAN: u64 = 31;
103
3.41k
    let feb = days_in_feb(year);
104
    const MAR: u64 = 31;
105
    const APR: u64 = 30;
106
    const MAY: u64 = 31;
107
    const JUN: u64 = 30;
108
    const JUL: u64 = 31;
109
    const AUG: u64 = 31;
110
    const SEP: u64 = 30;
111
    const OCT: u64 = 31;
112
    const NOV: u64 = 30;
113
3.41k
    let days_before_month_in_year = match month {
114
1.92k
        1 => 0,
115
454
        2 => JAN,
116
139
        3 => JAN + feb,
117
219
        4 => JAN + feb + MAR,
118
77
        5 => JAN + feb + MAR + APR,
119
52
        6 => JAN + feb + MAR + APR + MAY,
120
61
        7 => JAN + feb + MAR + APR + MAY + JUN,
121
86
        8 => JAN + feb + MAR + APR + MAY + JUN + JUL,
122
119
        9 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG,
123
147
        10 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG + SEP,
124
71
        11 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG + SEP + OCT,
125
70
        12 => JAN + feb + MAR + APR + MAY + JUN + JUL + AUG + SEP + OCT + NOV,
126
0
        _ => unreachable!(), // `read_two_digits` already bounds-checked it.
127
    };
128
129
3.41k
    let days_before =
130
3.41k
        days_before_year_since_unix_epoch + days_before_month_in_year + day_of_month - 1;
131
132
3.41k
    let seconds_since_unix_epoch =
133
3.41k
        (days_before * 24 * 60 * 60) + (hours * 60 * 60) + (minutes * 60) + seconds;
134
135
3.41k
    Ok(UnixTime::since_unix_epoch(Duration::from_secs(
136
3.41k
        seconds_since_unix_epoch,
137
3.41k
    )))
138
3.42k
}
139
140
3.42k
fn days_before_year_since_unix_epoch(year: u64) -> Result<u64, Error> {
141
    // We don't support dates before January 1, 1970 because that is the
142
    // Unix epoch. It is likely that other software won't deal well with
143
    // certificates that have dates before the epoch.
144
3.42k
    if year < UNIX_EPOCH_YEAR {
145
9
        return Err(Error::BadDerTime);
146
3.41k
    }
147
3.41k
    let days_before_year_ad = days_before_year_ad(year);
148
3.41k
    debug_assert!(days_before_year_ad >= DAYS_BEFORE_UNIX_EPOCH_AD);
149
3.41k
    Ok(days_before_year_ad - DAYS_BEFORE_UNIX_EPOCH_AD)
150
3.42k
}
151
152
const UNIX_EPOCH_YEAR: u64 = 1970;
153
154
3.41k
fn days_before_year_ad(year: u64) -> u64 {
155
3.41k
    ((year - 1) * 365)
156
3.41k
        + ((year - 1) / 4)    // leap years are every 4 years,
157
3.41k
        - ((year - 1) / 100)  // except years divisible by 100,
158
3.41k
        + ((year - 1) / 400) // except years divisible by 400.
159
3.41k
}
160
161
3.50k
pub(crate) fn days_in_month(year: u64, month: u64) -> u64 {
162
3.50k
    match month {
163
2.56k
        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
164
478
        4 | 6 | 9 | 11 => 30,
165
465
        2 => days_in_feb(year),
166
0
        _ => unreachable!(), // `read_two_digits` already bounds-checked it.
167
    }
168
3.50k
}
169
170
3.88k
fn days_in_feb(year: u64) -> u64 {
171
3.88k
    if (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) {
172
1.61k
        29
173
    } else {
174
2.26k
        28
175
    }
176
3.88k
}
177
178
/// All the days up to and including 1969, plus the 477 leap days since AD began
179
/// (calculated in Gregorian rules).
180
const DAYS_BEFORE_UNIX_EPOCH_AD: u64 = 1969 * 365 + 477;
181
182
#[cfg(test)]
183
mod tests {
184
    use super::*;
185
186
    #[test]
187
    fn test_days_before_unix_epoch() {
188
        assert_eq!(
189
            DAYS_BEFORE_UNIX_EPOCH_AD,
190
            days_before_year_ad(UNIX_EPOCH_YEAR)
191
        );
192
    }
193
194
    #[test]
195
    fn test_days_before_year_since_unix_epoch() {
196
        assert_eq!(Ok(0), days_before_year_since_unix_epoch(UNIX_EPOCH_YEAR));
197
        assert_eq!(
198
            Ok(365),
199
            days_before_year_since_unix_epoch(UNIX_EPOCH_YEAR + 1)
200
        );
201
        assert_eq!(
202
            Err(Error::BadDerTime),
203
            days_before_year_since_unix_epoch(UNIX_EPOCH_YEAR - 1)
204
        );
205
    }
206
207
    #[test]
208
    fn test_days_in_month() {
209
        assert_eq!(days_in_month(2017, 1), 31);
210
        assert_eq!(days_in_month(2017, 2), 28);
211
        assert_eq!(days_in_month(2017, 3), 31);
212
        assert_eq!(days_in_month(2017, 4), 30);
213
        assert_eq!(days_in_month(2017, 5), 31);
214
        assert_eq!(days_in_month(2017, 6), 30);
215
        assert_eq!(days_in_month(2017, 7), 31);
216
        assert_eq!(days_in_month(2017, 8), 31);
217
        assert_eq!(days_in_month(2017, 9), 30);
218
        assert_eq!(days_in_month(2017, 10), 31);
219
        assert_eq!(days_in_month(2017, 11), 30);
220
        assert_eq!(days_in_month(2017, 12), 31);
221
222
        // leap cases
223
        assert_eq!(days_in_month(2000, 2), 29);
224
        assert_eq!(days_in_month(2004, 2), 29);
225
        assert_eq!(days_in_month(2016, 2), 29);
226
        assert_eq!(days_in_month(2100, 2), 28);
227
    }
228
229
    #[test]
230
    fn test_time_from_ymdhms_utc() {
231
        // 1969-12-31 00:00:00
232
        assert_eq!(
233
            Err(Error::BadDerTime),
234
            time_from_ymdhms_utc(UNIX_EPOCH_YEAR - 1, 1, 1, 0, 0, 0)
235
        );
236
237
        // 1969-12-31 23:59:59
238
        assert_eq!(
239
            Err(Error::BadDerTime),
240
            time_from_ymdhms_utc(UNIX_EPOCH_YEAR - 1, 12, 31, 23, 59, 59)
241
        );
242
243
        // 1970-01-01 00:00:00
244
        assert_eq!(
245
            UnixTime::since_unix_epoch(Duration::from_secs(0)),
246
            time_from_ymdhms_utc(UNIX_EPOCH_YEAR, 1, 1, 0, 0, 0).unwrap()
247
        );
248
249
        // 1970-01-01 00:00:01
250
        assert_eq!(
251
            UnixTime::since_unix_epoch(Duration::from_secs(1)),
252
            time_from_ymdhms_utc(UNIX_EPOCH_YEAR, 1, 1, 0, 0, 1).unwrap()
253
        );
254
255
        // 1971-01-01 00:00:00
256
        assert_eq!(
257
            UnixTime::since_unix_epoch(Duration::from_secs(365 * 86400)),
258
            time_from_ymdhms_utc(UNIX_EPOCH_YEAR + 1, 1, 1, 0, 0, 0).unwrap()
259
        );
260
261
        // year boundary
262
        assert_eq!(
263
            UnixTime::since_unix_epoch(Duration::from_secs(1_483_228_799)),
264
            time_from_ymdhms_utc(2016, 12, 31, 23, 59, 59).unwrap()
265
        );
266
        assert_eq!(
267
            UnixTime::since_unix_epoch(Duration::from_secs(1_483_228_800)),
268
            time_from_ymdhms_utc(2017, 1, 1, 0, 0, 0).unwrap()
269
        );
270
271
        // not a leap year
272
        assert_eq!(
273
            UnixTime::since_unix_epoch(Duration::from_secs(1_492_449_162)),
274
            time_from_ymdhms_utc(2017, 4, 17, 17, 12, 42).unwrap()
275
        );
276
277
        // leap year, post-feb
278
        assert_eq!(
279
            UnixTime::since_unix_epoch(Duration::from_secs(1_460_913_162)),
280
            time_from_ymdhms_utc(2016, 4, 17, 17, 12, 42).unwrap()
281
        );
282
    }
283
}