/src/open62541/plugins/crypto/mbedtls/certificategroup.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This work is licensed under a Creative Commons CCZero 1.0 Universal License. |
2 | | * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. |
3 | | * |
4 | | * Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB |
5 | | * Copyright 2019 (c) Kalycito Infotech Private Limited |
6 | | * Copyright 2019 (c) Julius Pfrommer, Fraunhofer IOSB |
7 | | * Copyright 2024 (c) Fraunhofer IOSB (Author: Noel Graf) |
8 | | */ |
9 | | |
10 | | #include <open62541/util.h> |
11 | | #include <open62541/plugin/certificategroup_default.h> |
12 | | |
13 | | #ifdef UA_ENABLE_ENCRYPTION_MBEDTLS |
14 | | |
15 | | #include <mbedtls/x509.h> |
16 | | #include <mbedtls/oid.h> |
17 | | #include <mbedtls/x509_crt.h> |
18 | | #include <mbedtls/entropy.h> |
19 | | #include <mbedtls/version.h> |
20 | | #include <mbedtls/sha256.h> |
21 | | #if defined(MBEDTLS_USE_PSA_CRYPTO) |
22 | | #include <mbedtls/psa_util.h> |
23 | | #endif |
24 | | |
25 | | #include "securitypolicy_common.h" |
26 | | |
27 | | #define REMOTECERTIFICATETRUSTED 1 |
28 | | #define ISSUERKNOWN 2 |
29 | | #define DUALPARENT 3 |
30 | | #define PARENTFOUND 4 |
31 | | |
32 | | /* Configuration parameters */ |
33 | | |
34 | | #define MEMORYCERTSTORE_PARAMETERSSIZE 2 |
35 | 0 | #define MEMORYCERTSTORE_PARAMINDEX_MAXTRUSTLISTSIZE 0 |
36 | 0 | #define MEMORYCERTSTORE_PARAMINDEX_MAXREJECTEDLISTSIZE 1 |
37 | | |
38 | | static const struct { |
39 | | UA_QualifiedName name; |
40 | | const UA_DataType *type; |
41 | | UA_Boolean required; |
42 | | } MemoryCertStoreParameters[MEMORYCERTSTORE_PARAMETERSSIZE] = { |
43 | | {{0, UA_STRING_STATIC("max-trust-listsize")}, &UA_TYPES[UA_TYPES_UINT16], false}, |
44 | | {{0, UA_STRING_STATIC("max-rejected-listsize")}, &UA_TYPES[UA_TYPES_STRING], false} |
45 | | }; |
46 | | |
47 | | typedef struct { |
48 | | UA_TrustListDataType trustList; |
49 | | size_t rejectedCertificatesSize; |
50 | | UA_ByteString *rejectedCertificates; |
51 | | |
52 | | UA_UInt32 maxTrustListSize; |
53 | | UA_UInt32 maxRejectedListSize; |
54 | | |
55 | | UA_Boolean reloadRequired; |
56 | | |
57 | | mbedtls_x509_crt trustedCertificates; |
58 | | mbedtls_x509_crt issuerCertificates; |
59 | | mbedtls_x509_crl trustedCrls; |
60 | | mbedtls_x509_crl issuerCrls; |
61 | | } MemoryCertStore; |
62 | | |
63 | | static UA_Boolean mbedtlsCheckCA(mbedtls_x509_crt *cert); |
64 | | |
65 | | static UA_StatusCode |
66 | 0 | MemoryCertStore_removeFromTrustList(UA_CertificateGroup *certGroup, const UA_TrustListDataType *trustList) { |
67 | | /* Check parameter */ |
68 | 0 | if(certGroup == NULL || trustList == NULL) { |
69 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
70 | 0 | } |
71 | | |
72 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
73 | 0 | context->reloadRequired = true; |
74 | 0 | return UA_TrustListDataType_remove(trustList, &context->trustList); |
75 | 0 | } |
76 | | |
77 | | static UA_StatusCode |
78 | 0 | MemoryCertStore_getTrustList(UA_CertificateGroup *certGroup, UA_TrustListDataType *trustList) { |
79 | | /* Check parameter */ |
80 | 0 | if(certGroup == NULL || trustList == NULL) { |
81 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
82 | 0 | } |
83 | | |
84 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
85 | 0 | return UA_TrustListDataType_copy(&context->trustList, trustList); |
86 | 0 | } |
87 | | |
88 | | static UA_StatusCode |
89 | 0 | MemoryCertStore_setTrustList(UA_CertificateGroup *certGroup, const UA_TrustListDataType *trustList) { |
90 | | /* Check parameter */ |
91 | 0 | if(certGroup == NULL || trustList == NULL) { |
92 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
93 | 0 | } |
94 | | |
95 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
96 | 0 | if(context->maxTrustListSize != 0 && UA_TrustListDataType_getSize(trustList) > context->maxTrustListSize) { |
97 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
98 | 0 | } |
99 | 0 | context->reloadRequired = true; |
100 | | /* Remove the section of the trust list that needs to be reset, while keeping the remaining parts intact */ |
101 | 0 | return UA_TrustListDataType_set(trustList, &context->trustList); |
102 | 0 | } |
103 | | |
104 | | static UA_StatusCode |
105 | 0 | MemoryCertStore_addToTrustList(UA_CertificateGroup *certGroup, const UA_TrustListDataType *trustList) { |
106 | | /* Check parameter */ |
107 | 0 | if(certGroup == NULL || trustList == NULL) { |
108 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
109 | 0 | } |
110 | | |
111 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
112 | 0 | if(context->maxTrustListSize != 0 && UA_TrustListDataType_getSize(&context->trustList) + UA_TrustListDataType_getSize(trustList) > context->maxTrustListSize) { |
113 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
114 | 0 | } |
115 | 0 | context->reloadRequired = true; |
116 | 0 | return UA_TrustListDataType_add(trustList, &context->trustList); |
117 | 0 | } |
118 | | |
119 | | static UA_StatusCode |
120 | 0 | MemoryCertStore_getRejectedList(UA_CertificateGroup *certGroup, UA_ByteString **rejectedList, size_t *rejectedListSize) { |
121 | | /* Check parameter */ |
122 | 0 | if(certGroup == NULL || rejectedList == NULL || rejectedListSize == NULL) { |
123 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
124 | 0 | } |
125 | | |
126 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
127 | 0 | UA_StatusCode retval = UA_Array_copy(context->rejectedCertificates, context->rejectedCertificatesSize, |
128 | 0 | (void**)rejectedList, &UA_TYPES[UA_TYPES_BYTESTRING]); |
129 | |
|
130 | 0 | if(retval == UA_STATUSCODE_GOOD) |
131 | 0 | *rejectedListSize = context->rejectedCertificatesSize; |
132 | |
|
133 | 0 | return retval; |
134 | 0 | } |
135 | | |
136 | | static UA_StatusCode |
137 | 0 | mbedtlsCheckCrlMatch(mbedtls_x509_crt *cert, mbedtls_x509_crl *crl) { |
138 | 0 | char certSubject[MBEDTLS_X509_MAX_DN_NAME_SIZE]; |
139 | 0 | char crlIssuer[MBEDTLS_X509_MAX_DN_NAME_SIZE]; |
140 | |
|
141 | 0 | mbedtls_x509_dn_gets(certSubject, sizeof(certSubject), &cert->subject); |
142 | 0 | mbedtls_x509_dn_gets(crlIssuer, sizeof(crlIssuer), &crl->issuer); |
143 | |
|
144 | 0 | if(strncmp(certSubject, crlIssuer, MBEDTLS_X509_MAX_DN_NAME_SIZE) == 0) |
145 | 0 | return UA_STATUSCODE_GOOD; |
146 | | |
147 | 0 | return UA_STATUSCODE_BADNOMATCH; |
148 | 0 | } |
149 | | |
150 | | static UA_StatusCode |
151 | | mbedtlsFindCrls(UA_CertificateGroup *certGroup, const UA_ByteString *certificate, |
152 | | const UA_ByteString *crlList, const size_t crlListSize, |
153 | 0 | UA_ByteString **crls, size_t *crlsSize) { |
154 | 0 | mbedtls_x509_crt cert; |
155 | 0 | mbedtls_x509_crt_init(&cert); |
156 | 0 | UA_StatusCode retval = UA_mbedTLS_LoadCertificate(certificate, &cert); |
157 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
158 | 0 | UA_LOG_WARNING(certGroup->logging, UA_LOGCATEGORY_SECURITYPOLICY, |
159 | 0 | "An error occurred while parsing the certificate."); |
160 | 0 | return retval; |
161 | 0 | } |
162 | | |
163 | | /* Check if the certificate is a CA certificate. |
164 | | * Only a CA certificate can have a CRL. */ |
165 | 0 | if(!mbedtlsCheckCA(&cert)) { |
166 | 0 | UA_LOG_WARNING(certGroup->logging, UA_LOGCATEGORY_SECURITYPOLICY, |
167 | 0 | "The certificate is not a CA certificate and therefore does not have a CRL."); |
168 | 0 | mbedtls_x509_crt_free(&cert); |
169 | 0 | return UA_STATUSCODE_GOOD; |
170 | 0 | } |
171 | | |
172 | 0 | UA_Boolean foundMatch = false; |
173 | 0 | for(size_t i = 0; i < crlListSize; i++) { |
174 | 0 | mbedtls_x509_crl crl; |
175 | 0 | mbedtls_x509_crl_init(&crl); |
176 | 0 | retval = UA_mbedTLS_LoadCrl(&crlList[i], &crl); |
177 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
178 | 0 | UA_LOG_WARNING(certGroup->logging, UA_LOGCATEGORY_SECURITYPOLICY, |
179 | 0 | "An error occurred while parsing the crl."); |
180 | 0 | mbedtls_x509_crt_free(&cert); |
181 | 0 | return retval; |
182 | 0 | } |
183 | | |
184 | 0 | retval = mbedtlsCheckCrlMatch(&cert, &crl); |
185 | 0 | mbedtls_x509_crl_free(&crl); |
186 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
187 | 0 | continue; |
188 | 0 | } |
189 | | |
190 | | /* Continue the search, as a certificate may be associated with multiple CRLs. */ |
191 | 0 | foundMatch = true; |
192 | 0 | retval = UA_Array_appendCopy((void **)crls, crlsSize, &crlList[i], |
193 | 0 | &UA_TYPES[UA_TYPES_BYTESTRING]); |
194 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
195 | 0 | mbedtls_x509_crt_free(&cert); |
196 | 0 | return retval; |
197 | 0 | } |
198 | 0 | } |
199 | 0 | mbedtls_x509_crt_free(&cert); |
200 | |
|
201 | 0 | if(!foundMatch) |
202 | 0 | return UA_STATUSCODE_BADNOMATCH; |
203 | | |
204 | 0 | return UA_STATUSCODE_GOOD; |
205 | 0 | } |
206 | | |
207 | | static UA_StatusCode |
208 | | MemoryCertStore_getCertificateCrls(UA_CertificateGroup *certGroup, const UA_ByteString *certificate, |
209 | | const UA_Boolean isTrusted, UA_ByteString **crls, |
210 | 0 | size_t *crlsSize) { |
211 | | /* Check parameter */ |
212 | 0 | if(certGroup == NULL || certificate == NULL || crls == NULL) { |
213 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
214 | 0 | } |
215 | | |
216 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
217 | |
|
218 | 0 | if(isTrusted) { |
219 | 0 | return mbedtlsFindCrls(certGroup, certificate, |
220 | 0 | context->trustList.trustedCrls, |
221 | 0 | context->trustList.trustedCrlsSize, crls, |
222 | 0 | crlsSize); |
223 | 0 | } |
224 | 0 | return mbedtlsFindCrls(certGroup, certificate, |
225 | 0 | context->trustList.issuerCrls, |
226 | 0 | context->trustList.issuerCrlsSize, crls, |
227 | 0 | crlsSize); |
228 | 0 | } |
229 | | |
230 | | static UA_StatusCode |
231 | 0 | MemoryCertStore_addToRejectedList(UA_CertificateGroup *certGroup, const UA_ByteString *certificate) { |
232 | | /* Check parameter */ |
233 | 0 | if(certGroup == NULL || certificate == NULL) { |
234 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
235 | 0 | } |
236 | | |
237 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
238 | | |
239 | | /* check duplicate certificate */ |
240 | 0 | for(size_t i = 0; i < context->rejectedCertificatesSize; i++) { |
241 | 0 | if(UA_ByteString_equal(certificate, &context->rejectedCertificates[i])) |
242 | 0 | return UA_STATUSCODE_GOOD; /* certificate already exist */ |
243 | 0 | } |
244 | | |
245 | | /* Store rejected certificate */ |
246 | 0 | if(context->maxRejectedListSize == 0 || context->rejectedCertificatesSize < context->maxRejectedListSize) { |
247 | 0 | return UA_Array_appendCopy((void**)&context->rejectedCertificates, &context->rejectedCertificatesSize, |
248 | 0 | certificate, &UA_TYPES[UA_TYPES_BYTESTRING]); |
249 | 0 | } |
250 | 0 | UA_Array_delete(context->rejectedCertificates, context->rejectedCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
251 | 0 | context->rejectedCertificates = NULL; |
252 | 0 | context->rejectedCertificatesSize = 0; |
253 | 0 | return UA_Array_appendCopy((void**)&context->rejectedCertificates, &context->rejectedCertificatesSize, |
254 | 0 | certificate, &UA_TYPES[UA_TYPES_BYTESTRING]); |
255 | 0 | } |
256 | | |
257 | | static void |
258 | 0 | MemoryCertStore_clear(UA_CertificateGroup *certGroup) { |
259 | | /* check parameter */ |
260 | 0 | if(certGroup == NULL) { |
261 | 0 | return; |
262 | 0 | } |
263 | | |
264 | 0 | UA_NodeId_clear(&certGroup->certificateGroupId); |
265 | |
|
266 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
267 | 0 | if(context) { |
268 | 0 | UA_TrustListDataType_clear(&context->trustList); |
269 | |
|
270 | 0 | UA_Array_delete(context->rejectedCertificates, context->rejectedCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]); |
271 | 0 | context->rejectedCertificates = NULL; |
272 | 0 | context->rejectedCertificatesSize = 0; |
273 | |
|
274 | 0 | mbedtls_x509_crt_free(&context->trustedCertificates); |
275 | 0 | mbedtls_x509_crt_free(&context->issuerCertificates); |
276 | 0 | mbedtls_x509_crl_free(&context->trustedCrls); |
277 | 0 | mbedtls_x509_crl_free(&context->issuerCrls); |
278 | |
|
279 | 0 | UA_free(context); |
280 | 0 | certGroup->context = NULL; |
281 | 0 | } |
282 | 0 | } |
283 | | |
284 | | static UA_StatusCode |
285 | 0 | reloadCertificates(UA_CertificateGroup *certGroup) { |
286 | | /* Check parameter */ |
287 | 0 | if(certGroup == NULL) { |
288 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
289 | 0 | } |
290 | | |
291 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
292 | 0 | UA_ByteString data; |
293 | 0 | UA_ByteString_init(&data); |
294 | 0 | int err = 0; |
295 | |
|
296 | 0 | mbedtls_x509_crt_free(&context->trustedCertificates); |
297 | 0 | mbedtls_x509_crt_init(&context->trustedCertificates); |
298 | 0 | for(size_t i = 0; i < context->trustList.trustedCertificatesSize; ++i) { |
299 | 0 | data = UA_mbedTLS_CopyDataFormatAware(&context->trustList.trustedCertificates[i]); |
300 | 0 | err = mbedtls_x509_crt_parse(&context->trustedCertificates, data.data, data.length); |
301 | 0 | UA_ByteString_clear(&data); |
302 | 0 | if(err) |
303 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
304 | 0 | } |
305 | | |
306 | 0 | mbedtls_x509_crt_free(&context->issuerCertificates); |
307 | 0 | mbedtls_x509_crt_init(&context->issuerCertificates); |
308 | 0 | for(size_t i = 0; i < context->trustList.issuerCertificatesSize; ++i) { |
309 | 0 | data = UA_mbedTLS_CopyDataFormatAware(&context->trustList.issuerCertificates[i]); |
310 | 0 | err = mbedtls_x509_crt_parse(&context->issuerCertificates, data.data, data.length); |
311 | 0 | UA_ByteString_clear(&data); |
312 | 0 | if(err) |
313 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
314 | 0 | } |
315 | | |
316 | 0 | mbedtls_x509_crl_free(&context->trustedCrls); |
317 | 0 | mbedtls_x509_crl_init(&context->trustedCrls); |
318 | 0 | for(size_t i = 0; i < context->trustList.trustedCrlsSize; i++) { |
319 | 0 | data = UA_mbedTLS_CopyDataFormatAware(&context->trustList.trustedCrls[i]); |
320 | 0 | err = mbedtls_x509_crl_parse(&context->trustedCrls, data.data, data.length); |
321 | 0 | UA_ByteString_clear(&data); |
322 | 0 | if(err) |
323 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
324 | 0 | } |
325 | | |
326 | 0 | mbedtls_x509_crl_free(&context->issuerCrls); |
327 | 0 | mbedtls_x509_crl_init(&context->issuerCrls); |
328 | 0 | for(size_t i = 0; i < context->trustList.issuerCrlsSize; i++) { |
329 | 0 | data = UA_mbedTLS_CopyDataFormatAware(&context->trustList.issuerCrls[i]); |
330 | 0 | err = mbedtls_x509_crl_parse(&context->issuerCrls, data.data, data.length); |
331 | 0 | UA_ByteString_clear(&data); |
332 | 0 | if(err) |
333 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
334 | 0 | } |
335 | | |
336 | 0 | return UA_STATUSCODE_GOOD; |
337 | 0 | } |
338 | | |
339 | 0 | #define UA_MBEDTLS_MAX_CHAIN_LENGTH 10 |
340 | 0 | #define UA_MBEDTLS_MAX_DN_LENGTH 256 |
341 | | |
342 | | /* We need to access some private fields below */ |
343 | | #ifndef MBEDTLS_PRIVATE |
344 | 0 | #define MBEDTLS_PRIVATE(x) x |
345 | | #endif |
346 | | |
347 | | /* Is the certificate a CA? */ |
348 | | static UA_Boolean |
349 | 0 | mbedtlsCheckCA(mbedtls_x509_crt *cert) { |
350 | | /* The Basic Constraints extension must be set and the cert acts as CA */ |
351 | 0 | if(!(cert->MBEDTLS_PRIVATE(ext_types) & MBEDTLS_X509_EXT_BASIC_CONSTRAINTS) || |
352 | 0 | !cert->MBEDTLS_PRIVATE(ca_istrue)) |
353 | 0 | return false; |
354 | | |
355 | | /* The Key Usage extension must be set to cert signing and CRL issuing */ |
356 | 0 | if(!(cert->MBEDTLS_PRIVATE(ext_types) & MBEDTLS_X509_EXT_KEY_USAGE) || |
357 | 0 | mbedtls_x509_crt_check_key_usage(cert, MBEDTLS_X509_KU_KEY_CERT_SIGN) != 0 || |
358 | 0 | mbedtls_x509_crt_check_key_usage(cert, MBEDTLS_X509_KU_CRL_SIGN) != 0) |
359 | 0 | return false; |
360 | | |
361 | 0 | return true; |
362 | 0 | } |
363 | | |
364 | | static UA_Boolean |
365 | 0 | mbedtlsSameName(UA_String name, const mbedtls_x509_name *name2) { |
366 | 0 | char buf[UA_MBEDTLS_MAX_DN_LENGTH]; |
367 | 0 | int len = mbedtls_x509_dn_gets(buf, UA_MBEDTLS_MAX_DN_LENGTH, name2); |
368 | 0 | if(len < 0) |
369 | 0 | return false; |
370 | 0 | UA_String nameString = {(size_t)len, (UA_Byte*)buf}; |
371 | 0 | return UA_String_equal(&name, &nameString); |
372 | 0 | } |
373 | | |
374 | | static UA_Boolean |
375 | 0 | mbedtlsSameBuf(mbedtls_x509_buf *a, mbedtls_x509_buf *b) { |
376 | 0 | if(a->len != b->len) |
377 | 0 | return false; |
378 | 0 | return (memcmp(a->p, b->p, a->len) == 0); |
379 | 0 | } |
380 | | |
381 | | /* Return the first matching issuer candidate AFTER prev. |
382 | | * This can return the cert itself if self-signed. */ |
383 | | static mbedtls_x509_crt * |
384 | | mbedtlsFindNextIssuer(MemoryCertStore *ctx, mbedtls_x509_crt *stack, |
385 | 0 | mbedtls_x509_crt *cert, mbedtls_x509_crt *prev) { |
386 | 0 | char inbuf[UA_MBEDTLS_MAX_DN_LENGTH]; |
387 | 0 | int nameLen = mbedtls_x509_dn_gets(inbuf, UA_MBEDTLS_MAX_DN_LENGTH, &cert->issuer); |
388 | 0 | if(nameLen < 0) |
389 | 0 | return NULL; |
390 | 0 | UA_String issuerName = {(size_t)nameLen, (UA_Byte*)inbuf}; |
391 | 0 | do { |
392 | 0 | for(mbedtls_x509_crt *i = stack; i; i = i->next) { |
393 | 0 | if(prev) { |
394 | 0 | if(prev == i) |
395 | 0 | prev = NULL; /* This was the last issuer we tried to verify */ |
396 | 0 | continue; |
397 | 0 | } |
398 | | /* Compare issuer name and subject name. |
399 | | * Skip when the key does not match the signature. */ |
400 | 0 | if(mbedtlsSameName(issuerName, &i->subject) && |
401 | 0 | mbedtls_pk_can_do(&i->pk, cert->MBEDTLS_PRIVATE(sig_pk))) |
402 | 0 | return i; |
403 | 0 | } |
404 | | |
405 | | /* Switch from the stack that came with the cert to the issuer list and |
406 | | * then to the trust list. */ |
407 | 0 | if(stack == &ctx->trustedCertificates) |
408 | 0 | stack = NULL; |
409 | 0 | else if(stack == &ctx->issuerCertificates) |
410 | 0 | stack = &ctx->trustedCertificates; |
411 | 0 | else |
412 | 0 | stack = &ctx->issuerCertificates; |
413 | 0 | } while(stack); |
414 | 0 | return NULL; |
415 | 0 | } |
416 | | |
417 | | static UA_StatusCode |
418 | 0 | mbedtlsCheckRevoked(UA_CertificateGroup *cg, MemoryCertStore *ctx, mbedtls_x509_crt *cert) { |
419 | | /* Parse the Issuer Name */ |
420 | 0 | char inbuf[UA_MBEDTLS_MAX_DN_LENGTH]; |
421 | 0 | int nameLen = mbedtls_x509_dn_gets(inbuf, UA_MBEDTLS_MAX_DN_LENGTH, &cert->issuer); |
422 | 0 | if(nameLen < 0) |
423 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
424 | 0 | UA_String issuerName = {(size_t)nameLen, (UA_Byte*)inbuf}; |
425 | |
|
426 | 0 | if(ctx->trustedCrls.raw.len == 0 && ctx->issuerCrls.raw.len == 0) { |
427 | 0 | UA_LOG_WARNING(cg->logging, UA_LOGCATEGORY_SECURITYPOLICY, |
428 | 0 | "Zero revocation lists have been loaded. " |
429 | 0 | "This seems intentional - omitting the check."); |
430 | 0 | return UA_STATUSCODE_GOOD; |
431 | 0 | } |
432 | | |
433 | | /* Loop over the crl and match the Issuer Name */ |
434 | 0 | UA_StatusCode res = UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN; |
435 | 0 | for(mbedtls_x509_crl *crl = &ctx->trustedCrls; crl; crl = crl->next) { |
436 | | /* Is the CRL for certificates from the cert issuer? |
437 | | * Is the serial number of the certificate contained in the CRL? */ |
438 | 0 | if(mbedtlsSameName(issuerName, &crl->issuer)) { |
439 | 0 | if(mbedtls_x509_crt_is_revoked(cert, crl) != 0) |
440 | 0 | return UA_STATUSCODE_BADCERTIFICATEREVOKED; |
441 | 0 | res = UA_STATUSCODE_GOOD; /* There was at least one crl that did not revoke (so far) */ |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | /* Loop over the issuer crls separately */ |
446 | 0 | for(mbedtls_x509_crl *crl = &ctx->issuerCrls; crl; crl = crl->next) { |
447 | 0 | if(mbedtlsSameName(issuerName, &crl->issuer)) { |
448 | 0 | if(mbedtls_x509_crt_is_revoked(cert, crl) != 0) |
449 | 0 | return UA_STATUSCODE_BADCERTIFICATEREVOKED; |
450 | 0 | res = UA_STATUSCODE_GOOD; |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | 0 | return res; |
455 | 0 | } |
456 | | |
457 | | /* Verify that the public key of the issuer was used to sign the certificate */ |
458 | | static UA_Boolean |
459 | 0 | mbedtlsCheckSignature(const mbedtls_x509_crt *cert, mbedtls_x509_crt *issuer) { |
460 | 0 | size_t hash_len; |
461 | 0 | unsigned char hash[MBEDTLS_MD_MAX_SIZE]; |
462 | 0 | mbedtls_md_type_t md = cert->MBEDTLS_PRIVATE(sig_md); |
463 | 0 | #if !defined(MBEDTLS_USE_PSA_CRYPTO) |
464 | 0 | const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md); |
465 | 0 | hash_len = mbedtls_md_get_size(md_info); |
466 | 0 | if(mbedtls_md(md_info, cert->tbs.p, cert->tbs.len, hash) != 0) |
467 | 0 | return false; |
468 | | #else |
469 | | if(psa_hash_compute(mbedtls_md_psa_alg_from_type(md), cert->tbs.p, |
470 | | cert->tbs.len, hash, sizeof(hash), &hash_len) != PSA_SUCCESS) |
471 | | return false; |
472 | | #endif |
473 | 0 | const mbedtls_x509_buf *sig = &cert->MBEDTLS_PRIVATE(sig); |
474 | 0 | void *sig_opts = cert->MBEDTLS_PRIVATE(sig_opts); |
475 | 0 | mbedtls_pk_type_t pktype = cert->MBEDTLS_PRIVATE(sig_pk); |
476 | 0 | return (mbedtls_pk_verify_ext(pktype, sig_opts, &issuer->pk, md, |
477 | 0 | hash, hash_len, sig->p, sig->len) == 0); |
478 | 0 | } |
479 | | |
480 | | static UA_StatusCode |
481 | | mbedtlsVerifyChain(UA_CertificateGroup *cg, MemoryCertStore *ctx, mbedtls_x509_crt *stack, |
482 | 0 | mbedtls_x509_crt **old_issuers, mbedtls_x509_crt *cert, int depth) { |
483 | | /* Maxiumum chain length */ |
484 | 0 | if(depth == UA_MBEDTLS_MAX_CHAIN_LENGTH) |
485 | 0 | return UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE; |
486 | | |
487 | | /* Verification Step: Validity Period */ |
488 | 0 | if(mbedtls_x509_time_is_future(&cert->valid_from) || |
489 | 0 | mbedtls_x509_time_is_past(&cert->valid_to)) |
490 | 0 | return (depth == 0) ? UA_STATUSCODE_BADCERTIFICATETIMEINVALID : |
491 | 0 | UA_STATUSCODE_BADCERTIFICATEISSUERTIMEINVALID; |
492 | | |
493 | | /* Return the most specific error code. BADCERTIFICATECHAININCOMPLETE is |
494 | | * returned only if all possible chains are incomplete. */ |
495 | 0 | mbedtls_x509_crt *issuer = NULL; |
496 | 0 | UA_StatusCode ret = UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE; |
497 | 0 | while(ret != UA_STATUSCODE_GOOD) { |
498 | | /* Find the issuer. This can return the same certificate if it is |
499 | | * self-signed (subject == issuer). We come back here to try a different |
500 | | * "path" if a subsequent verification fails. */ |
501 | 0 | issuer = mbedtlsFindNextIssuer(ctx, stack, cert, issuer); |
502 | 0 | if(!issuer) |
503 | 0 | break; |
504 | | |
505 | | /* Verification Step: Certificate Usage |
506 | | * Can the issuer act as CA? Omit for self-signed leaf certificates. */ |
507 | 0 | if((depth > 0 || issuer != cert) && !mbedtlsCheckCA(issuer)) { |
508 | 0 | ret = UA_STATUSCODE_BADCERTIFICATEISSUERUSENOTALLOWED; |
509 | 0 | continue; |
510 | 0 | } |
511 | | |
512 | | /* Verification Step: Signature */ |
513 | 0 | if(!mbedtlsCheckSignature(cert, issuer)) { |
514 | 0 | ret = UA_STATUSCODE_BADCERTIFICATEINVALID; /* Wrong issuer, try again */ |
515 | 0 | continue; |
516 | 0 | } |
517 | | |
518 | | /* The certificate is self-signed. We have arrived at the top of the |
519 | | * chain. We check whether the certificate is trusted below. This is the |
520 | | * only place where we return UA_STATUSCODE_BADCERTIFICATEUNTRUSTED. |
521 | | * This signals that the chain is complete (but can be still |
522 | | * untrusted). |
523 | | * |
524 | | * Break here as we have reached the end of the chain. Omit the |
525 | | * Revocation Check for self-signed certificates. */ |
526 | 0 | if(issuer == cert || mbedtlsSameBuf(&cert->tbs, &issuer->tbs)) { |
527 | 0 | ret = UA_STATUSCODE_BADCERTIFICATEUNTRUSTED; |
528 | 0 | break; |
529 | 0 | } |
530 | | |
531 | | /* Verification Step: Revocation Check */ |
532 | 0 | ret = mbedtlsCheckRevoked(cg, ctx, cert); |
533 | 0 | if(depth > 0) { |
534 | 0 | if(ret == UA_STATUSCODE_BADCERTIFICATEREVOKED) |
535 | 0 | ret = UA_STATUSCODE_BADCERTIFICATEISSUERREVOKED; |
536 | 0 | if(ret == UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN) |
537 | 0 | ret = UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN; |
538 | 0 | } |
539 | 0 | if(ret != UA_STATUSCODE_GOOD) |
540 | 0 | continue; |
541 | | |
542 | | /* Detect (endless) loops of issuers */ |
543 | 0 | for(int i = 0; i < depth; i++) { |
544 | 0 | if(old_issuers[i] == issuer) |
545 | 0 | return UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE; |
546 | 0 | } |
547 | 0 | old_issuers[depth] = issuer; |
548 | | |
549 | | /* We have found the issuer certificate used for the signature. Recurse |
550 | | * to the next certificate in the chain (verify the current issuer). */ |
551 | 0 | ret = mbedtlsVerifyChain(cg, ctx, stack, old_issuers, issuer, depth + 1); |
552 | 0 | } |
553 | | |
554 | | /* The chain is complete, but we haven't yet identified a trusted |
555 | | * certificate "on the way down". Can we trust this certificate? */ |
556 | 0 | if(ret == UA_STATUSCODE_BADCERTIFICATEUNTRUSTED) { |
557 | 0 | for(mbedtls_x509_crt *t = &ctx->trustedCertificates; t; t = t->next) { |
558 | 0 | if(mbedtlsSameBuf(&cert->tbs, &t->tbs)) |
559 | 0 | return UA_STATUSCODE_GOOD; |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | 0 | return ret; |
564 | 0 | } |
565 | | |
566 | | /* This follows Part 6, 6.1.3 Determining if a Certificate is trusted. |
567 | | * It defines a sequence of steps for certificate verification. */ |
568 | | static UA_StatusCode |
569 | 0 | verifyCertificate(UA_CertificateGroup *certGroup, const UA_ByteString *certificate) { |
570 | | /* Check parameter */ |
571 | 0 | if (certGroup == NULL || certGroup->context == NULL) { |
572 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
573 | 0 | } |
574 | | |
575 | 0 | MemoryCertStore *context = (MemoryCertStore *)certGroup->context; |
576 | 0 | if(context->reloadRequired) { |
577 | 0 | UA_StatusCode retval = reloadCertificates(certGroup); |
578 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
579 | 0 | return retval; |
580 | 0 | } |
581 | 0 | context->reloadRequired = false; |
582 | 0 | } |
583 | | |
584 | | /* Verification Step: Certificate Structure |
585 | | * This parses the entire certificate chain contained in the bytestring. */ |
586 | 0 | mbedtls_x509_crt cert; |
587 | 0 | mbedtls_x509_crt_init(&cert); |
588 | 0 | int mbedErr = mbedtls_x509_crt_parse(&cert, certificate->data, |
589 | 0 | certificate->length); |
590 | 0 | if(mbedErr) |
591 | 0 | return UA_STATUSCODE_BADCERTIFICATEINVALID; |
592 | | |
593 | | /* Verification Step: Certificate Usage |
594 | | * Check whether the certificate is a User certificate or a CA certificate. |
595 | | * Refer the test case CTT/Security/Security Certificate Validation/029.js |
596 | | * for more details. */ |
597 | 0 | if(mbedtlsCheckCA(&cert)) { |
598 | 0 | mbedtls_x509_crt_free(&cert); |
599 | 0 | return UA_STATUSCODE_BADCERTIFICATEUSENOTALLOWED; |
600 | 0 | } |
601 | | |
602 | | /* These steps are performed outside of this method. |
603 | | * Because we need the server or client context. |
604 | | * - Security Policy |
605 | | * - Host Name |
606 | | * - URI */ |
607 | | |
608 | | /* Verification Step: Build Certificate Chain |
609 | | * We perform the checks for each certificate inside. */ |
610 | 0 | mbedtls_x509_crt *old_issuers[UA_MBEDTLS_MAX_CHAIN_LENGTH]; |
611 | 0 | UA_StatusCode ret = mbedtlsVerifyChain(certGroup, context, &cert, old_issuers, &cert, 0); |
612 | 0 | mbedtls_x509_crt_free(&cert); |
613 | 0 | return ret; |
614 | 0 | } |
615 | | |
616 | | static UA_StatusCode |
617 | | MemoryCertStore_verifyCertificate(UA_CertificateGroup *certGroup, |
618 | 0 | const UA_ByteString *certificate) { |
619 | | /* Check parameter */ |
620 | 0 | if(certGroup == NULL || certificate == NULL) { |
621 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
622 | 0 | } |
623 | | |
624 | 0 | UA_StatusCode retval = verifyCertificate(certGroup, certificate); |
625 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
626 | 0 | if(MemoryCertStore_addToRejectedList(certGroup, certificate) != UA_STATUSCODE_GOOD) { |
627 | 0 | UA_LOG_WARNING(certGroup->logging, UA_LOGCATEGORY_SECURITYPOLICY, |
628 | 0 | "Could not append certificate to rejected list"); |
629 | 0 | } |
630 | 0 | } |
631 | 0 | return retval; |
632 | 0 | } |
633 | | |
634 | | UA_StatusCode |
635 | | UA_CertificateGroup_Memorystore(UA_CertificateGroup *certGroup, |
636 | | UA_NodeId *certificateGroupId, |
637 | | const UA_TrustListDataType *trustList, |
638 | | const UA_Logger *logger, |
639 | 0 | const UA_KeyValueMap *params) { |
640 | |
|
641 | 0 | if(certGroup == NULL || certificateGroupId == NULL) { |
642 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
643 | 0 | } |
644 | | |
645 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
646 | | |
647 | | /* Clear if the plugin is already initialized */ |
648 | 0 | if(certGroup->clear) |
649 | 0 | certGroup->clear(certGroup); |
650 | |
|
651 | 0 | UA_NodeId_copy(certificateGroupId, &certGroup->certificateGroupId); |
652 | 0 | certGroup->logging = logger; |
653 | |
|
654 | 0 | certGroup->getTrustList = MemoryCertStore_getTrustList; |
655 | 0 | certGroup->setTrustList = MemoryCertStore_setTrustList; |
656 | 0 | certGroup->addToTrustList = MemoryCertStore_addToTrustList; |
657 | 0 | certGroup->removeFromTrustList = MemoryCertStore_removeFromTrustList; |
658 | 0 | certGroup->getRejectedList = MemoryCertStore_getRejectedList; |
659 | 0 | certGroup->getCertificateCrls = MemoryCertStore_getCertificateCrls; |
660 | 0 | certGroup->verifyCertificate = MemoryCertStore_verifyCertificate; |
661 | 0 | certGroup->clear = MemoryCertStore_clear; |
662 | | |
663 | | /* Set PKI Store context data */ |
664 | 0 | MemoryCertStore *context = (MemoryCertStore *)UA_calloc(1, sizeof(MemoryCertStore)); |
665 | 0 | if(!context) { |
666 | 0 | retval = UA_STATUSCODE_BADOUTOFMEMORY; |
667 | 0 | goto cleanup; |
668 | 0 | } |
669 | 0 | certGroup->context = context; |
670 | | /* Default values */ |
671 | 0 | context->maxTrustListSize = 65535; |
672 | 0 | context->maxRejectedListSize = 100; |
673 | |
|
674 | 0 | if(params) { |
675 | 0 | const UA_UInt32 *maxTrustListSize = (const UA_UInt32*) |
676 | 0 | UA_KeyValueMap_getScalar(params, MemoryCertStoreParameters[MEMORYCERTSTORE_PARAMINDEX_MAXTRUSTLISTSIZE].name, |
677 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
678 | |
|
679 | 0 | const UA_UInt32 *maxRejectedListSize = (const UA_UInt32*) |
680 | 0 | UA_KeyValueMap_getScalar(params, MemoryCertStoreParameters[MEMORYCERTSTORE_PARAMINDEX_MAXREJECTEDLISTSIZE].name, |
681 | 0 | &UA_TYPES[UA_TYPES_UINT32]); |
682 | |
|
683 | 0 | if(maxTrustListSize) { |
684 | 0 | context->maxTrustListSize = *maxTrustListSize; |
685 | 0 | } |
686 | |
|
687 | 0 | if(maxRejectedListSize) { |
688 | 0 | context->maxRejectedListSize = *maxRejectedListSize; |
689 | 0 | } |
690 | 0 | } |
691 | |
|
692 | 0 | UA_TrustListDataType_add(trustList, &context->trustList); |
693 | 0 | reloadCertificates(certGroup); |
694 | |
|
695 | 0 | return UA_STATUSCODE_GOOD; |
696 | | |
697 | 0 | cleanup: |
698 | 0 | certGroup->clear(certGroup); |
699 | 0 | return retval; |
700 | 0 | } |
701 | | |
702 | | #if !defined(mbedtls_x509_subject_alternative_name) |
703 | | |
704 | | /* Find binary substring. Taken and adjusted from |
705 | | * http://tungchingkai.blogspot.com/2011/07/binary-strstr.html */ |
706 | | |
707 | | static const unsigned char * |
708 | 0 | bstrchr(const unsigned char *s, const unsigned char ch, size_t l) { |
709 | | /* find first occurrence of c in char s[] for length l*/ |
710 | 0 | for(; l > 0; ++s, --l) { |
711 | 0 | if(*s == ch) |
712 | 0 | return s; |
713 | 0 | } |
714 | 0 | return NULL; |
715 | 0 | } |
716 | | |
717 | | static const unsigned char * |
718 | 0 | UA_Bstrstr(const unsigned char *s1, size_t l1, const unsigned char *s2, size_t l2) { |
719 | | /* find first occurrence of s2[] in s1[] for length l1*/ |
720 | 0 | const unsigned char *ss1 = s1; |
721 | 0 | const unsigned char *ss2 = s2; |
722 | | /* handle special case */ |
723 | 0 | if(l1 == 0) |
724 | 0 | return (NULL); |
725 | 0 | if(l2 == 0) |
726 | 0 | return s1; |
727 | | |
728 | | /* match prefix */ |
729 | 0 | for (; (s1 = bstrchr(s1, *s2, (uintptr_t)ss1-(uintptr_t)s1+(uintptr_t)l1)) != NULL && |
730 | 0 | (uintptr_t)ss1-(uintptr_t)s1+(uintptr_t)l1 != 0; ++s1) { |
731 | | |
732 | | /* match rest of prefix */ |
733 | 0 | const unsigned char *sc1, *sc2; |
734 | 0 | for (sc1 = s1, sc2 = s2; ;) |
735 | 0 | if (++sc2 >= ss2+l2) |
736 | 0 | return s1; |
737 | 0 | else if (*++sc1 != *sc2) |
738 | 0 | break; |
739 | 0 | } |
740 | 0 | return NULL; |
741 | 0 | } |
742 | | |
743 | | #endif |
744 | | |
745 | | UA_StatusCode |
746 | | UA_CertificateUtils_verifyApplicationURI(UA_RuleHandling ruleHandling, |
747 | | const UA_ByteString *certificate, |
748 | | const UA_String *applicationURI, |
749 | 0 | UA_Logger *logger) { |
750 | | /* Parse the certificate */ |
751 | 0 | mbedtls_x509_crt remoteCertificate; |
752 | 0 | mbedtls_x509_crt_init(&remoteCertificate); |
753 | |
|
754 | 0 | UA_StatusCode retval = UA_mbedTLS_LoadCertificate(certificate, &remoteCertificate); |
755 | 0 | if(retval != UA_STATUSCODE_GOOD) |
756 | 0 | return retval; |
757 | | |
758 | | #if defined(mbedtls_x509_subject_alternative_name) |
759 | | /* Get the Subject Alternative Name and compate */ |
760 | | mbedtls_x509_subject_alternative_name san; |
761 | | mbedtls_x509_sequence *cur = &remoteCertificate.subject_alt_names; |
762 | | retval = UA_STATUSCODE_BADCERTIFICATEURIINVALID; |
763 | | for(; cur; cur = cur->next) { |
764 | | int res = mbedtls_x509_parse_subject_alt_name(&cur->buf, &san); |
765 | | if(res != 0) |
766 | | continue; |
767 | | if(san.type != MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) { |
768 | | mbedtls_x509_free_subject_alt_name(&san); |
769 | | continue; |
770 | | } |
771 | | |
772 | | UA_String uri = {san.san.unstructured_name.len, san.san.unstructured_name.p}; |
773 | | UA_Boolean found = UA_String_equal(&uri, applicationURI); |
774 | | if(found) { |
775 | | retval = UA_STATUSCODE_GOOD; |
776 | | } else if(ruleHandling != UA_RULEHANDLING_ACCEPT) { |
777 | | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURITYPOLICY, |
778 | | "The certificate's Subject Alternative Name URI (%S) " |
779 | | "does not match the ApplicationURI (%S)", |
780 | | uri, *applicationURI); |
781 | | } |
782 | | mbedtls_x509_free_subject_alt_name(&san); |
783 | | break; |
784 | | } |
785 | | |
786 | | if(!cur && ruleHandling != UA_RULEHANDLING_ACCEPT) { |
787 | | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURITYPOLICY, |
788 | | "The certificate has no Subject Alternative Name URI defined"); |
789 | | } |
790 | | #else |
791 | | /* Poor man's ApplicationUri verification. mbedTLS does not parse all fields |
792 | | * of the Alternative Subject Name. Instead test whether the URI-string is |
793 | | * present in the v3_ext field in general. */ |
794 | 0 | if(UA_Bstrstr(remoteCertificate.v3_ext.p, remoteCertificate.v3_ext.len, |
795 | 0 | applicationURI->data, applicationURI->length) == NULL) |
796 | 0 | retval = UA_STATUSCODE_BADCERTIFICATEURIINVALID; |
797 | |
|
798 | 0 | if(retval != UA_STATUSCODE_GOOD && ruleHandling != UA_RULEHANDLING_ACCEPT) { |
799 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURITYPOLICY, |
800 | 0 | "The certificate's application URI could not be verified. StatusCode %s", |
801 | 0 | UA_StatusCode_name(retval)); |
802 | 0 | } |
803 | 0 | #endif |
804 | |
|
805 | 0 | if(ruleHandling != UA_RULEHANDLING_ABORT) |
806 | 0 | retval = UA_STATUSCODE_GOOD; |
807 | |
|
808 | 0 | mbedtls_x509_crt_free(&remoteCertificate); |
809 | 0 | return retval; |
810 | 0 | } |
811 | | |
812 | | UA_StatusCode |
813 | | UA_CertificateUtils_getExpirationDate(UA_ByteString *certificate, |
814 | 0 | UA_DateTime *expiryDateTime) { |
815 | 0 | mbedtls_x509_crt publicKey; |
816 | 0 | mbedtls_x509_crt_init(&publicKey); |
817 | |
|
818 | 0 | UA_StatusCode retval = UA_mbedTLS_LoadCertificate(certificate, &publicKey); |
819 | 0 | if(retval != UA_STATUSCODE_GOOD) |
820 | 0 | return retval; |
821 | | |
822 | 0 | UA_DateTimeStruct ts; |
823 | 0 | ts.year = (UA_Int16)publicKey.valid_to.year; |
824 | 0 | ts.month = (UA_UInt16)publicKey.valid_to.mon; |
825 | 0 | ts.day = (UA_UInt16)publicKey.valid_to.day; |
826 | 0 | ts.hour = (UA_UInt16)publicKey.valid_to.hour; |
827 | 0 | ts.min = (UA_UInt16)publicKey.valid_to.min; |
828 | 0 | ts.sec = (UA_UInt16)publicKey.valid_to.sec; |
829 | 0 | ts.milliSec = 0; |
830 | 0 | ts.microSec = 0; |
831 | 0 | ts.nanoSec = 0; |
832 | 0 | *expiryDateTime = UA_DateTime_fromStruct(ts); |
833 | 0 | mbedtls_x509_crt_free(&publicKey); |
834 | 0 | return UA_STATUSCODE_GOOD; |
835 | 0 | } |
836 | | |
837 | | UA_StatusCode |
838 | | UA_CertificateUtils_getSubjectName(UA_ByteString *certificate, |
839 | 0 | UA_String *subjectName) { |
840 | 0 | mbedtls_x509_crt publicKey; |
841 | 0 | mbedtls_x509_crt_init(&publicKey); |
842 | |
|
843 | 0 | mbedtls_x509_crl crl; |
844 | 0 | mbedtls_x509_crl_init(&crl); |
845 | |
|
846 | 0 | char buf[1024]; |
847 | 0 | int res = 0; |
848 | 0 | UA_StatusCode retval = UA_mbedTLS_LoadCertificate(certificate, &publicKey); |
849 | 0 | if(retval == UA_STATUSCODE_GOOD) { |
850 | 0 | res = mbedtls_x509_dn_gets(buf, 1024, &publicKey.subject); |
851 | 0 | mbedtls_x509_crt_free(&publicKey); |
852 | 0 | } else { |
853 | 0 | retval = UA_mbedTLS_LoadCrl(certificate, &crl); |
854 | 0 | if(retval != UA_STATUSCODE_GOOD) |
855 | 0 | return retval; |
856 | 0 | res = mbedtls_x509_dn_gets(buf, 1024, &crl.issuer); |
857 | 0 | mbedtls_x509_crl_free(&crl); |
858 | 0 | } |
859 | | |
860 | 0 | if(res < 0) |
861 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
862 | 0 | UA_String tmp = {(size_t)res, (UA_Byte*)buf}; |
863 | 0 | return UA_String_copy(&tmp, subjectName); |
864 | 0 | } |
865 | | |
866 | | UA_StatusCode |
867 | | UA_CertificateUtils_getThumbprint(UA_ByteString *certificate, |
868 | 0 | UA_String *thumbprint){ |
869 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
870 | 0 | if(certificate == NULL || thumbprint->length != (UA_SHA1_LENGTH * 2)) |
871 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
872 | | |
873 | | // prepare temporary to hold the binary thumbprint |
874 | 0 | UA_Byte buf[UA_SHA1_LENGTH]; |
875 | 0 | UA_ByteString thumbpr = { |
876 | 0 | /*.length =*/ sizeof(buf), |
877 | 0 | /*.data =*/ buf |
878 | 0 | }; |
879 | |
|
880 | 0 | retval = mbedtls_thumbprint_sha1(certificate, &thumbpr); |
881 | | |
882 | | // convert to hexadecimal string representation |
883 | 0 | size_t t = 0u; |
884 | 0 | for (size_t i = 0u; i < thumbpr.length; i++) { |
885 | 0 | UA_Byte shift = 4u; |
886 | | // byte consists of two nibbles: AAAABBBB |
887 | 0 | const UA_Byte curByte = thumbpr.data[i]; |
888 | | // convert AAAA first then BBBB |
889 | 0 | for(size_t n = 0u; n < 2u; n++) { |
890 | 0 | UA_Byte curNibble = (curByte >> shift) & 0x0Fu; |
891 | 0 | if(curNibble >= 10u) |
892 | 0 | thumbprint->data[t++] = (65u + (curNibble - 10u)); // 65 == 'A' |
893 | 0 | else |
894 | 0 | thumbprint->data[t++] = (48u + curNibble); // 48 == '0' |
895 | 0 | shift -= 4u; |
896 | 0 | } |
897 | 0 | } |
898 | |
|
899 | 0 | return retval; |
900 | 0 | } |
901 | | |
902 | | UA_StatusCode |
903 | | UA_CertificateUtils_getKeySize(UA_ByteString *certificate, |
904 | 0 | size_t *keySize){ |
905 | 0 | mbedtls_x509_crt publicKey; |
906 | 0 | mbedtls_x509_crt_init(&publicKey); |
907 | |
|
908 | 0 | UA_StatusCode retval = UA_mbedTLS_LoadCertificate(certificate, &publicKey); |
909 | 0 | if(retval != UA_STATUSCODE_GOOD) |
910 | 0 | return retval; |
911 | | |
912 | 0 | mbedtls_rsa_context *rsa = mbedtls_pk_rsa(publicKey.pk); |
913 | |
|
914 | 0 | #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 |
915 | 0 | *keySize = rsa->len * 8; |
916 | | #else |
917 | | *keySize = mbedtls_rsa_get_len(rsa) * 8; |
918 | | #endif |
919 | 0 | mbedtls_x509_crt_free(&publicKey); |
920 | |
|
921 | 0 | return UA_STATUSCODE_GOOD; |
922 | 0 | } |
923 | | |
924 | | UA_StatusCode |
925 | | UA_CertificateUtils_comparePublicKeys(const UA_ByteString *certificate1, |
926 | 0 | const UA_ByteString *certificate2) { |
927 | 0 | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
928 | |
|
929 | 0 | mbedtls_x509_crt cert1; |
930 | 0 | mbedtls_x509_crt cert2; |
931 | 0 | mbedtls_x509_csr csr1; |
932 | 0 | mbedtls_x509_csr csr2; |
933 | 0 | mbedtls_mpi N1, E1; |
934 | 0 | mbedtls_mpi N2, E2; |
935 | |
|
936 | 0 | UA_ByteString data1 = UA_mbedTLS_CopyDataFormatAware(certificate1); |
937 | 0 | UA_ByteString data2 = UA_mbedTLS_CopyDataFormatAware(certificate2); |
938 | |
|
939 | 0 | mbedtls_x509_crt_init(&cert1); |
940 | 0 | mbedtls_x509_crt_init(&cert2); |
941 | 0 | mbedtls_x509_csr_init(&csr1); |
942 | 0 | mbedtls_x509_csr_init(&csr2); |
943 | 0 | mbedtls_mpi_init(&N1); |
944 | 0 | mbedtls_mpi_init(&E1); |
945 | 0 | mbedtls_mpi_init(&N2); |
946 | 0 | mbedtls_mpi_init(&E2); |
947 | |
|
948 | 0 | int mbedErr = mbedtls_x509_crt_parse(&cert1, data1.data, data1.length); |
949 | 0 | if(mbedErr) { |
950 | | /* Try to load as a csr */ |
951 | 0 | mbedErr = mbedtls_x509_csr_parse(&csr1, data1.data, data1.length); |
952 | 0 | if(mbedErr) { |
953 | 0 | retval = UA_STATUSCODE_BADINTERNALERROR; |
954 | 0 | goto cleanup; |
955 | 0 | } |
956 | 0 | } |
957 | | |
958 | 0 | mbedErr = mbedtls_x509_crt_parse(&cert2, data2.data, data2.length); |
959 | 0 | if(mbedErr) { |
960 | | /* Try to load as a csr */ |
961 | 0 | mbedErr = mbedtls_x509_csr_parse(&csr2, data2.data, data2.length); |
962 | 0 | if(mbedErr) { |
963 | 0 | retval = UA_STATUSCODE_BADINTERNALERROR; |
964 | 0 | goto cleanup; |
965 | 0 | } |
966 | 0 | } |
967 | | |
968 | 0 | #if MBEDTLS_VERSION_NUMBER < 0x03000000 |
969 | 0 | mbedtls_pk_context pk1 = cert1.pk.pk_info ? cert1.pk : csr1.pk; |
970 | 0 | mbedtls_pk_context pk2 = cert2.pk.pk_info ? cert2.pk : csr2.pk; |
971 | | #else |
972 | | mbedtls_pk_context pk1 = cert1.pk_raw.p ? cert1.pk : csr1.pk; |
973 | | mbedtls_pk_context pk2 = cert2.pk_raw.p ? cert2.pk : csr2.pk; |
974 | | #endif |
975 | |
|
976 | 0 | if(!mbedtls_pk_rsa(pk1) || !mbedtls_pk_rsa(pk2)) { |
977 | 0 | retval = UA_STATUSCODE_BADINTERNALERROR; |
978 | 0 | goto cleanup; |
979 | 0 | } |
980 | | |
981 | 0 | if(!mbedtls_pk_can_do(&pk1, MBEDTLS_PK_RSA) && |
982 | 0 | !mbedtls_pk_can_do(&pk2, MBEDTLS_PK_RSA)) { |
983 | 0 | retval = UA_STATUSCODE_BADINTERNALERROR; |
984 | 0 | goto cleanup; |
985 | 0 | } |
986 | | |
987 | 0 | #if MBEDTLS_VERSION_NUMBER < 0x02070000 |
988 | 0 | N1 = mbedtls_pk_rsa(pk1)->N; |
989 | 0 | E1 = mbedtls_pk_rsa(pk1)->E; |
990 | 0 | N2 = mbedtls_pk_rsa(pk2)->N; |
991 | 0 | E2 = mbedtls_pk_rsa(pk2)->E; |
992 | | #else |
993 | | if(mbedtls_rsa_export(mbedtls_pk_rsa(pk1), &N1, NULL, NULL, NULL, &E1) != 0) { |
994 | | retval = UA_STATUSCODE_BADINTERNALERROR; |
995 | | goto cleanup; |
996 | | } |
997 | | if(mbedtls_rsa_export(mbedtls_pk_rsa(pk2), &N2, NULL, NULL, NULL, &E2) != 0) { |
998 | | retval = UA_STATUSCODE_BADINTERNALERROR; |
999 | | goto cleanup; |
1000 | | } |
1001 | | #endif |
1002 | |
|
1003 | 0 | if(mbedtls_mpi_cmp_mpi(&N1, &N2) || mbedtls_mpi_cmp_mpi(&E1, &E2)) |
1004 | 0 | retval = UA_STATUSCODE_BADNOMATCH; |
1005 | |
|
1006 | 0 | cleanup: |
1007 | 0 | mbedtls_mpi_free(&N1); |
1008 | 0 | mbedtls_mpi_free(&E1); |
1009 | 0 | mbedtls_mpi_free(&N2); |
1010 | 0 | mbedtls_mpi_free(&E2); |
1011 | 0 | mbedtls_x509_crt_free(&cert1); |
1012 | 0 | mbedtls_x509_crt_free(&cert2); |
1013 | 0 | mbedtls_x509_csr_free(&csr1); |
1014 | 0 | mbedtls_x509_csr_free(&csr2); |
1015 | 0 | UA_ByteString_clear(&data1); |
1016 | 0 | UA_ByteString_clear(&data2); |
1017 | |
|
1018 | 0 | return retval; |
1019 | 0 | } |
1020 | | |
1021 | | UA_StatusCode |
1022 | | UA_CertificateUtils_checkKeyPair(const UA_ByteString *certificate, |
1023 | 0 | const UA_ByteString *privateKey) { |
1024 | 0 | mbedtls_x509_crt cert; |
1025 | 0 | mbedtls_pk_context pk; |
1026 | |
|
1027 | 0 | mbedtls_x509_crt_init(&cert); |
1028 | 0 | mbedtls_pk_init(&pk); |
1029 | |
|
1030 | 0 | UA_StatusCode retval = UA_mbedTLS_LoadCertificate(certificate, &cert); |
1031 | 0 | if(retval != UA_STATUSCODE_GOOD) |
1032 | 0 | goto cleanup; |
1033 | | |
1034 | 0 | retval = UA_mbedTLS_LoadPrivateKey(privateKey, &pk, NULL); |
1035 | 0 | if(retval != UA_STATUSCODE_GOOD) |
1036 | 0 | goto cleanup; |
1037 | | |
1038 | | /* Verify the private key matches the public key in the certificate */ |
1039 | 0 | if(!mbedtls_pk_can_do(&pk, mbedtls_pk_get_type(&cert.pk))) { |
1040 | 0 | retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
1041 | 0 | goto cleanup; |
1042 | 0 | } |
1043 | | |
1044 | | /* Check if the public key from the certificate matches the private key */ |
1045 | 0 | #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 |
1046 | 0 | if(mbedtls_pk_check_pair(&cert.pk, &pk) != 0) { |
1047 | 0 | retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
1048 | 0 | } |
1049 | | #else |
1050 | | if(mbedtls_pk_check_pair(&cert.pk, &pk, mbedtls_entropy_func, NULL) != 0) { |
1051 | | retval = UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
1052 | | } |
1053 | | #endif |
1054 | |
|
1055 | 0 | cleanup: |
1056 | 0 | mbedtls_pk_free(&pk); |
1057 | 0 | mbedtls_x509_crt_free(&cert); |
1058 | |
|
1059 | 0 | return retval; |
1060 | 0 | } |
1061 | | |
1062 | | UA_StatusCode |
1063 | 0 | UA_CertificateUtils_checkCA(const UA_ByteString *certificate) { |
1064 | 0 | mbedtls_x509_crt cert; |
1065 | 0 | mbedtls_x509_crt_init(&cert); |
1066 | |
|
1067 | 0 | UA_StatusCode retval = UA_mbedTLS_LoadCertificate(certificate, &cert); |
1068 | 0 | if(retval != UA_STATUSCODE_GOOD) |
1069 | 0 | goto cleanup; |
1070 | | |
1071 | 0 | retval = mbedtlsCheckCA(&cert) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADNOMATCH; |
1072 | |
|
1073 | 0 | cleanup: |
1074 | 0 | mbedtls_x509_crt_free(&cert); |
1075 | |
|
1076 | 0 | return retval; |
1077 | 0 | } |
1078 | | |
1079 | | UA_StatusCode |
1080 | | UA_CertificateUtils_decryptPrivateKey(const UA_ByteString privateKey, |
1081 | | const UA_ByteString password, |
1082 | 0 | UA_ByteString *outDerKey) { |
1083 | 0 | if(!outDerKey) |
1084 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1085 | | |
1086 | 0 | if (privateKey.length == 0) { |
1087 | 0 | *outDerKey = UA_BYTESTRING_NULL; |
1088 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
1089 | 0 | } |
1090 | | |
1091 | | /* Already in DER format -> return verbatim */ |
1092 | 0 | if(privateKey.length > 1 && privateKey.data[0] == 0x30 && privateKey.data[1] == 0x82) |
1093 | 0 | return UA_ByteString_copy(&privateKey, outDerKey); |
1094 | | |
1095 | | /* Create a null-terminated string */ |
1096 | 0 | UA_ByteString nullTerminatedKey = UA_mbedTLS_CopyDataFormatAware(&privateKey); |
1097 | 0 | if(nullTerminatedKey.length != privateKey.length + 1) |
1098 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
1099 | | |
1100 | | /* Create the private-key context */ |
1101 | 0 | mbedtls_pk_context ctx; |
1102 | 0 | mbedtls_pk_init(&ctx); |
1103 | 0 | #if MBEDTLS_VERSION_NUMBER >= 0x02060000 && MBEDTLS_VERSION_NUMBER < 0x03000000 |
1104 | 0 | int err = mbedtls_pk_parse_key(&ctx, nullTerminatedKey.data, |
1105 | 0 | nullTerminatedKey.length, |
1106 | 0 | password.data, password.length); |
1107 | | #else |
1108 | | mbedtls_entropy_context entropy; |
1109 | | mbedtls_entropy_init(&entropy); |
1110 | | int err = mbedtls_pk_parse_key(&ctx, nullTerminatedKey.data, |
1111 | | nullTerminatedKey.length, |
1112 | | password.data, password.length, |
1113 | | mbedtls_entropy_func, &entropy); |
1114 | | mbedtls_entropy_free(&entropy); |
1115 | | #endif |
1116 | 0 | UA_ByteString_clear(&nullTerminatedKey); |
1117 | 0 | if(err != 0) { |
1118 | 0 | mbedtls_pk_free(&ctx); |
1119 | 0 | return UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
1120 | 0 | } |
1121 | | |
1122 | | /* Write the DER-encoded key into a local buffer */ |
1123 | 0 | unsigned char buf[1 << 14]; |
1124 | 0 | size_t pos = (size_t)mbedtls_pk_write_key_der(&ctx, buf, sizeof(buf)); |
1125 | | |
1126 | | /* Allocate memory */ |
1127 | 0 | UA_StatusCode res = UA_ByteString_allocBuffer(outDerKey, pos); |
1128 | 0 | if(res != UA_STATUSCODE_GOOD) { |
1129 | 0 | mbedtls_pk_free(&ctx); |
1130 | 0 | return res; |
1131 | 0 | } |
1132 | | |
1133 | | /* Copy to the output */ |
1134 | 0 | memcpy(outDerKey->data, &buf[sizeof(buf) - pos], pos); |
1135 | 0 | mbedtls_pk_free(&ctx); |
1136 | 0 | return UA_STATUSCODE_GOOD; |
1137 | 0 | } |
1138 | | |
1139 | | #endif |