/src/open62541/plugins/crypto/mbedtls/create_certificate.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | * |
5 | | * Copyright (c) 2023 Fraunhofer IOSB (Author: Noel Graf) |
6 | | * |
7 | | */ |
8 | | |
9 | | #include <open62541/plugin/create_certificate.h> |
10 | | |
11 | | #if defined(UA_ENABLE_ENCRYPTION_MBEDTLS) |
12 | | |
13 | | #include "securitypolicy_common.h" |
14 | | #include "../deps/musl_inet_pton.h" |
15 | | |
16 | | #include <time.h> |
17 | | |
18 | | #include <mbedtls/x509_crt.h> |
19 | | #include <mbedtls/oid.h> |
20 | | #include <mbedtls/asn1write.h> |
21 | | #include <mbedtls/entropy.h> |
22 | | #include <mbedtls/ctr_drbg.h> |
23 | | #include <mbedtls/platform.h> |
24 | | #include <mbedtls/version.h> |
25 | | |
26 | | #define SET_OID(x, oid) \ |
27 | 0 | do { x.len = MBEDTLS_OID_SIZE(oid); x.p = (unsigned char *) oid; } while (0) |
28 | | |
29 | | typedef struct mbedtls_write_san_node{ |
30 | | int type; |
31 | | char* host; |
32 | | size_t hostlen; |
33 | | } mbedtls_write_san_node; |
34 | | |
35 | | typedef struct mbedtls_write_san_list{ |
36 | | mbedtls_write_san_node node; |
37 | | struct mbedtls_write_san_list* next; |
38 | | } mbedtls_write_san_list; |
39 | | |
40 | | static size_t mbedtls_get_san_list_deep(const mbedtls_write_san_list* sanlist); |
41 | | |
42 | | int mbedtls_x509write_crt_set_subject_alt_name(mbedtls_x509write_cert *ctx, const mbedtls_write_san_list* sanlist); |
43 | | |
44 | | #if MBEDTLS_VERSION_NUMBER < 0x03030000 |
45 | | int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, |
46 | | const mbedtls_asn1_sequence *exts); |
47 | | #endif |
48 | | |
49 | | static int write_certificate(mbedtls_x509write_cert *crt, UA_CertificateFormat certFormat, |
50 | | UA_ByteString *outCertificate, int (*f_rng)(void *, unsigned char *, size_t), |
51 | | void *p_rng); |
52 | | |
53 | | static int write_private_key(mbedtls_pk_context *key, UA_CertificateFormat keyFormat, UA_ByteString *outPrivateKey); |
54 | | |
55 | | UA_StatusCode |
56 | | UA_CreateCertificate(const UA_Logger *logger, const UA_String *subject, |
57 | | size_t subjectSize, const UA_String *subjectAltName, |
58 | | size_t subjectAltNameSize, UA_CertificateFormat certFormat, |
59 | | UA_KeyValueMap *params, UA_ByteString *outPrivateKey, |
60 | 0 | UA_ByteString *outCertificate) { |
61 | 0 | if(!outPrivateKey || !outCertificate || !logger || !subjectAltName || !subject || |
62 | 0 | subjectAltNameSize == 0 || subjectSize == 0 || |
63 | 0 | (certFormat != UA_CERTIFICATEFORMAT_DER && certFormat != UA_CERTIFICATEFORMAT_PEM)) |
64 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
65 | | |
66 | | /* Use the maximum size */ |
67 | 0 | UA_UInt16 keySizeBits = 4096; |
68 | | /* Default to 1 year */ |
69 | 0 | UA_UInt16 expiresInDays = 365; |
70 | |
|
71 | 0 | if(params) { |
72 | 0 | const UA_UInt16 *keySizeBitsValue = (const UA_UInt16 *)UA_KeyValueMap_getScalar( |
73 | 0 | params, UA_QUALIFIEDNAME(0, "key-size-bits"), &UA_TYPES[UA_TYPES_UINT16]); |
74 | 0 | if(keySizeBitsValue) |
75 | 0 | keySizeBits = *keySizeBitsValue; |
76 | |
|
77 | 0 | const UA_UInt16 *expiresInDaysValue = (const UA_UInt16 *)UA_KeyValueMap_getScalar( |
78 | 0 | params, UA_QUALIFIEDNAME(0, "expires-in-days"), &UA_TYPES[UA_TYPES_UINT16]); |
79 | 0 | if(expiresInDaysValue) |
80 | 0 | expiresInDays = *expiresInDaysValue; |
81 | 0 | } |
82 | |
|
83 | 0 | UA_ByteString_init(outPrivateKey); |
84 | 0 | UA_ByteString_init(outCertificate); |
85 | |
|
86 | 0 | mbedtls_pk_context key; |
87 | 0 | mbedtls_ctr_drbg_context ctr_drbg; |
88 | 0 | mbedtls_entropy_context entropy; |
89 | 0 | const char *pers = "gen_key"; |
90 | 0 | mbedtls_x509write_cert crt; |
91 | |
|
92 | 0 | UA_StatusCode errRet = UA_STATUSCODE_GOOD; |
93 | | |
94 | | /* Set to sane values */ |
95 | 0 | mbedtls_pk_init(&key); |
96 | 0 | mbedtls_ctr_drbg_init(&ctr_drbg); |
97 | 0 | mbedtls_entropy_init(&entropy); |
98 | 0 | mbedtls_x509write_crt_init(&crt); |
99 | | |
100 | | /* Seed the random number generator */ |
101 | 0 | if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)pers, strlen(pers)) != 0) { |
102 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
103 | 0 | "Failed to initialize the random number generator."); |
104 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
105 | 0 | goto cleanup; |
106 | 0 | } |
107 | | |
108 | | /* Generate an RSA key pair */ |
109 | 0 | if (mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)) != 0 || |
110 | 0 | mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, &ctr_drbg, keySizeBits, 65537) != 0) { |
111 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
112 | 0 | "Failed to generate RSA key pair."); |
113 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
114 | 0 | goto cleanup; |
115 | 0 | } |
116 | | |
117 | | /* Setting certificate values */ |
118 | 0 | mbedtls_x509write_crt_set_version(&crt, MBEDTLS_X509_CRT_VERSION_3); |
119 | 0 | mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA256); |
120 | |
|
121 | 0 | size_t subject_char_len = 0; |
122 | 0 | for(size_t i = 0; i < subjectSize; i++) { |
123 | 0 | subject_char_len += subject[i].length; |
124 | 0 | } |
125 | 0 | char *subject_char = (char*)UA_malloc(subject_char_len + subjectSize); |
126 | 0 | if(!subject_char) { |
127 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
128 | 0 | "Cannot allocate memory for subject. Out of memory."); |
129 | 0 | errRet = UA_STATUSCODE_BADOUTOFMEMORY; |
130 | 0 | goto cleanup; |
131 | 0 | } |
132 | | |
133 | 0 | size_t pos = 0; |
134 | 0 | for(size_t i = 0; i < subjectSize; i++) { |
135 | 0 | subject_char_len += subject[i].length; |
136 | 0 | memcpy(subject_char + pos, subject[i].data, subject[i].length); |
137 | 0 | pos += subject[i].length; |
138 | 0 | if(i < subjectSize - 1) |
139 | 0 | subject_char[pos++] = ','; |
140 | 0 | else |
141 | 0 | subject_char[pos++] = '\0'; |
142 | 0 | } |
143 | |
|
144 | 0 | if((mbedtls_x509write_crt_set_subject_name(&crt, subject_char)) != 0) { |
145 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
146 | 0 | "Setting subject failed."); |
147 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
148 | 0 | UA_free(subject_char); |
149 | 0 | goto cleanup; |
150 | 0 | } |
151 | | |
152 | 0 | if((mbedtls_x509write_crt_set_issuer_name(&crt, subject_char)) != 0) { |
153 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
154 | 0 | "Setting issuer failed."); |
155 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
156 | 0 | UA_free(subject_char); |
157 | 0 | goto cleanup; |
158 | 0 | } |
159 | | |
160 | 0 | UA_free(subject_char); |
161 | |
|
162 | 0 | mbedtls_write_san_list *cur = NULL; |
163 | 0 | mbedtls_write_san_list *cur_tmp = NULL; |
164 | 0 | mbedtls_write_san_list *head = NULL; |
165 | 0 | for(size_t i = 0; i < subjectAltNameSize; i++) { |
166 | 0 | char *sanType; |
167 | 0 | char *sanValue; |
168 | 0 | size_t sanValueLength; |
169 | 0 | char *subAlt = (char *)UA_malloc(subjectAltName[i].length + 1); |
170 | 0 | memcpy(subAlt, subjectAltName[i].data, subjectAltName[i].length); |
171 | | |
172 | | /* null-terminate the copied string */ |
173 | 0 | subAlt[subjectAltName[i].length] = 0; |
174 | | /* split into SAN type and value */ |
175 | 0 | sanType = strtok(subAlt, ":"); |
176 | 0 | sanValue = (char *)subjectAltName[i].data + strlen(sanType) + 1; |
177 | 0 | sanValueLength = subjectAltName[i].length - strlen(sanType) - 1; |
178 | |
|
179 | 0 | if(sanType) { |
180 | 0 | cur_tmp = (mbedtls_write_san_list*)mbedtls_calloc(1, sizeof(mbedtls_write_san_list)); |
181 | 0 | cur_tmp->next = NULL; |
182 | 0 | cur_tmp->node.host = sanValue; |
183 | 0 | cur_tmp->node.hostlen = sanValueLength; |
184 | |
|
185 | 0 | if(strcmp(sanType, "DNS") == 0) { |
186 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_DNS_NAME; |
187 | 0 | } else if(strcmp(sanType, "URI") == 0) { |
188 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER; |
189 | 0 | } else if(strcmp(sanType, "IP") == 0) { |
190 | 0 | uint8_t ip[4] = {0}; |
191 | 0 | if(musl_inet_pton(AF_INET, sanValue, ip) <= 0) { |
192 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURECHANNEL, "IP SAN preparation failed"); |
193 | 0 | mbedtls_free(cur_tmp); |
194 | 0 | UA_free(subAlt); |
195 | 0 | continue; |
196 | 0 | } |
197 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_IP_ADDRESS; |
198 | 0 | cur_tmp->node.host = (char *)ip; |
199 | 0 | cur_tmp->node.hostlen = sizeof(ip); |
200 | 0 | } else if(strcmp(sanType, "RFC822") == 0) { |
201 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_RFC822_NAME; |
202 | 0 | } else { |
203 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURECHANNEL, "Given an unsupported SAN"); |
204 | 0 | mbedtls_free(cur_tmp); |
205 | 0 | UA_free(subAlt); |
206 | 0 | continue; |
207 | 0 | } |
208 | 0 | } else { |
209 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURECHANNEL, "Invalid Input format"); |
210 | 0 | UA_free(subAlt); |
211 | 0 | continue; |
212 | 0 | } |
213 | | |
214 | 0 | if(!cur) { |
215 | 0 | cur = cur_tmp; |
216 | 0 | head = cur_tmp; |
217 | 0 | } else { |
218 | 0 | cur->next = cur_tmp; |
219 | 0 | cur = cur->next; |
220 | 0 | } |
221 | |
|
222 | 0 | UA_free(subAlt); |
223 | 0 | } |
224 | |
|
225 | 0 | if((mbedtls_x509write_crt_set_subject_alt_name(&crt, head)) != 0) { |
226 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
227 | 0 | "Setting subject alternative name failed."); |
228 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
229 | 0 | while(head != NULL) { |
230 | 0 | cur_tmp = head->next; |
231 | 0 | mbedtls_free(head); |
232 | 0 | head = cur_tmp; |
233 | 0 | } |
234 | 0 | goto cleanup; |
235 | 0 | } |
236 | | |
237 | 0 | while(head != NULL) { |
238 | 0 | cur_tmp = head->next; |
239 | 0 | mbedtls_free(head); |
240 | 0 | head = cur_tmp; |
241 | 0 | } |
242 | |
|
243 | | #if MBEDTLS_VERSION_NUMBER >= 0x03040000 |
244 | | unsigned char *serial = (unsigned char *)"1"; |
245 | | size_t serial_len = 1; |
246 | | mbedtls_x509write_crt_set_serial_raw(&crt, serial, serial_len); |
247 | | #else |
248 | 0 | mbedtls_mpi serial_mpi; |
249 | 0 | mbedtls_mpi_init(&serial_mpi); |
250 | 0 | mbedtls_mpi_lset(&serial_mpi, 1); |
251 | 0 | mbedtls_x509write_crt_set_serial(&crt, &serial_mpi); |
252 | 0 | mbedtls_mpi_free(&serial_mpi); |
253 | 0 | #endif |
254 | | |
255 | | /* Get the current time */ |
256 | 0 | time_t rawTime; |
257 | 0 | struct tm *timeInfo; |
258 | 0 | time(&rawTime); |
259 | 0 | timeInfo = gmtime(&rawTime); |
260 | | |
261 | | /* Format the current timestamp */ |
262 | 0 | char current_timestamp[15]; // YYYYMMDDhhmmss + '\0' |
263 | 0 | strftime(current_timestamp, sizeof(current_timestamp), "%Y%m%d%H%M%S", timeInfo); |
264 | | |
265 | | /* Calculate the future timestamp */ |
266 | 0 | timeInfo->tm_mday += expiresInDays; |
267 | 0 | time_t future_time = mktime(timeInfo); |
268 | | |
269 | | /* Format the future timestamp */ |
270 | 0 | char future_timestamp[15]; // YYYYMMDDhhmmss + '\0' |
271 | 0 | strftime(future_timestamp, sizeof(future_timestamp), "%Y%m%d%H%M%S", gmtime(&future_time)); |
272 | |
|
273 | 0 | if(mbedtls_x509write_crt_set_validity(&crt, current_timestamp, future_timestamp) != 0) { |
274 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
275 | 0 | "Setting 'not before' and 'not after' failed."); |
276 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
277 | 0 | goto cleanup; |
278 | 0 | } |
279 | | |
280 | 0 | if(mbedtls_x509write_crt_set_basic_constraints(&crt, 0, -1) != 0) { |
281 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
282 | 0 | "Setting basic constraints failed."); |
283 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
284 | 0 | goto cleanup; |
285 | 0 | } |
286 | | |
287 | 0 | if(mbedtls_x509write_crt_set_key_usage(&crt, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION |
288 | 0 | | MBEDTLS_X509_KU_KEY_ENCIPHERMENT | MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
289 | 0 | | MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN) != 0) { |
290 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
291 | 0 | "Setting key usage failed."); |
292 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
293 | 0 | goto cleanup; |
294 | 0 | } |
295 | | |
296 | 0 | mbedtls_asn1_sequence *ext_key_usage; |
297 | 0 | ext_key_usage = (mbedtls_asn1_sequence *)mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); |
298 | 0 | ext_key_usage->buf.tag = MBEDTLS_ASN1_OID; |
299 | 0 | SET_OID(ext_key_usage->buf, MBEDTLS_OID_SERVER_AUTH); |
300 | 0 | ext_key_usage->next = (mbedtls_asn1_sequence *)mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); |
301 | 0 | ext_key_usage->next->buf.tag = MBEDTLS_ASN1_OID; |
302 | 0 | SET_OID(ext_key_usage->next->buf, MBEDTLS_OID_CLIENT_AUTH); |
303 | |
|
304 | 0 | if(mbedtls_x509write_crt_set_ext_key_usage(&crt, ext_key_usage) != 0) { |
305 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
306 | 0 | "Setting extended key usage failed."); |
307 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
308 | 0 | mbedtls_free(ext_key_usage->next); |
309 | 0 | mbedtls_free(ext_key_usage); |
310 | 0 | goto cleanup; |
311 | 0 | } |
312 | | |
313 | 0 | mbedtls_free(ext_key_usage->next); |
314 | 0 | mbedtls_free(ext_key_usage); |
315 | |
|
316 | 0 | mbedtls_x509write_crt_set_subject_key(&crt, &key); |
317 | 0 | mbedtls_x509write_crt_set_issuer_key(&crt, &key); |
318 | | |
319 | | |
320 | | /* Write private key */ |
321 | 0 | if ((write_private_key(&key, certFormat, outPrivateKey)) != 0) { |
322 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
323 | 0 | "Create Certificate: Writing private key failed."); |
324 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
325 | 0 | goto cleanup; |
326 | 0 | } |
327 | | |
328 | | /* Write Certificate */ |
329 | 0 | if ((write_certificate(&crt, certFormat, outCertificate, |
330 | 0 | mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { |
331 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
332 | 0 | "Create Certificate: Writing certificate failed."); |
333 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
334 | 0 | goto cleanup; |
335 | 0 | } |
336 | | |
337 | 0 | mbedtls_ctr_drbg_free(&ctr_drbg); |
338 | 0 | mbedtls_entropy_free(&entropy); |
339 | 0 | mbedtls_x509write_crt_free(&crt); |
340 | 0 | mbedtls_pk_free(&key); |
341 | |
|
342 | 0 | cleanup: |
343 | 0 | mbedtls_ctr_drbg_free(&ctr_drbg); |
344 | 0 | mbedtls_entropy_free(&entropy); |
345 | 0 | mbedtls_x509write_crt_free(&crt); |
346 | 0 | mbedtls_pk_free(&key); |
347 | 0 | return errRet; |
348 | 0 | } |
349 | | |
350 | 0 | static int write_private_key(mbedtls_pk_context *key, UA_CertificateFormat keyFormat, UA_ByteString *outPrivateKey) { |
351 | 0 | int ret; |
352 | 0 | unsigned char output_buf[16000]; |
353 | 0 | unsigned char *c = output_buf; |
354 | 0 | size_t len = 0; |
355 | |
|
356 | 0 | memset(output_buf, 0, sizeof(output_buf)); |
357 | 0 | switch(keyFormat) { |
358 | 0 | case UA_CERTIFICATEFORMAT_DER: { |
359 | 0 | if((ret = mbedtls_pk_write_key_der(key, output_buf, sizeof(output_buf))) < 0) { |
360 | 0 | return ret; |
361 | 0 | } |
362 | | |
363 | 0 | len = ret; |
364 | 0 | c = output_buf + sizeof(output_buf) - len; |
365 | 0 | break; |
366 | 0 | } |
367 | 0 | case UA_CERTIFICATEFORMAT_PEM: { |
368 | 0 | if((ret = mbedtls_pk_write_key_pem(key, output_buf, sizeof(output_buf))) != 0) { |
369 | 0 | return ret; |
370 | 0 | } |
371 | | |
372 | 0 | len = strlen((char *)output_buf); |
373 | 0 | break; |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | 0 | outPrivateKey->length = len; |
378 | 0 | UA_ByteString_allocBuffer(outPrivateKey, outPrivateKey->length); |
379 | 0 | memcpy(outPrivateKey->data, c, outPrivateKey->length); |
380 | |
|
381 | 0 | return 0; |
382 | 0 | } |
383 | | |
384 | | static int write_certificate(mbedtls_x509write_cert *crt, UA_CertificateFormat certFormat, |
385 | | UA_ByteString *outCertificate, int (*f_rng)(void *, unsigned char *, size_t), |
386 | 0 | void *p_rng) { |
387 | 0 | int ret; |
388 | 0 | unsigned char output_buf[4096]; |
389 | 0 | unsigned char *c = output_buf; |
390 | 0 | size_t len = 0; |
391 | |
|
392 | 0 | memset(output_buf, 0, sizeof(output_buf)); |
393 | 0 | switch(certFormat) { |
394 | 0 | case UA_CERTIFICATEFORMAT_DER: { |
395 | 0 | if((ret = mbedtls_x509write_crt_der(crt, output_buf, sizeof(output_buf), f_rng, p_rng)) < 0) { |
396 | 0 | return ret; |
397 | 0 | } |
398 | | |
399 | 0 | len = ret; |
400 | 0 | c = output_buf + sizeof(output_buf) - len; |
401 | 0 | break; |
402 | 0 | } |
403 | 0 | case UA_CERTIFICATEFORMAT_PEM: { |
404 | 0 | if((ret = mbedtls_x509write_crt_pem(crt, output_buf, sizeof(output_buf), f_rng, p_rng)) < 0) { |
405 | 0 | return ret; |
406 | 0 | } |
407 | | |
408 | 0 | len = strlen((char *)output_buf); |
409 | 0 | break; |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | 0 | outCertificate->length = len; |
414 | 0 | UA_ByteString_allocBuffer(outCertificate, outCertificate->length); |
415 | 0 | memcpy(outCertificate->data, c, outCertificate->length); |
416 | |
|
417 | 0 | return 0; |
418 | 0 | } |
419 | | |
420 | | #if MBEDTLS_VERSION_NUMBER < 0x03030000 |
421 | | int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, |
422 | 0 | const mbedtls_asn1_sequence *exts) { |
423 | 0 | unsigned char buf[256]; |
424 | 0 | unsigned char *c = buf + sizeof(buf); |
425 | 0 | int ret; |
426 | 0 | size_t len = 0; |
427 | 0 | const mbedtls_asn1_sequence *last_ext = NULL; |
428 | 0 | const mbedtls_asn1_sequence *ext; |
429 | |
|
430 | 0 | memset(buf, 0, sizeof(buf)); |
431 | | |
432 | | /* We need at least one extension: SEQUENCE SIZE (1..MAX) OF KeyPurposeId */ |
433 | 0 | if(!exts) { |
434 | 0 | return MBEDTLS_ERR_X509_BAD_INPUT_DATA; |
435 | 0 | } |
436 | | |
437 | | /* Iterate over exts backwards, so we write them out in the requested order */ |
438 | 0 | while(last_ext != exts) { |
439 | 0 | for(ext = exts; ext->next != last_ext; ext = ext->next) { |
440 | 0 | } |
441 | 0 | if(ext->buf.tag != MBEDTLS_ASN1_OID) { |
442 | 0 | return MBEDTLS_ERR_X509_BAD_INPUT_DATA; |
443 | 0 | } |
444 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, buf, ext->buf.p, ext->buf.len)); |
445 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, ext->buf.len)); |
446 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_OID)); |
447 | 0 | last_ext = ext; |
448 | 0 | } |
449 | | |
450 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); |
451 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); |
452 | | |
453 | 0 | return mbedtls_x509write_crt_set_extension(ctx, MBEDTLS_OID_EXTENDED_KEY_USAGE, |
454 | 0 | MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), 1, c, len); |
455 | 0 | } |
456 | | |
457 | | #endif |
458 | | |
459 | 0 | static size_t mbedtls_get_san_list_deep(const mbedtls_write_san_list* sanlist) { |
460 | 0 | size_t ret = 0; |
461 | 0 | const mbedtls_write_san_list* cur = sanlist; |
462 | 0 | while (cur) { |
463 | 0 | ++ret; |
464 | 0 | cur = cur->next; |
465 | 0 | } |
466 | |
|
467 | 0 | return ret; |
468 | 0 | } |
469 | | |
470 | 0 | int mbedtls_x509write_crt_set_subject_alt_name(mbedtls_x509write_cert *ctx, const mbedtls_write_san_list* sanlist) { |
471 | 0 | int ret = 0; |
472 | 0 | size_t sandeep = 0; |
473 | 0 | const mbedtls_write_san_list* cur = sanlist; |
474 | 0 | unsigned char* buf; |
475 | 0 | unsigned char* pc; |
476 | 0 | size_t len; |
477 | 0 | size_t buflen = 0; |
478 | | |
479 | | /* How many alt names to be written */ |
480 | 0 | sandeep = mbedtls_get_san_list_deep(sanlist); |
481 | 0 | if (sandeep == 0) |
482 | 0 | return ret; |
483 | | |
484 | 0 | buflen = MBEDTLS_SAN_MAX_LEN * sandeep + sandeep; |
485 | 0 | buf = (unsigned char *)mbedtls_calloc(1, buflen); |
486 | 0 | if(!buf) |
487 | 0 | return MBEDTLS_ERR_ASN1_ALLOC_FAILED; |
488 | | |
489 | 0 | memset(buf, 0, buflen); |
490 | 0 | pc = buf + buflen; |
491 | |
|
492 | 0 | len = 0; |
493 | 0 | while(cur) { |
494 | 0 | switch (cur->node.type) { |
495 | 0 | case MBEDTLS_X509_SAN_DNS_NAME: |
496 | 0 | case MBEDTLS_X509_SAN_RFC822_NAME: |
497 | 0 | case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: |
498 | 0 | case MBEDTLS_X509_SAN_IP_ADDRESS: |
499 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, |
500 | 0 | mbedtls_asn1_write_raw_buffer(&pc, buf, (const unsigned char *)cur->node.host, |
501 | 0 | cur->node.hostlen)); |
502 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&pc, buf, cur->node.hostlen)); |
503 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_tag(&pc, buf, |
504 | 0 | MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); |
505 | 0 | break; |
506 | 0 | default: |
507 | | /* Error out on an unsupported SAN */ |
508 | 0 | ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; |
509 | 0 | goto cleanup; |
510 | 0 | } |
511 | | |
512 | 0 | cur = cur->next; |
513 | 0 | } |
514 | | |
515 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&pc, buf, len)); |
516 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_tag(&pc, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); |
517 | | |
518 | 0 | ret = mbedtls_x509write_crt_set_extension(ctx, MBEDTLS_OID_SUBJECT_ALT_NAME, |
519 | 0 | MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), 0, buf + buflen - len, len); |
520 | |
|
521 | 0 | mbedtls_free(buf); |
522 | 0 | return ret; |
523 | | |
524 | 0 | cleanup: |
525 | | mbedtls_free(buf); |
526 | 0 | return ret; |
527 | 0 | } |
528 | | |
529 | | #endif |