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 static UINT _nx_secure_x509_extract_name_oid_data(const UCHAR *buffer, UINT oid, ULONG length,
27 UINT *bytes_processed,
28 NX_SECURE_X509_DISTINGUISHED_NAME *name);
29
30 /**************************************************************************/
31 /* */
32 /* FUNCTION RELEASE */
33 /* */
34 /* _nx_secure_x509_distinguished_name_parse PORTABLE C */
35 /* 6.1.6 */
36 /* AUTHOR */
37 /* */
38 /* Timothy Stapko, Microsoft Corporation */
39 /* */
40 /* DESCRIPTION */
41 /* */
42 /* This function parses a DER-encoded X.509 Distinguished Name in the */
43 /* supplied buffer, placing the parsed data into a structure that is */
44 /* returned to the caller. */
45 /* */
46 /* INPUT */
47 /* */
48 /* buffer Pointer to data to be parsed */
49 /* length Length of data in buffer */
50 /* bytes_processed Return number of bytes parsed */
51 /* name Return parsed name structure */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* status Completion status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _nx_secure_x509_asn1_tlv_block_parse Parse ASN.1 block */
60 /* _nx_secure_x509_extract_name_oid_data Extract Distinguished Name */
61 /* _nx_secure_x509_oid_parse Parse OID in certificate */
62 /* */
63 /* CALLED BY */
64 /* */
65 /* _nx_secure_x509_crl_issuer_parse Parse issuer in crl */
66 /* _nx_secure_x509_parse_issuer Parse issuer in certificate */
67 /* _nx_secure_x509_parse_subject Parse subject in certificate */
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_distinguished_name_parse(const UCHAR * buffer,UINT length,UINT * bytes_processed,NX_SECURE_X509_DISTINGUISHED_NAME * name)81 UINT _nx_secure_x509_distinguished_name_parse(const UCHAR *buffer, UINT length, UINT *bytes_processed,
82 NX_SECURE_X509_DISTINGUISHED_NAME *name)
83 {
84 USHORT tlv_type;
85 USHORT tlv_type_class;
86 ULONG tlv_length;
87 UINT bytes;
88 const UCHAR *tlv_data;
89 ULONG header_length;
90 UINT status;
91 UINT current_index;
92 const UCHAR *sequence_ptr;
93 ULONG sequence_length;
94 UINT oid;
95
96 /* Parse distinguished name information including Common Name, country/region, organization, etc... */
97 /*
98 * Sequence (16): Name information - parsed above this point in hierarchy
99 * Set (17): Information (multiple sets here - one per OID)
100 * Sequence (16): Information (multiple sequences here?)
101 * ASN.1 OID (6) (e.g. Country/Region)
102 * ASN.1 String (UTF8-12, Printable-19) (e.g. "US")
103 * ...
104 */
105
106 current_index = 0;
107 *bytes_processed = 0;
108
109 /* Continue parsing ASN.1 sets until we reach the end of the surrounding sequence, determined
110 * by the "length" parameter. */
111 while (length > 0)
112 {
113 /* First, parse the set. */
114 status = _nx_secure_x509_asn1_tlv_block_parse(&buffer[current_index], (ULONG *)&length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
115
116 /* Make sure we parsed the block alright. */
117 if (status != 0)
118 {
119 return(status);
120 }
121
122 if (tlv_type != NX_SECURE_ASN_TAG_SET || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
123 {
124 return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
125 }
126
127 /* Advance the top-level buffer pointer to the next SET entry. */
128 current_index += (header_length + tlv_length);
129 *bytes_processed += (header_length + tlv_length);
130 sequence_length = tlv_length;
131
132 /* Next, parse the sequence. */
133 status = _nx_secure_x509_asn1_tlv_block_parse(tlv_data, &sequence_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
134
135 /* Make sure we parsed the block alright. */
136 if (status != 0)
137 {
138 return(status);
139 }
140
141 if (tlv_type != NX_SECURE_ASN_TAG_SEQUENCE || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
142 {
143 return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
144 }
145
146 /* Save the sequence for the calls below. */
147 sequence_ptr = tlv_data;
148 sequence_length = tlv_length;
149
150 /* Now we have the actual data. First extract the OID.*/
151 status = _nx_secure_x509_asn1_tlv_block_parse(sequence_ptr, &sequence_length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
152
153 /* Make sure we parsed the block alright. */
154 if (status != 0)
155 {
156 return(status);
157 }
158
159 if (tlv_type != NX_SECURE_ASN_TAG_OID || tlv_type_class != NX_SECURE_ASN_TAG_CLASS_UNIVERSAL)
160 {
161 return(NX_SECURE_X509_UNEXPECTED_ASN1_TAG);
162 }
163
164 /* The OID is in the data we extracted. */
165 _nx_secure_x509_oid_parse(tlv_data, tlv_length, &oid);
166
167 /* Extract the data in the block following the OID. Use the remaining length of the sequence
168 * for the length of the data going in. */
169 status = _nx_secure_x509_extract_name_oid_data(&tlv_data[tlv_length], oid, sequence_length, &bytes, name);
170
171 if (status != 0)
172 {
173 return(status);
174 }
175 }
176
177 /* All done parsing id info! */
178 return(NX_SECURE_X509_SUCCESS);
179 }
180
181
182 /* This function extracts name identification data from the certificate distinguished name fields (subject and issuer)
183 by matching OIDs and populating the correct fields in the distinguished name structure. */
184 /**************************************************************************/
185 /* */
186 /* FUNCTION RELEASE */
187 /* */
188 /* _nx_secure_x509_extract_name_oid_data PORTABLE C */
189 /* 6.1 */
190 /* AUTHOR */
191 /* */
192 /* Timothy Stapko, Microsoft Corporation */
193 /* */
194 /* DESCRIPTION */
195 /* */
196 /* This function parses a DER-encoded X.509 Distinguished Name in the */
197 /* supplied buffer, placing the parsed data into a structure that is */
198 /* returned to the caller. */
199 /* */
200 /* INPUT */
201 /* */
202 /* buffer Pointer to data to be parsed */
203 /* oid OID data to be parsed */
204 /* length Length of data in buffer */
205 /* bytes_processed Return number of bytes parsed */
206 /* name Return parsed name structure */
207 /* */
208 /* OUTPUT */
209 /* */
210 /* status Completion status */
211 /* */
212 /* CALLS */
213 /* */
214 /* _nx_secure_x509_asn1_tlv_block_parse Parse ASN.1 block */
215 /* */
216 /* CALLED BY */
217 /* */
218 /* _nx_secure_x509_distinguished_name_parse */
219 /* Parse Distinguished Name */
220 /* */
221 /* RELEASE HISTORY */
222 /* */
223 /* DATE NAME DESCRIPTION */
224 /* */
225 /* 05-19-2020 Timothy Stapko Initial Version 6.0 */
226 /* 09-30-2020 Timothy Stapko Modified comment(s), */
227 /* resulting in version 6.1 */
228 /* */
229 /**************************************************************************/
_nx_secure_x509_extract_name_oid_data(const UCHAR * buffer,UINT oid,ULONG length,UINT * bytes_processed,NX_SECURE_X509_DISTINGUISHED_NAME * name)230 static UINT _nx_secure_x509_extract_name_oid_data(const UCHAR *buffer, UINT oid, ULONG length,
231 UINT *bytes_processed,
232 NX_SECURE_X509_DISTINGUISHED_NAME *name)
233 {
234 USHORT tlv_type;
235 USHORT tlv_type_class;
236 ULONG tlv_length;
237 const UCHAR *tlv_data;
238 ULONG header_length;
239 UINT status;
240
241 status = _nx_secure_x509_asn1_tlv_block_parse(buffer, &length, &tlv_type, &tlv_type_class, &tlv_length, &tlv_data, &header_length);
242 if (status != 0)
243 {
244 return(status);
245 }
246
247 *bytes_processed = (header_length + tlv_length);
248
249 switch (oid)
250 {
251 case NX_SECURE_TLS_X509_TYPE_COMMON_NAME:
252 name -> nx_secure_x509_common_name = tlv_data;
253 name -> nx_secure_x509_common_name_length = (USHORT)tlv_length;
254 break;
255 case NX_SECURE_TLS_X509_TYPE_COUNTRY:
256 name -> nx_secure_x509_country = tlv_data;
257 name -> nx_secure_x509_country_length = (USHORT)tlv_length;
258 break;
259 case NX_SECURE_TLS_X509_TYPE_STATE:
260 name -> nx_secure_x509_state = tlv_data;
261 name -> nx_secure_x509_state_length = (USHORT)tlv_length;
262 break;
263 case NX_SECURE_TLS_X509_TYPE_ORGANIZATION:
264 name -> nx_secure_x509_organization = tlv_data;
265 name -> nx_secure_x509_organization_length = (USHORT)tlv_length;
266 break;
267 case NX_SECURE_TLS_X509_TYPE_ORG_UNIT:
268 name -> nx_secure_x509_org_unit = tlv_data;
269 name -> nx_secure_x509_org_unit_length = (USHORT)tlv_length;
270 break;
271 #ifdef NX_SECURE_X509_USE_EXTENDED_DISTINGUISHED_NAMES
272 /* When using extended distinguished names, parse them here. */
273 case NX_SECURE_TLS_X509_TYPE_LOCALITY:
274 name -> nx_secure_x509_locality = tlv_data;
275 name -> nx_secure_x509_locality_length = (USHORT)tlv_length;
276 break;
277 #endif
278 default:
279 /* In the case of unrecognized OIDs, just return success. There are numerous
280 extensions and their presence is not a problem if we don't recognize them. */
281 break;
282 }
283
284 /* We have successfully extracted our data based on the OID. */
285 return(NX_SECURE_X509_SUCCESS);
286 }
287
288