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