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, &not_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, &not_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