1 | /* |
2 | * X.509 base functions for creating certificates / CSRs |
3 | * |
4 | * Copyright The Mbed TLS Contributors |
5 | * SPDX-License-Identifier: Apache-2.0 |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
8 | * not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
15 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
18 | */ |
19 | |
20 | #include "common.h" |
21 | |
22 | #if defined(MBEDTLS_X509_CREATE_C) |
23 | |
24 | #include "mbedtls/x509.h" |
25 | #include "mbedtls/asn1write.h" |
26 | #include "mbedtls/error.h" |
27 | #include "mbedtls/oid.h" |
28 | |
29 | #include <string.h> |
30 | |
31 | /* Structure linking OIDs for X.509 DN AttributeTypes to their |
32 | * string representations and default string encodings used by Mbed TLS. */ |
33 | typedef struct { |
34 | const char *name; /* String representation of AttributeType, e.g. |
35 | * "CN" or "emailAddress". */ |
36 | size_t name_len; /* Length of 'name', without trailing 0 byte. */ |
37 | const char *oid; /* String representation of OID of AttributeType, |
38 | * as per RFC 5280, Appendix A.1. */ |
39 | int default_tag; /* The default character encoding used for the |
40 | * given attribute type, e.g. |
41 | * MBEDTLS_ASN1_UTF8_STRING for UTF-8. */ |
42 | } x509_attr_descriptor_t; |
43 | |
44 | #define ADD_STRLEN(s) s, sizeof(s) - 1 |
45 | |
46 | /* X.509 DN attributes from RFC 5280, Appendix A.1. */ |
47 | static const x509_attr_descriptor_t x509_attrs[] = |
48 | { |
49 | { ADD_STRLEN("CN" ), |
50 | MBEDTLS_OID_AT_CN, MBEDTLS_ASN1_UTF8_STRING }, |
51 | { ADD_STRLEN("commonName" ), |
52 | MBEDTLS_OID_AT_CN, MBEDTLS_ASN1_UTF8_STRING }, |
53 | { ADD_STRLEN("C" ), |
54 | MBEDTLS_OID_AT_COUNTRY, MBEDTLS_ASN1_PRINTABLE_STRING }, |
55 | { ADD_STRLEN("countryName" ), |
56 | MBEDTLS_OID_AT_COUNTRY, MBEDTLS_ASN1_PRINTABLE_STRING }, |
57 | { ADD_STRLEN("O" ), |
58 | MBEDTLS_OID_AT_ORGANIZATION, MBEDTLS_ASN1_UTF8_STRING }, |
59 | { ADD_STRLEN("organizationName" ), |
60 | MBEDTLS_OID_AT_ORGANIZATION, MBEDTLS_ASN1_UTF8_STRING }, |
61 | { ADD_STRLEN("L" ), |
62 | MBEDTLS_OID_AT_LOCALITY, MBEDTLS_ASN1_UTF8_STRING }, |
63 | { ADD_STRLEN("locality" ), |
64 | MBEDTLS_OID_AT_LOCALITY, MBEDTLS_ASN1_UTF8_STRING }, |
65 | { ADD_STRLEN("R" ), |
66 | MBEDTLS_OID_PKCS9_EMAIL, MBEDTLS_ASN1_IA5_STRING }, |
67 | { ADD_STRLEN("OU" ), |
68 | MBEDTLS_OID_AT_ORG_UNIT, MBEDTLS_ASN1_UTF8_STRING }, |
69 | { ADD_STRLEN("organizationalUnitName" ), |
70 | MBEDTLS_OID_AT_ORG_UNIT, MBEDTLS_ASN1_UTF8_STRING }, |
71 | { ADD_STRLEN("ST" ), |
72 | MBEDTLS_OID_AT_STATE, MBEDTLS_ASN1_UTF8_STRING }, |
73 | { ADD_STRLEN("stateOrProvinceName" ), |
74 | MBEDTLS_OID_AT_STATE, MBEDTLS_ASN1_UTF8_STRING }, |
75 | { ADD_STRLEN("emailAddress" ), |
76 | MBEDTLS_OID_PKCS9_EMAIL, MBEDTLS_ASN1_IA5_STRING }, |
77 | { ADD_STRLEN("serialNumber" ), |
78 | MBEDTLS_OID_AT_SERIAL_NUMBER, MBEDTLS_ASN1_PRINTABLE_STRING }, |
79 | { ADD_STRLEN("postalAddress" ), |
80 | MBEDTLS_OID_AT_POSTAL_ADDRESS, MBEDTLS_ASN1_PRINTABLE_STRING }, |
81 | { ADD_STRLEN("postalCode" ), |
82 | MBEDTLS_OID_AT_POSTAL_CODE, MBEDTLS_ASN1_PRINTABLE_STRING }, |
83 | { ADD_STRLEN("dnQualifier" ), |
84 | MBEDTLS_OID_AT_DN_QUALIFIER, MBEDTLS_ASN1_PRINTABLE_STRING }, |
85 | { ADD_STRLEN("title" ), |
86 | MBEDTLS_OID_AT_TITLE, MBEDTLS_ASN1_UTF8_STRING }, |
87 | { ADD_STRLEN("surName" ), |
88 | MBEDTLS_OID_AT_SUR_NAME, MBEDTLS_ASN1_UTF8_STRING }, |
89 | { ADD_STRLEN("SN" ), |
90 | MBEDTLS_OID_AT_SUR_NAME, MBEDTLS_ASN1_UTF8_STRING }, |
91 | { ADD_STRLEN("givenName" ), |
92 | MBEDTLS_OID_AT_GIVEN_NAME, MBEDTLS_ASN1_UTF8_STRING }, |
93 | { ADD_STRLEN("GN" ), |
94 | MBEDTLS_OID_AT_GIVEN_NAME, MBEDTLS_ASN1_UTF8_STRING }, |
95 | { ADD_STRLEN("initials" ), |
96 | MBEDTLS_OID_AT_INITIALS, MBEDTLS_ASN1_UTF8_STRING }, |
97 | { ADD_STRLEN("pseudonym" ), |
98 | MBEDTLS_OID_AT_PSEUDONYM, MBEDTLS_ASN1_UTF8_STRING }, |
99 | { ADD_STRLEN("generationQualifier" ), |
100 | MBEDTLS_OID_AT_GENERATION_QUALIFIER, MBEDTLS_ASN1_UTF8_STRING }, |
101 | { ADD_STRLEN("domainComponent" ), |
102 | MBEDTLS_OID_DOMAIN_COMPONENT, MBEDTLS_ASN1_IA5_STRING }, |
103 | { ADD_STRLEN("DC" ), |
104 | MBEDTLS_OID_DOMAIN_COMPONENT, MBEDTLS_ASN1_IA5_STRING }, |
105 | { NULL, 0, NULL, MBEDTLS_ASN1_NULL } |
106 | }; |
107 | |
108 | static const x509_attr_descriptor_t *x509_attr_descr_from_name(const char *name, size_t name_len) |
109 | { |
110 | const x509_attr_descriptor_t *cur; |
111 | |
112 | for (cur = x509_attrs; cur->name != NULL; cur++) { |
113 | if (cur->name_len == name_len && |
114 | strncmp(cur->name, name, name_len) == 0) { |
115 | break; |
116 | } |
117 | } |
118 | |
119 | if (cur->name == NULL) { |
120 | return NULL; |
121 | } |
122 | |
123 | return cur; |
124 | } |
125 | |
126 | int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *name) |
127 | { |
128 | int ret = MBEDTLS_ERR_X509_INVALID_NAME; |
129 | const char *s = name, *c = s; |
130 | const char *end = s + strlen(s); |
131 | const char *oid = NULL; |
132 | const x509_attr_descriptor_t *attr_descr = NULL; |
133 | int in_tag = 1; |
134 | char data[MBEDTLS_X509_MAX_DN_NAME_SIZE]; |
135 | char *d = data; |
136 | |
137 | /* Clear existing chain if present */ |
138 | mbedtls_asn1_free_named_data_list(head); |
139 | |
140 | while (c <= end) { |
141 | if (in_tag && *c == '=') { |
142 | if ((attr_descr = x509_attr_descr_from_name(s, c - s)) == NULL) { |
143 | ret = MBEDTLS_ERR_X509_UNKNOWN_OID; |
144 | goto exit; |
145 | } |
146 | |
147 | oid = attr_descr->oid; |
148 | s = c + 1; |
149 | in_tag = 0; |
150 | d = data; |
151 | } |
152 | |
153 | if (!in_tag && *c == '\\' && c != end) { |
154 | c++; |
155 | |
156 | /* Check for valid escaped characters */ |
157 | if (c == end || *c != ',') { |
158 | ret = MBEDTLS_ERR_X509_INVALID_NAME; |
159 | goto exit; |
160 | } |
161 | } else if (!in_tag && (*c == ',' || c == end)) { |
162 | mbedtls_asn1_named_data *cur = |
163 | mbedtls_asn1_store_named_data(head, oid, strlen(oid), |
164 | (unsigned char *) data, |
165 | d - data); |
166 | |
167 | if (cur == NULL) { |
168 | return MBEDTLS_ERR_X509_ALLOC_FAILED; |
169 | } |
170 | |
171 | // set tagType |
172 | cur->val.tag = attr_descr->default_tag; |
173 | |
174 | while (c < end && *(c + 1) == ' ') { |
175 | c++; |
176 | } |
177 | |
178 | s = c + 1; |
179 | in_tag = 1; |
180 | |
181 | /* Successfully parsed one name, update ret to success */ |
182 | ret = 0; |
183 | } |
184 | |
185 | if (!in_tag && s != c + 1) { |
186 | *(d++) = *c; |
187 | |
188 | if (d - data == MBEDTLS_X509_MAX_DN_NAME_SIZE) { |
189 | ret = MBEDTLS_ERR_X509_INVALID_NAME; |
190 | goto exit; |
191 | } |
192 | } |
193 | |
194 | c++; |
195 | } |
196 | |
197 | exit: |
198 | |
199 | return ret; |
200 | } |
201 | |
202 | /* The first byte of the value in the mbedtls_asn1_named_data structure is reserved |
203 | * to store the critical boolean for us |
204 | */ |
205 | int mbedtls_x509_set_extension(mbedtls_asn1_named_data **head, const char *oid, size_t oid_len, |
206 | int critical, const unsigned char *val, size_t val_len) |
207 | { |
208 | mbedtls_asn1_named_data *cur; |
209 | |
210 | if ((cur = mbedtls_asn1_store_named_data(head, oid, oid_len, |
211 | NULL, val_len + 1)) == NULL) { |
212 | return MBEDTLS_ERR_X509_ALLOC_FAILED; |
213 | } |
214 | |
215 | cur->val.p[0] = critical; |
216 | memcpy(cur->val.p + 1, val, val_len); |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | /* |
222 | * RelativeDistinguishedName ::= |
223 | * SET OF AttributeTypeAndValue |
224 | * |
225 | * AttributeTypeAndValue ::= SEQUENCE { |
226 | * type AttributeType, |
227 | * value AttributeValue } |
228 | * |
229 | * AttributeType ::= OBJECT IDENTIFIER |
230 | * |
231 | * AttributeValue ::= ANY DEFINED BY AttributeType |
232 | */ |
233 | static int x509_write_name(unsigned char **p, |
234 | unsigned char *start, |
235 | mbedtls_asn1_named_data *cur_name) |
236 | { |
237 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
238 | size_t len = 0; |
239 | const char *oid = (const char *) cur_name->oid.p; |
240 | size_t oid_len = cur_name->oid.len; |
241 | const unsigned char *name = cur_name->val.p; |
242 | size_t name_len = cur_name->val.len; |
243 | |
244 | // Write correct string tag and value |
245 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tagged_string(p, start, |
246 | cur_name->val.tag, |
247 | (const char *) name, |
248 | name_len)); |
249 | // Write OID |
250 | // |
251 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid, |
252 | oid_len)); |
253 | |
254 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); |
255 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, |
256 | MBEDTLS_ASN1_CONSTRUCTED | |
257 | MBEDTLS_ASN1_SEQUENCE)); |
258 | |
259 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); |
260 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, |
261 | MBEDTLS_ASN1_CONSTRUCTED | |
262 | MBEDTLS_ASN1_SET)); |
263 | |
264 | return (int) len; |
265 | } |
266 | |
267 | int mbedtls_x509_write_names(unsigned char **p, unsigned char *start, |
268 | mbedtls_asn1_named_data *first) |
269 | { |
270 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
271 | size_t len = 0; |
272 | mbedtls_asn1_named_data *cur = first; |
273 | |
274 | while (cur != NULL) { |
275 | MBEDTLS_ASN1_CHK_ADD(len, x509_write_name(p, start, cur)); |
276 | cur = cur->next; |
277 | } |
278 | |
279 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); |
280 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED | |
281 | MBEDTLS_ASN1_SEQUENCE)); |
282 | |
283 | return (int) len; |
284 | } |
285 | |
286 | int mbedtls_x509_write_sig(unsigned char **p, unsigned char *start, |
287 | const char *oid, size_t oid_len, |
288 | unsigned char *sig, size_t size) |
289 | { |
290 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
291 | size_t len = 0; |
292 | |
293 | if (*p < start || (size_t) (*p - start) < size) { |
294 | return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; |
295 | } |
296 | |
297 | len = size; |
298 | (*p) -= len; |
299 | memcpy(*p, sig, len); |
300 | |
301 | if (*p - start < 1) { |
302 | return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; |
303 | } |
304 | |
305 | *--(*p) = 0; |
306 | len += 1; |
307 | |
308 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); |
309 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_BIT_STRING)); |
310 | |
311 | // Write OID |
312 | // |
313 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_algorithm_identifier(p, start, oid, |
314 | oid_len, 0)); |
315 | |
316 | return (int) len; |
317 | } |
318 | |
319 | static int x509_write_extension(unsigned char **p, unsigned char *start, |
320 | mbedtls_asn1_named_data *ext) |
321 | { |
322 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
323 | size_t len = 0; |
324 | |
325 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, ext->val.p + 1, |
326 | ext->val.len - 1)); |
327 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, ext->val.len - 1)); |
328 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OCTET_STRING)); |
329 | |
330 | if (ext->val.p[0] != 0) { |
331 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_bool(p, start, 1)); |
332 | } |
333 | |
334 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, ext->oid.p, |
335 | ext->oid.len)); |
336 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, ext->oid.len)); |
337 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OID)); |
338 | |
339 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); |
340 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED | |
341 | MBEDTLS_ASN1_SEQUENCE)); |
342 | |
343 | return (int) len; |
344 | } |
345 | |
346 | /* |
347 | * Extension ::= SEQUENCE { |
348 | * extnID OBJECT IDENTIFIER, |
349 | * critical BOOLEAN DEFAULT FALSE, |
350 | * extnValue OCTET STRING |
351 | * -- contains the DER encoding of an ASN.1 value |
352 | * -- corresponding to the extension type identified |
353 | * -- by extnID |
354 | * } |
355 | */ |
356 | int mbedtls_x509_write_extensions(unsigned char **p, unsigned char *start, |
357 | mbedtls_asn1_named_data *first) |
358 | { |
359 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
360 | size_t len = 0; |
361 | mbedtls_asn1_named_data *cur_ext = first; |
362 | |
363 | while (cur_ext != NULL) { |
364 | MBEDTLS_ASN1_CHK_ADD(len, x509_write_extension(p, start, cur_ext)); |
365 | cur_ext = cur_ext->next; |
366 | } |
367 | |
368 | return (int) len; |
369 | } |
370 | |
371 | #endif /* MBEDTLS_X509_CREATE_C */ |
372 | |