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