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
28 #ifndef NX_SECURE_X509_DISABLE_CRL
29 static UINT _nx_secure_x509_crl_parse_entry(const UCHAR *buffer, ULONG length, UINT *bytes_processed,
30 const UCHAR **serial_number, UINT *serial_number_length);
31 #endif /* NX_SECURE_X509_DISABLE_CRL */
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _nx_secure_x509_crl_revocation_check PORTABLE C */
38 /* 6.1.12 */
39 /* AUTHOR */
40 /* */
41 /* Timothy Stapko, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function takes a DER-encoded Certificate Revocation List and */
46 /* searches for a specific certificate in that list. The issuer of the */
47 /* CRL is validated against a supplied certificate store, the */
48 /* CRL issuer is validated to be the same as the one for the */
49 /* certificate being checked, and the serial number of the certificate */
50 /* in question is used to search the revoked certificates list. */
51 /* If the issuers match, the signature checks out, and the certificate */
52 /* is not present in the list, the call is successful. All other cases */
53 /* cause an error to be returned. */
54 /* */
55 /* INPUT */
56 /* */
57 /* crl_data Pointer to DER-encoded CRL */
58 /* crl_length Length of CRL data in buffer */
59 /* store Certificate store to be used */
60 /* certificate The certificate being checked */
61 /* */
62 /* OUTPUT */
63 /* */
64 /* status Completion status */
65 /* */
66 /* CALLS */
67 /* */
68 /* _nx_secure_x509_certificate_chain_verify */
69 /* Verify cert against stores */
70 /* _nx_secure_x509_certificate_revocation_list_parse */
71 /* Parse revocation list */
72 /* _nx_secure_x509_crl_parse_entry Parse an entry in crl */
73 /* _nx_secure_x509_crl_verify Verify revocation list */
74 /* _nx_secure_x509_distinguished_name_compare */
75 /* Compare distinguished name */
76 /* _nx_secure_x509_store_certificate_find */
77 /* Find a cert in a store */
78 /* */
79 /* CALLED BY */
80 /* */
81 /* Application Code */
82 /* */
83 /* RELEASE HISTORY */
84 /* */
85 /* DATE NAME DESCRIPTION */
86 /* */
87 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
88 /* 09-30-2020 Timothy Stapko Modified comment(s), */
89 /* resulting in version 6.1 */
90 /* 04-02-2021 Timothy Stapko Modified comment(s), */
91 /* removed dependency on TLS, */
92 /* resulting in version 6.1.6 */
93 /* 08-02-2021 Timothy Stapko Modified comment(s), */
94 /* fixed compiler warnings, */
95 /* resulting in version 6.1.8 */
96 /* 04-25-2022 Yuxin Zhou Modified comment(s), */
97 /* modified to improve code */
98 /* coverage result, */
99 /* resulting in version 6.1.11 */
100 /* 07-29-2022 Yuxin Zhou Modified comment(s), and */
101 /* checked expiration for all */
102 /* the certs in the chain, */
103 /* resulting in version 6.1.12 */
104 /* */
105 /**************************************************************************/
_nx_secure_x509_crl_revocation_check(const UCHAR * crl_data,UINT crl_length,NX_SECURE_X509_CERTIFICATE_STORE * store,NX_SECURE_X509_CERT * certificate)106 UINT _nx_secure_x509_crl_revocation_check(const UCHAR *crl_data, UINT crl_length,
107 NX_SECURE_X509_CERTIFICATE_STORE *store,
108 NX_SECURE_X509_CERT *certificate)
109 {
110 #ifndef NX_SECURE_X509_DISABLE_CRL
111 NX_SECURE_X509_CRL crl;
112 UINT status;
113 UINT crl_bytes;
114 INT compare_value;
115 UINT bytes_processed;
116 UINT length;
117 const UCHAR *current_buffer;
118 NX_SECURE_X509_CERT *issuer_certificate;
119 UINT issuer_location;
120 const UCHAR *serial_number = NX_NULL;
121 UINT serial_number_length;
122
123 NX_SECURE_MEMSET(&crl, 0, sizeof(NX_SECURE_X509_CRL));
124
125 /* First, parse the CRL. */
126 status = _nx_secure_x509_certificate_revocation_list_parse(crl_data, crl_length, &crl_bytes, &crl);
127
128 if (status != NX_SECURE_X509_SUCCESS)
129 {
130 return(status);
131 }
132
133 /* Check that the issuers match according to process in RFC 5280. */
134 compare_value = _nx_secure_x509_distinguished_name_compare(&crl.nx_secure_x509_crl_issuer, &certificate -> nx_secure_x509_issuer, NX_SECURE_X509_NAME_ALL_FIELDS);
135
136 if (compare_value)
137 {
138 /* The issuers did not match, return error. */
139 return(NX_SECURE_X509_CRL_ISSUER_MISMATCH);
140 }
141
142 /* Now, check the signature of the CRL. */
143
144 /* First, get the issuer certificate. If we have a valid store and CRL, the issuer should be available. */
145 status = _nx_secure_x509_store_certificate_find(store, &crl.nx_secure_x509_crl_issuer, 0, &issuer_certificate, &issuer_location);
146
147 if (status != NX_SECURE_X509_SUCCESS)
148 {
149 return(status);
150 }
151
152 /* Now, check that the issuer is valid. */
153 status = _nx_secure_x509_certificate_chain_verify(store, issuer_certificate, 0);
154
155 if (status != NX_SECURE_X509_SUCCESS)
156 {
157 return(status);
158 }
159
160 /* Now, verify that the CRL itself is OK. */
161 status = _nx_secure_x509_crl_verify(certificate, &crl, store, issuer_certificate);
162
163 if (status != NX_SECURE_X509_SUCCESS)
164 {
165 return(status);
166 }
167
168 /* If we get here, then the CRL is OK and is issued by the same CA as the certificate in question.
169 Finally, we parse the revocation list and see if the serial number of the certificate being
170 validated is in the CRL revocation list. */
171
172 /* Parse and search the revokedCertificates list that we obtained when we parsed the CRL.
173 * revokedCertificates SEQUENCE OF SEQUENCE
174 * {
175 * userCertificate CertificateSerialNumber,
176 * revocationDate Time,
177 * crlEntryExtensions Extensions OPTIONAL
178 * -- if present, version MUST be v2
179 * } OPTIONAL,
180 */
181 current_buffer = crl.nx_secure_x509_crl_revoked_certs;
182 length = crl.nx_secure_x509_crl_revoked_certs_length;
183 /* Loop through all the entries in the sequence. */
184 while (length > 0)
185 {
186 /* Parse an entry in the revokedCertificates list and get back the serial number. */
187 status = _nx_secure_x509_crl_parse_entry(current_buffer, length, &bytes_processed, &serial_number, &serial_number_length);
188
189 if (status != NX_SECURE_X509_SUCCESS)
190 {
191 return(status);
192 }
193
194 /* Make sure we don't run past the end of the sequence if one of the entries was too big. */
195 NX_ASSERT(bytes_processed <= length);
196
197 /* Compare the serial number we got from the list (if it exists) to the one in our certificate. */
198 compare_value = NX_SECURE_MEMCMP(serial_number, certificate -> nx_secure_x509_serial_number,
199 certificate -> nx_secure_x509_serial_number_length);
200
201 /* See if we have a match. */
202 if (compare_value == 0)
203 {
204 /* This certificate has been revoked! */
205 return(NX_SECURE_X509_CRL_CERTIFICATE_REVOKED);
206 }
207
208 /* No match, go to the next one. */
209 length -= bytes_processed;
210 current_buffer = current_buffer + bytes_processed;
211 }
212
213 /* If we get here, the CRL was good and the certificate has not been revoked. */
214 return(NX_SECURE_X509_SUCCESS);
215 #else /* NX_SECURE_X509_DISABLE_CRL */
216 NX_CRYPTO_PARAMETER_NOT_USED(crl_data);
217 NX_CRYPTO_PARAMETER_NOT_USED(crl_length);
218 NX_CRYPTO_PARAMETER_NOT_USED(store);
219 NX_CRYPTO_PARAMETER_NOT_USED(certificate);
220 #ifdef NX_CRYPTO_STANDALONE_ENABLE
221 return(NX_CRYPTO_FORMAT_NOT_SUPPORTED);
222 #else
223 return(NX_NOT_SUPPORTED);
224 #endif /* NX_CRYPTO_STANDALONE_ENABLE */
225 #endif /* NX_SECURE_X509_DISABLE_CRL */
226 }
227
228 #ifndef NX_SECURE_X509_DISABLE_CRL
229 /* Helper function to parse entries in the revokedCertificates list. */
230 /**************************************************************************/
231 /* */
232 /* FUNCTION RELEASE */
233 /* */
234 /* _nx_secure_x509_crl_parse_entry PORTABLE C */
235 /* 6.1 */
236 /* AUTHOR */
237 /* */
238 /* Timothy Stapko, Microsoft Corporation */
239 /* */
240 /* DESCRIPTION */
241 /* */
242 /* This function parses an entry in the revokedCertificates list */
243 /* embedded within an X.509 CRL. Each entry contains the serial number */
244 /* of a revoked certificate. This serial number is returned so the */
245 /* caller can compare it to the serial number of the certificate being */
246 /* checked for revocation. */
247 /* */
248 /* INPUT */
249 /* */
250 /* buffer Pointer into CRL data */
251 /* length Length of CRL data */
252 /* bytes_processed Return bytes parsed */
253 /* serial_number Return cert serial number */
254 /* serial_number_length Return serial number length */
255 /* */
256 /* OUTPUT */
257 /* */
258 /* status Completion status */
259 /* */
260 /* CALLS */
261 /* */
262 /* _nx_secure_x509_asn1_tlv_block_parse Parse ASN.1 block */
263 /* */
264 /* CALLED BY */
265 /* */
266 /* _nx_secure_x509_crl_revocation_check Check revocation in crl */
267 /* */
268 /* RELEASE HISTORY */
269 /* */
270 /* DATE NAME DESCRIPTION */
271 /* */
272 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
273 /* 09-30-2020 Timothy Stapko Modified comment(s), */
274 /* resulting in version 6.1 */
275 /* */
276 /**************************************************************************/
_nx_secure_x509_crl_parse_entry(const UCHAR * buffer,ULONG length,UINT * bytes_processed,const UCHAR ** serial_number,UINT * serial_number_length)277 static UINT _nx_secure_x509_crl_parse_entry(const UCHAR *buffer, ULONG length, UINT *bytes_processed,
278 const UCHAR **serial_number, UINT *serial_number_length)
279 {
280 USHORT tlv_type;
281 USHORT tlv_type_class;
282 ULONG tlv_length;
283 const UCHAR *tlv_data;
284 const UCHAR *current_buffer;
285 ULONG header_length;
286 UINT status;
287
288
289 /* Each entry in the list is a sequence containing the serial number, revocation date, and extensions. */
290 *bytes_processed = 0;
291 current_buffer = buffer;
292
293 /* First, parse the sequence. */
294 status = _nx_secure_x509_asn1_tlv_block_parse(current_buffer, &length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
295
296 /* Make sure we parsed the block alright. */
297 if (status != 0)
298 {
299 return(status);
300 }
301
302 if (tlv_type != NX_SECURE_ASN_TAG_SEQUENCE || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
303 {
304 return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
305 }
306
307 *bytes_processed += header_length + tlv_length;
308 current_buffer = current_buffer + header_length;
309 length = tlv_length;
310
311 /* Next, parse the serial number. */
312 status = _nx_secure_x509_asn1_tlv_block_parse(tlv_data, &length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
313
314 /* Make sure we parsed the block alright. */
315 if (status != 0)
316 {
317 return(status);
318 }
319
320 /* The serial number is an ASN.1 Integer. */
321 if (tlv_type != NX_SECURE_ASN_TAG_INTEGER || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
322 {
323 return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
324 }
325
326 /* Save off the serial number in case someone wants it. */
327 *serial_number = tlv_data;
328 *serial_number_length = (USHORT)tlv_length;
329
330 /* Don't adjust bytes processed since we handled that above. Just adjust buffer. */
331 current_buffer = current_buffer + (tlv_length + header_length);
332
333 /* Parse the "revocationDate" field. */
334 status = _nx_secure_x509_asn1_tlv_block_parse(current_buffer, &length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
335
336 /* Make sure we parsed the block alright. */
337 if (status != 0)
338 {
339 return(status);
340 }
341
342 if ((tlv_type != NX_SECURE_ASN_TAG_UTC_TIME && tlv_type != NX_SECURE_ASN_TAG_GENERALIZED_TIME) || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
343 {
344 return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
345 }
346
347 /* Extensions are optional. Add here when implemented. */
348
349 return(NX_SECURE_X509_SUCCESS);
350 }
351 #endif /* NX_SECURE_X509_DISABLE_CRL */
352