Coverage Report

Created: 2026-10-27 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/asn1/a_time.cc
Line
Count
Source
1
// Copyright 1999-2016 The OpenSSL Project Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include <openssl/asn1.h>
16
#include <openssl/posix_time.h>
17
18
#include <assert.h>
19
#include <stdio.h>
20
#include <string.h>
21
#include <time.h>
22
23
#include <openssl/asn1t.h>
24
#include <openssl/bytestring.h>
25
#include <openssl/err.h>
26
#include <openssl/mem.h>
27
28
#include "internal.h"
29
30
31
using namespace bssl;
32
33
// This is an implementation of the ASN1 Time structure which is: Time ::=
34
// CHOICE { utcTime UTCTime, generalTime GeneralizedTime } written by Steve
35
// Henson.
36
37
BSSL_NAMESPACE_BEGIN
38
39
IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME)
40
41
BSSL_NAMESPACE_END
42
43
IMPLEMENT_ASN1_FUNCTIONS_const(ASN1_TIME)
44
45
0
ASN1_TIME *ASN1_TIME_set_posix(ASN1_TIME *s, int64_t posix_time) {
46
0
  return ASN1_TIME_adj(s, posix_time, 0, 0);
47
0
}
48
49
0
ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t time) {
50
0
  return ASN1_TIME_adj(s, time, 0, 0);
51
0
}
52
53
0
static int fits_in_utc_time(const struct tm *tm) {
54
0
  return 50 <= tm->tm_year && tm->tm_year < 150;
55
0
}
56
57
ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, int64_t posix_time, int offset_day,
58
0
                         long offset_sec) {
59
0
  struct tm tm;
60
61
0
  if (!OPENSSL_posix_to_tm(posix_time, &tm)) {
62
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_ERROR_GETTING_TIME);
63
0
    return nullptr;
64
0
  }
65
0
  if (offset_day || offset_sec) {
66
0
    if (!OPENSSL_gmtime_adj(&tm, offset_day, offset_sec)) {
67
0
      return nullptr;
68
0
    }
69
0
  }
70
0
  if (fits_in_utc_time(&tm)) {
71
0
    return ASN1_UTCTIME_adj(s, posix_time, offset_day, offset_sec);
72
0
  }
73
0
  return ASN1_GENERALIZEDTIME_adj(s, posix_time, offset_day, offset_sec);
