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
27 /**************************************************************************/
28 /* */
29 /* FUNCTION RELEASE */
30 /* */
31 /* _nx_secure_x509_extension_find PORTABLE C */
32 /* 6.1.6 */
33 /* AUTHOR */
34 /* */
35 /* Timothy Stapko, Microsoft Corporation */
36 /* */
37 /* DESCRIPTION */
38 /* */
39 /* This function parses through the list of extensions in an X.509 */
40 /* certificate looking for a specific extension. The data in the */
41 /* extension is returned in a structure for use in specific extension */
42 /* handling functions. */
43 /* */
44 /* INPUT */
45 /* */
46 /* certificate Certificate to search */
47 /* extension Return instruction data */
48 /* extension_id Extension to search for */
49 /* */
50 /* OUTPUT */
51 /* */
52 /* status Completion status */
53 /* */
54 /* CALLS */
55 /* */
56 /* _nx_secure_x509_asn1_tlv_block_parse Parse ASN.1 block */
57 /* _nx_secure_x509_oid_parse Parse OID in certificate */
58 /* */
59 /* CALLED BY */
60 /* */
61 /* Application Code */
62 /* _nx_secure_x509_common_name_dns_check Check Common Name by DNS */
63 /* _nx_secure_x509_extended_key_usage_extension_parse */
64 /* Parse Extended KeyUsage */
65 /* extension */
66 /* _nx_secure_x509_key_usage_extension_parse */
67 /* Parse KeyUsage extension */
68 /* */
69 /* RELEASE HISTORY */
70 /* */
71 /* DATE NAME DESCRIPTION */
72 /* */
73 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
74 /* 09-30-2020 Timothy Stapko Modified comment(s), */
75 /* resulting in version 6.1 */
76 /* 04-02-2021 Timothy Stapko Modified comment(s), */
77 /* removed dependency on TLS, */
78 /* resulting in version 6.1.6 */
79 /* */
80 /**************************************************************************/
_nx_secure_x509_extension_find(NX_SECURE_X509_CERT * certificate,NX_SECURE_X509_EXTENSION * extension,USHORT extension_id)81 UINT _nx_secure_x509_extension_find(NX_SECURE_X509_CERT *certificate,
82 NX_SECURE_X509_EXTENSION *extension, USHORT extension_id)
83 {
84 USHORT tlv_type;
85 USHORT tlv_type_class;
86 ULONG tlv_length;
87 ULONG extensions_sequence_length;
88 ULONG seq_length;
89 UINT extension_oid = 0;
90 USHORT critical_flag;
91 const UCHAR *tlv_data;
92 const UCHAR *current_buffer;
93 ULONG header_length;
94 UINT status;
95 UINT found_extension = NX_CRYPTO_FALSE;
96
97 /* Now, parse the extensions. */
98 /* Extension ASN.1 format:
99 * ASN.1 Sequence: Extension (single)
100 * {
101 * OID: Extension identifier
102 * BOOLEAN: Critical (default=False)
103 * OCTET STRING: DER-encoded specific extension data
104 * }
105 */
106
107 /* The length of our extensions is the length of the sequence. */
108 current_buffer = certificate -> nx_secure_x509_extensions_data;
109 extensions_sequence_length = certificate -> nx_secure_x509_extensions_data_length;
110
111 /* Keep looping until we run out of data to parse. */
112 while (extensions_sequence_length > 0)
113 {
114 /* Next, parse the single extension sequence. */
115 status = _nx_secure_x509_asn1_tlv_block_parse(current_buffer, &extensions_sequence_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
116
117 /* Make sure we parsed the block alright. */
118 if (status != 0)
119 {
120 return(status);
121 }
122
123 /* If the next item up is not a sequence, then it isn't an extensions block. */
124 if (!(tlv_type_class == NX_SECURE_ASN_TAG_CLASS_UNIVERSAL && tlv_type == NX_SECURE_ASN_TAG_SEQUENCE))
125 {
126 /* The extensions sequence isn't empty and we should be seeing another extension sequence
127 but we got something else so something is amiss. */
128 return(NX_SECURE_X509_INVALID_EXTENSION_SEQUENCE);
129 }
130
131 /* Advance our buffer to the next item. We are now in the sequence so advance buffer after this
132 instead of pointing to tlv_data. */
133 current_buffer += header_length + tlv_length;
134 seq_length = tlv_length;
135
136 /* Parse the OID. */
137 status = _nx_secure_x509_asn1_tlv_block_parse(tlv_data, &seq_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
138 if (status != 0)
139 {
140 return status;
141 }
142
143 /* Now check out what we got. Should be an OID... */
144 if (tlv_type == NX_SECURE_ASN_TAG_OID)
145 {
146 /* The OID is in the data we extracted. */
147 _nx_secure_x509_oid_parse(tlv_data, tlv_length, &extension_oid);
148 }
149 else
150 {
151 /* Invalid extension, end parsing. */
152 return(NX_SECURE_X509_INVALID_EXTENSION_SEQUENCE);
153 }
154
155 /* See if we found a match. */
156 if (extension_oid == extension_id)
157 {
158 /* Advance the working pointer to the data immediately following the OID. */
159 current_buffer = tlv_data + tlv_length;
160
161 /* We found a matching extension, break out and populate the return structure. */
162 found_extension = NX_CRYPTO_TRUE;
163 break;
164 }
165 } /* End while loop. */
166
167 /* Did we find the extension we were looking for? */
168 if (found_extension != NX_CRYPTO_TRUE)
169 {
170 return(NX_SECURE_X509_EXTENSION_NOT_FOUND);
171 }
172
173 /* If we get here, we found a matching extension. */
174 extension -> nx_secure_x509_extension_id = (USHORT)extension_oid;
175
176 /* Parse the Critical boolean. */
177 status = _nx_secure_x509_asn1_tlv_block_parse(current_buffer, &seq_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
178 if (status != 0)
179 {
180 return status;
181 }
182
183 /* Now check out what we got. Possibly a boolean for the critical flag, but
184 if it isn't there, then it's default is FALSE. */
185 critical_flag = NX_CRYPTO_FALSE;
186 if (tlv_type == NX_SECURE_ASN_TAG_BOOLEAN)
187 {
188 /* The boolean is in the data we extracted. Convert ASN.1 boolean TRUE (all bits set) to integer 1. */
189 critical_flag = tlv_data[0] != 0;
190
191 /* Advance our buffer to the next item. */
192 current_buffer += header_length + tlv_length;
193
194 /* Parse the octet string containing the DER-encoded extension data. */
195 status = _nx_secure_x509_asn1_tlv_block_parse(current_buffer, &seq_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
196 if (status != 0)
197 {
198 return status;
199 }
200 }
201 extension -> nx_secure_x509_extension_critical = critical_flag;
202
203 /* Now check out what we got. Should be an octet string... */
204 if (tlv_type != NX_SECURE_ASN_TAG_OCTET_STRING)
205 {
206 /* Invalid extension, end parsing. */
207 return(NX_SECURE_X509_INVALID_EXTENSION_SEQUENCE);
208 }
209
210 /* Save off a pointer to the extension data (format determined by actual extension type). */
211 extension -> nx_secure_x509_extension_data = tlv_data;
212 extension -> nx_secure_x509_extension_data_length = tlv_length;
213
214 #if 0
215 /* Dispatch based on OID to individual parsing routines. */
216 switch (extension_oid)
217 {
218 case NX_SECURE_TLS_X509_TYPE_AUTHORITY_KEY_ID:
219 /*printf("Got authority key ID extension.\n");*/
220 break;
221 case NX_SECURE_TLS_X509_TYPE_SUBJECT_KEY_ID:
222 /*printf("Got subject key ID extension.\n");*/
223 break;
224 case NX_SECURE_TLS_X509_TYPE_BASIC_CONSTRAINTS:
225 /*printf("Got basic constraints extension. \n");*/
226 break;
227 case NX_SECURE_TLS_X509_TYPE_NETSCAPE_COMMENT:
228 /*printf("Got Netscape comment extension. \n");*/
229 break;
230 case NX_SECURE_TLS_X509_TYPE_UNKNOWN:
231 /* Unknown extension, ignore. */
232 /*printf("Unknown extension OID\n");*/
233 break;
234 default:
235 /* Unsupported extension, ignore. */
236 /*printf("Unsupported extension OID: %d\n", extension_oid);*/
237 break;
238 }
239 #endif
240
241
242 return(NX_SECURE_X509_SUCCESS);
243 }
244
245