1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Secure Component */
16 /** */
17 /** X.509 Digital Certificates */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SECURE_SOURCE_CODE
23
24 #include "nx_secure_x509.h"
25
26 /* Local helper function. */
27 static UINT _nx_secure_x509_asn1_time_to_unix_convert(const UCHAR *asn1_time, USHORT asn1_length,
28 USHORT format, ULONG *unix_time);
29
30 /**************************************************************************/
31 /* */
32 /* FUNCTION RELEASE */
33 /* */
34 /* _nx_secure_x509_expiration_check PORTABLE C */
35 /* 6.1.6 */
36 /* AUTHOR */
37 /* */
38 /* Timothy Stapko, Microsoft Corporation */
39 /* */
40 /* DESCRIPTION */
41 /* */
42 /* This function checks a certificate's validity period against the */
43 /* current time, which is a 32-bit UNIX-epoch format value of GMT. */
44 /* */
45 /* INPUT */
46 /* */
47 /* certificate Pointer to certificate */
48 /* current_time Current GMT value */
49 /* */
50 /* OUTPUT */
51 /* */
52 /* status Validity of certificate */
53 /* */
54 /* CALLS */
55 /* */
56 /* _nx_secure_x509_asn1_time_to_unix_convert */
57 /* Convert ASN.1 time to UNIX */
58 /* */
59 /* CALLED BY */
60 /* */
61 /* _nx_secure_tls_remote_certificate_verify */
62 /* Verify the server certificate */
63 /* */
64 /* RELEASE HISTORY */
65 /* */
66 /* DATE NAME DESCRIPTION */
67 /* */
68 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
69 /* 09-30-2020 Timothy Stapko Modified comment(s), */
70 /* resulting in version 6.1 */
71 /* 04-02-2021 Timothy Stapko Modified comment(s), */
72 /* removed dependency on TLS, */
73 /* resulting in version 6.1.6 */
74 /* */
75 /**************************************************************************/
_nx_secure_x509_expiration_check(NX_SECURE_X509_CERT * certificate,ULONG current_time)76 UINT _nx_secure_x509_expiration_check(NX_SECURE_X509_CERT *certificate, ULONG current_time)
77 {
78 ULONG not_before;
79 ULONG not_after;
80 UINT status;
81
82 /* First, convert the X.509 ASN.1 time format into 32-bit UINX-epoch format of the "not before" field. */
83 status = _nx_secure_x509_asn1_time_to_unix_convert(certificate -> nx_secure_x509_not_before, certificate -> nx_secure_x509_not_before_length,
84 certificate -> nx_secure_x509_validity_format, ¬_before);
85 if (status != NX_SECURE_X509_SUCCESS)
86 {
87 return(status);
88 }
89
90 /* Convert the "not after" time field. */
91 status = _nx_secure_x509_asn1_time_to_unix_convert(certificate -> nx_secure_x509_not_after, certificate -> nx_secure_x509_not_after_length,
92 certificate -> nx_secure_x509_validity_format, ¬_after);
93 if (status != NX_SECURE_X509_SUCCESS)
94 {
95 return(status);
96 }
97
98 /* Check if certificate is expired. */
99 if (current_time > not_after)
100 {
101 /* Certificate is expired. */
102 return(NX_SECURE_X509_CERTIFICATE_EXPIRED);
103 }
104
105 /* Check if certificate is not yet valid. */
106 if (current_time < not_before)
107 {
108 /* Certificate is not valid yet. */
109 return(NX_SECURE_X509_CERTIFICATE_NOT_YET_VALID);
110 }
111
112 return(NX_SECURE_X509_SUCCESS);
113 }
114
115
116
117 /* Helper function to convert the ASN.1 time formats into UNIX epoch time for comparison. */
118
119 #define date_2_chars_to_int(buffer, index) (ULONG)(((buffer[index] - '0') * 10) + (buffer[index + 1] - '0'))
120
121 /* Array indexed on month - 1 gives the total number of days in all previous months (through last day of previous
122 month). Leap years are handled in the logic below and are not reflected in this array. */
123 /* J F M A M J J A S O N D */
124 static const UINT days_before_month[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
125
126 /**************************************************************************/
127 /* */
128 /* FUNCTION RELEASE */
129 /* */
130 /* _nx_secure_x509_asn1_time_to_unix_convert PORTABLE C */
131 /* 6.1.11 */
132 /* AUTHOR */
133 /* */
134 /* Timothy Stapko, Microsoft Corporation */
135 /* */
136 /* DESCRIPTION */
137 /* */
138 /* This function converts ASN.1 time to 32-bit UNIX-epoch format value */
139 /* of GMT. */
140 /* */
141 /* INPUT */
142 /* */
143 /* asn1_time String of ASN.1 time */
144 /* asn1_length Length of ASN.1 time string */
145 /* format Format of UNIX time */
146 /* unix_time UNIX time value for output */
147 /* */
148 /* OUTPUT */
149 /* */
150 /* status Completion status */
151 /* */
152 /* CALLS */
153 /* */
154 /* None */
155 /* */
156 /* CALLED BY */
157 /* */
158 /* _nx_secure_x509_expiration_check Verify expiration of cert */
159 /* */
160 /* RELEASE HISTORY */
161 /* */
162 /* DATE NAME DESCRIPTION */
163 /* */
164 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
165 /* 09-30-2020 Timothy Stapko Modified comment(s), */
166 /* resulting in version 6.1 */
167 /* 04-25-2022 Yuxin Zhou Modified comment(s), and */
168 /* changed LONG to ULONG to */
169 /* extend the time range, */
170 /* removed unused code, */
171 /* resulting in version 6.1.11 */
172 /* */
173 /**************************************************************************/
_nx_secure_x509_asn1_time_to_unix_convert(const UCHAR * asn1_time,USHORT asn1_length,USHORT format,ULONG * unix_time)174 static UINT _nx_secure_x509_asn1_time_to_unix_convert(const UCHAR *asn1_time, USHORT asn1_length,
175 USHORT format, ULONG *unix_time)
176 {
177 ULONG year, month, day, hour, minute, second;
178 UINT index;
179
180 NX_CRYPTO_PARAMETER_NOT_USED(asn1_length);
181 index = 0;
182
183 /* See what format we are using. */
184 if (format == NX_SECURE_ASN_TAG_UTC_TIME)
185 {
186 /* UTCTime is either "YYMMDDhhmm[ss]Z" or "YYMMDDhhmm[ss](+|-)hhmm" */
187 year = date_2_chars_to_int(asn1_time, 0);
188 month = date_2_chars_to_int(asn1_time, 2);
189 day = date_2_chars_to_int(asn1_time, 4) - 1; /* For calculations, day is 0-based. */
190 hour = date_2_chars_to_int(asn1_time, 6);
191 minute = date_2_chars_to_int(asn1_time, 8);
192 second = 0;
193
194 /* Check the next field, can be 'Z' for Zulu time (GMT) or [+/-] for local time offset. */
195 index = 10;
196
197 /* Check for optional seconds. */
198 if (asn1_time[index] != 'Z' && asn1_time[index] != '+' && asn1_time[index] != '-')
199 {
200 second = date_2_chars_to_int(asn1_time, index);
201 index += 2;
202 }
203
204 /* Check for GMT time or local time offset. */
205 if (asn1_time[index] != 'Z')
206 {
207 /* Check for optional local time offset. NOTE: The additions and subtractions here may
208 * result in values > 24 or < 0 but that is OK for the calculations. */
209 if (asn1_time[index] == '+')
210 {
211 index++; /* Skip the '+' */
212 hour -= date_2_chars_to_int(asn1_time, index);
213 index += 2;
214 minute -= date_2_chars_to_int(asn1_time, index);
215 }
216 else if (asn1_time[index] == '-')
217 {
218 index++; /* Skip the '-' */
219 hour += date_2_chars_to_int(asn1_time, index);
220 index += 2;
221 minute += date_2_chars_to_int(asn1_time, index);
222 }
223 else
224 {
225 /* Not a correct UTC time! */
226 return(NX_SECURE_X509_INVALID_DATE_FORMAT);
227 }
228 }
229
230 /* printf("year: %lu, month: %lu, day: %lu, hour: %lu, minute: %lu, second: %lu\n", year, month, day, hour, minute, second);*/
231
232 /* Now we have our time in integers, calculate leap years. We aren't concerned with years outside the UNIX
233 time range of 1970-2038 so we can assume every 4 years starting with 1972 is a leap year (years divisible
234 by 100 are NOT leap years unless also divisible by 400, which the year 2000 is). Using integer division gives
235 us the floor of the number of 4 year periods, so add 1. */
236 if (year >= 70)
237 {
238 /* Year is before 2000. Subtract 72 to get duration from first leap year in epoch. */
239 year -= 70;
240 day += ((year + 2) / 4);
241 }
242 else
243 {
244 /* Year is 2000 or greater. Add 28 (2000-1972) to get duration from first leap year in epoch. */
245 year += 30;
246 day += ((year - 2) / 4) + 1;
247 }
248
249 /* If it is leap year and month is before March, subtract 1 day. */
250 if (((year + 2) % 4 == 0) && (month < 3))
251 {
252 day -= 1;
253 }
254
255 /* Finally, calculate the number of seconds from the extracted values. */
256 day += year * 365;
257 day += days_before_month[month - 1];
258 hour += day * 24;
259 minute += hour * 60;
260 second += minute * 60;
261
262 /* Finally, return the converted time. */
263 *unix_time = second;
264 }
265 else if (format == NX_SECURE_ASN_TAG_GENERALIZED_TIME)
266 {
267 /* Generalized time formats:
268 Local time only. ``YYYYMMDDHH[MM[SS[.fff]]]'', where the optional fff is three decimal places (fractions of seconds).
269 Universal time (UTC time) only. ``YYYYMMDDHH[MM[SS[.fff]]]Z''. MM, SS, .fff are optional.
270 Difference between local and UTC times. ``YYYYMMDDHH[MM[SS[.fff]]]+-HHMM''. +/-HHMM is local time offset. */
271 /* TODO: Implement conversion to 32-bit UNIX time. */
272 return(NX_SECURE_X509_INVALID_DATE_FORMAT);
273 }
274 else
275 {
276 return(NX_SECURE_X509_INVALID_DATE_FORMAT);
277 }
278
279 return(NX_SECURE_X509_SUCCESS);
280 }
281
282