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