74
0
}
75
76
0
int ASN1_TIME_check(const ASN1_TIME *t) {
77
0
  if (t->type == V_ASN1_GENERALIZEDTIME) {
78
0
    return ASN1_GENERALIZEDTIME_check(t);
79
0
  } else if (t->type == V_ASN1_UTCTIME) {
80
0
    return ASN1_UTCTIME_check(t);
81
0
  }
82
0
  return 0;
83
0
}
84
85
// Convert an ASN1_TIME structure to GeneralizedTime
86
ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *in,
87
0
                                                   ASN1_GENERALIZEDTIME **out) {
88
0
  if (!ASN1_TIME_check(in)) {
89
0
    return nullptr;
90
0
  }
91
92
0
  UniquePtr<ASN1_GENERALIZEDTIME> tmp;
93
0
  ASN1_GENERALIZEDTIME *ret = nullptr;
94
0
  if (!out || !*out) {
95
0
    tmp.reset(ASN1_GENERALIZEDTIME_new());
96
0
    if (tmp == nullptr) {
97
0
      return nullptr;
98
0
    }
99
0
    ret = tmp.get();
100
0
  } else {
101
0
    ret = *out;
102
0
  }
103
104
  // If already GeneralizedTime just copy across
105
0
  if (in->type == V_ASN1_GENERALIZEDTIME) {
106
0
    if (!ASN1_STRING_set(ret, in->data, in->length)) {
107
0
      return nullptr;
108
0
    }
109
0
  } else {
110
0
    assert(in->type == V_ASN1_UTCTIME);
111
    // `ASN1_TIME_check` implies a bound on the string's lengths. In particular,
112
    // the longest possible UTCTime is "YYMMDDHHMMSS+HHMM", with the (invalid)
113
    // timezone offsets.
114
0
    static constexpr size_t kMaxUTCTimeLength = 17;
115
0
    BSSL_CHECK(in->length > 0 &&
116
0
               static_cast<size_t>(in->length) <= kMaxUTCTimeLength);
117
0
    char buf[2 /* century */ + kMaxUTCTimeLength + 1 /* NUL */];
118
    // Work out the century and prepend.
119
0
    if (in->data[0] >= '5') {
120
0
      snprintf(buf, sizeof(buf), "19%.*s", in->length,
121
0
               reinterpret_cast<const char *>(in->data));
122
0
    } else {
123
0
      snprintf(buf, sizeof(buf), "20%.*s", in->length,
124
0
               reinterpret_cast<const char *>(in->data));
125
0
    }
126
0
    if (!ASN1_STRING_set(ret, buf, -1 /* use strlen */)) {
127
0
      return nullptr;
128
0
    }
129
0
  }
130
131
0
  if (out != nullptr && *out == nullptr) {
132
0
    *out = ret;
133
0
  }
134
0
  tmp.release();  // Ownership passed to caller.
135
0
  return ret;
136
0
}
137
138
0
int ASN1_TIME_set_string(ASN1_TIME *s, const char *str) {
139
0
  return ASN1_UTCTIME_set_string(s, str) ||
140
0
         ASN1_GENERALIZEDTIME_set_string(s, str);
141
0
}
142
143
0
int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str) {
144
0
  CBS cbs;
145
0
  CBS_init(&cbs, (const uint8_t *)str, strlen(str));
146
0
  int type;
147
0
  struct tm tm;
148
0
  if (CBS_parse_utc_time(&cbs, /*out_tm=*/nullptr,
149
0
                         /*allow_timezone_offset=*/0)) {
150
0
    type = V_ASN1_UTCTIME;
151
0
  } else if (CBS_parse_generalized_time(&cbs, &tm,
152
0
                                        /*allow_timezone_offset=*/0)) {
153
0
    type = V_ASN1_GENERALIZEDTIME;
154
0
    if (fits_in_utc_time(&tm)) {
155
0
      type = V_ASN1_UTCTIME;
156
0
      CBS_skip(&cbs, 2);
157
0
    }
158
0
  } else {
159
0
    return 0;
160
0
  }
161
162
0
  if (s != nullptr) {
163
0
    if (!ASN1_STRING_set(s, CBS_data(&cbs), CBS_len(&cbs))) {
164
0
      return 0;
165
0
    }
166
0
    s->type = type;
167
0
  }
168
0
  return 1;
169
0
}
170
171
static int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *t,
172
0
                           int allow_timezone_offset) {
173
0
  if (t == nullptr) {
174
0
    if (OPENSSL_posix_to_tm(time(nullptr), tm)) {
175
0
      return 1;
176
0
    }
177
0
    return 0;
178
0
  }
179
180
0
  if (t->type == V_ASN1_UTCTIME) {
181
0
    return asn1_utctime_to_tm(tm, t, allow_timezone_offset);
182
0
  } else if (t->type == V_ASN1_GENERALIZEDTIME) {
183
0
    return asn1_generalizedtime_to_tm(tm, t);
184
0
  }
185
186
0
  return 0;
187
0
}
188
189
int ASN1_TIME_diff(int *out_days, int *out_seconds, const ASN1_TIME *from,
190
0
                   const ASN1_TIME *to) {
191
0
  struct tm tm_from, tm_to;
192
0
  if (!asn1_time_to_tm(&tm_from, from, /*allow_timezone_offset=*/1)) {
193
0
    return 0;
194
0
  }
195
0
  if (!asn1_time_to_tm(&tm_to, to, /*allow_timezone_offset=*/1)) {
196
0
    return 0;
197
0
  }
198
0
  return OPENSSL_gmtime_diff(out_days, out_seconds, &tm_from, &tm_to);
199
0
}
200
201
0
int ASN1_TIME_to_posix_nonstandard(const ASN1_TIME *t, int64_t *out_time) {
202
0
  struct tm tm;
203
0
  if (!asn1_time_to_tm(&tm, t, /*allow_timezone_offset=*/1)) {
204
0
    return 0;
205
0
  }
206
0
  return OPENSSL_tm_to_posix(&tm, out_time);
207
0
}
208
209
// The functions below do *not* permissively allow the use of four digit
210
// timezone offsets in UTC times, as is done elsewhere in the code. They are
211
// both new API, and used internally to X509_cmp_time. This is to discourage the
212
// use of nonstandard times in new code, and to ensure that this code behaves
213
// correctly in X509_cmp_time which historically did its own time validations
214
// slightly different than the many other copies of X.509 time validation
215
// sprinkled through the codebase. The custom checks in X509_cmp_time meant that
216
// it did not allow four digit timezone offsets in UTC times.
217
0
int ASN1_TIME_to_time_t(const ASN1_TIME *t, time_t *out_time) {
218
0
  struct tm tm;
219
0
  if (!asn1_time_to_tm(&tm, t, /*allow_timezone_offset=*/0)) {
220
0
    return 0;
221
0
  }
222
0
  return OPENSSL_timegm(&tm, out_time);
223
0
}
224
225
0
int ASN1_TIME_to_posix(const ASN1_TIME *t, int64_t *out_time) {
226
0
  struct tm tm;
227
0
  if (!asn1_time_to_tm(&tm, t, /*allow_timezone_offset=*/0)) {
228
0
    return 0;
229
0
  }
230
0
  return OPENSSL_tm_to_posix(&tm, out_time);
231
0
}
232
233
int bssl::asn1_parse_time(CBS *cbs, ASN1_TIME *out,
234
366k
                          int allow_utc_timezone_offset) {
235
366k
  if (CBS_peek_asn1_tag(cbs, CBS_ASN1_UTCTIME)) {
236
365k
    return asn1_parse_utc_time(cbs, out, /*tag=*/0, allow_utc_timezone_offset);
237
365k
  }
238
373
  return asn1_parse_generalized_time(cbs, out, /*tag=*/0);
239
366k
}
240
241
5.50k
int bssl::asn1_marshal_time(CBB *cbb, const ASN1_TIME *in) {
242
5.50k
  if (in->type != V_ASN1_UTCTIME && in->type != V_ASN1_GENERALIZEDTIME) {
243
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
244
0
    return 0;
245
0
  }
246
5.50k
  return asn1_marshal_octet_string(cbb, in,
247
5.50k
                                   static_cast<CBS_ASN1_TAG>(in->type));
248
5.50k
}