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