diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.c_rehash.hpp.e70763dbbf7e71b0a70a7d981544c8cd.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.c_rehash.hpp.e70763dbbf7e71b0a70a7d981544c8cd.html new file mode 100644 index 00000000..2f573515 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.c_rehash.hpp.e70763dbbf7e71b0a70a7d981544c8cd.html @@ -0,0 +1,2277 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:3rd_party/cert_rehash/c_rehash.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:01730.0%
Functions:060.0%
Branches:04820.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + /* c_rehash.c - Create hash symlinks for certificates
2 + * C implementation based on the original Perl and shell versions
3 + *
4 + * Copyright (c) 2013-2014 Timo Teräs <timo.teras@iki.fi>
5 + * All rights reserved.
6 + *
7 + * This software is licensed under the MIT License.
8 + * Full license available at: http://opensource.org/licenses/MIT
9 + */
10 +
11 + #include <dirent.h>
12 + #include <limits.h>
13 + #include <stdio.h>
14 + #include <string.h>
15 + #include <sys/stat.h>
16 + #include <unistd.h>
17 +
18 + #include <openssl/evp.h>
19 + #include <openssl/pem.h>
20 + #include <openssl/x509.h>
21 +
22 + #include <everest/logging.hpp>
23 +
24 + #define MAX_COLLISIONS 256
25 + #define countof(x) (sizeof(x) / sizeof(x[0]))
26 +
27 + namespace evse_security {
28 +
29 + struct entry_info {
30 + struct entry_info* next;
31 + char* filename;
32 + unsigned short old_id;
33 + unsigned char need_symlink;
34 + unsigned char digest[EVP_MAX_MD_SIZE];
35 + };
36 +
37 + struct bucket_info {
38 + struct bucket_info* next;
39 + struct entry_info *first_entry, *last_entry;
40 + unsigned int hash;
41 + unsigned short type;
42 + unsigned short num_needed;
43 + };
44 +
45 + enum Type {
46 + TYPE_CERT = 0,
47 + TYPE_CRL
48 + };
49 +
50 + static const char* symlink_extensions[] = {"", "r"};
51 + static const char* file_extensions[] = {"pem", "crt", "cer", "crl"};
52 +
53 + static int evpmdsize;
54 + static const EVP_MD* evpmd;
55 +
56 + static struct bucket_info* hash_table[257];
57 +
58 + static void bit_set(unsigned char* set, unsigned bit) {
59 + set[bit / 8] |= 1 << (bit % 8);
60 + }
61 +
62 + static int bit_isset(unsigned char* set, unsigned bit) {
63 + return set[bit / 8] & (1 << (bit % 8));
64 + }
65 +
66 + static void add_entry(int type, unsigned int hash, const char* filename, const unsigned char* digest, int need_symlink,
67 + unsigned short old_id) {
68 + struct bucket_info* bi;
69 + struct entry_info *ei, *found = NULL;
70 + unsigned int ndx = (type + hash) % countof(hash_table);
71 +
72 + for (bi = hash_table[ndx]; bi; bi = bi->next)
73 + if (bi->type == type && bi->hash == hash)
74 + break;
75 + if (!bi) {
76 + bi = (bucket_info*)(calloc(1, sizeof(*bi)));
77 + if (!bi)
78 + return;
79 + bi->next = hash_table[ndx];
80 + bi->type = type;
81 + bi->hash = hash;
82 + hash_table[ndx] = bi;
83 + }
84 +
85 + for (ei = bi->first_entry; ei; ei = ei->next) {
86 + if (digest && memcmp(digest, ei->digest, evpmdsize) == 0) {
87 + EVLOG_warning << "Skipping duplicate certificate in file " << std::string(filename);
88 + return;
89 + }
90 + if (!strcmp(filename, ei->filename)) {
91 + found = ei;
92 + if (!digest)
93 + break;
94 + }
95 + }
96 + ei = found;
97 + if (!ei) {
98 + if (bi->num_needed >= MAX_COLLISIONS)
99 + return;
100 + ei = (entry_info*)(calloc(1, sizeof(*ei)));
101 + if (!ei)
102 + return;
103 +
104 + ei->old_id = ~0;
105 + ei->filename = strdup(filename);
106 + if (bi->last_entry)
107 + bi->last_entry->next = ei;
108 + if (!bi->first_entry)
109 + bi->first_entry = ei;
110 + bi->last_entry = ei;
111 + }
112 +
113 + if (old_id < ei->old_id)
114 + ei->old_id = old_id;
115 + if (need_symlink && !ei->need_symlink) {
116 + ei->need_symlink = 1;
117 + bi->num_needed++;
118 + memcpy(ei->digest, digest, evpmdsize);
119 + }
120 + }
121 +
122 + static int handle_symlink(const char* filename, const char* fullpath) {
123 + static signed char xdigit[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11,
124 + 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15};
126 + char linktarget[NAME_MAX], *endptr;
127 + unsigned int hash = 0;
128 + unsigned char ch;
129 + int i, type, id;
130 + ssize_t n;
131 +
132 + for (i = 0; i < 8; i++) {
133 + ch = filename[i] - '0';
134 + if (ch >= countof(xdigit) || xdigit[ch] < 0)
135 + return -1;
136 + hash <<= 4;
137 + hash += xdigit[ch];
138 + }
139 + if (filename[i++] != '.')
140 + return -1;
141 + for (type = countof(symlink_extensions) - 1; type > 0; type--)
142 + if (strcasecmp(symlink_extensions[type], &filename[i]) == 0)
143 + break;
144 + i += strlen(symlink_extensions[type]);
145 +
146 + id = strtoul(&filename[i], &endptr, 10);
147 + if (*endptr != 0)
148 + return -1;
149 +
150 + n = readlink(fullpath, linktarget, sizeof(linktarget));
151 + if (n >= sizeof(linktarget) || n < 0)
152 + return -1;
153 + linktarget[n] = 0;
154 +
155 + EVLOG_debug << "Found existing symlink " << std::string(filename) << " for " << hash << " (" << type
156 + << "), certname " << std::string(linktarget, strlen(linktarget));
157 + add_entry(type, hash, linktarget, NULL, 0, id);
158 + return 0;
159 + }
160 +
161 + static int handle_certificate(const char* filename, const char* fullpath) {
162 + STACK_OF(X509_INFO) * inf;
163 + X509_INFO* x;
164 + BIO* b;
165 + const char* ext;
166 + unsigned char digest[EVP_MAX_MD_SIZE];
167 + X509_NAME* name = NULL;
168 + int i, type, ret = -1;
169 +
170 + ext = strrchr(filename, '.');
171 + if (ext == NULL)
172 + return 0;
173 + for (i = 0; i < countof(file_extensions); i++) {
174 + if (strcasecmp(file_extensions[i], ext + 1) == 0)
175 + break;
176 + }
177 + if (i >= countof(file_extensions))
178 + return -1;
179 +
180 + b = BIO_new_file(fullpath, "r");
181 + if (!b)
182 + return -1;
183 + inf = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL);
184 + BIO_free(b);
185 + if (!inf)
186 + return -1;
187 +
188 + if (sk_X509_INFO_num(inf) == 1) {
189 + x = sk_X509_INFO_value(inf, 0);
190 + if (x->x509) {
191 + type = TYPE_CERT;
192 + name = X509_get_subject_name(x->x509);
193 + X509_digest(x->x509, evpmd, digest, NULL);
194 + } else if (x->crl) {
195 + type = TYPE_CRL;
196 + name = X509_CRL_get_issuer(x->crl);
197 + X509_CRL_digest(x->crl, evpmd, digest, NULL);
198 + }
199 + if (name)
200 + add_entry(type, X509_NAME_hash(name), filename, digest, 1, ~0);
201 + } else {
202 + EVLOG_warning << std::string(filename) << " does not contain exactly one certificate or CRL: skipping";
203 + }
204 +
205 + sk_X509_INFO_pop_free(inf, X509_INFO_free);
206 +
207 + return ret;
208 + }
209 +
210 + static int hash_dir(const char* dirname) {
211 + struct bucket_info *bi, *nextbi;
212 + struct entry_info *ei, *nextei;
213 + struct dirent* de;
214 + struct stat st;
215 + unsigned char idmask[MAX_COLLISIONS / 8];
216 + int i, n, nextid, buflen, ret = -1;
217 + const char* pathsep;
218 + char* buf;
219 + DIR* d;
220 +
221 + evpmd = EVP_sha1();
222 + evpmdsize = EVP_MD_size(evpmd);
223 +
224 + if (access(dirname, R_OK | W_OK | X_OK) != 0) {
225 + EVLOG_error << "Access denied '" << std::string(dirname) << "'";
226 + return -1;
227 + }
228 +
229 + buflen = strlen(dirname);
230 + pathsep = (buflen && dirname[buflen - 1] == '/') ? "" : "/";
231 + buflen += NAME_MAX + 2;
232 + buf = (char*)(malloc(buflen));
233 + if (buf == NULL)
234 + goto err;
235 +
236 + EVLOG_debug << "Doing " << std::string(dirname);
237 + d = opendir(dirname);
238 + if (!d)
239 + goto err;
240 +
241 + while ((de = readdir(d)) != NULL) {
242 + if (snprintf(buf, buflen, "%s%s%s", dirname, pathsep, de->d_name) >= buflen)
243 + continue;
244 + if (lstat(buf, &st) < 0)
245 + continue;
246 + if (S_ISLNK(st.st_mode) && handle_symlink(de->d_name, buf) == 0)
247 + continue;
248 + if (strcmp(buf, "/etc/ssl/certs/ca-certificates.crt") == 0) {
249 + /* Ignore the /etc/ssl/certs/ca-certificates.crt file */
250 + EVLOG_debug << "Skipping /etc/ssl/certs/ca-certificates.crt file";
251 + continue;
252 + }
253 + handle_certificate(de->d_name, buf);
254 + }
255 + closedir(d);
256 +
257 + for (i = 0; i < countof(hash_table); i++) {
258 + for (bi = hash_table[i]; bi; bi = nextbi) {
259 + nextbi = bi->next;
260 + EVLOG_debug << "Type " << bi->type << " hash " << bi->hash << " num entries " << bi->num_needed << ":";
261 +
262 + nextid = 0;
263 + memset(idmask, 0, (bi->num_needed + 7) / 8);
264 + for (ei = bi->first_entry; ei; ei = ei->next)
265 + if (ei->old_id < bi->num_needed)
266 + bit_set(idmask, ei->old_id);
267 +
268 + for (ei = bi->first_entry; ei; ei = nextei) {
269 + nextei = ei->next;
270 + EVLOG_debug << "\t(old_id " << ei->old_id << ", need_symlink " << static_cast<int>(ei->need_symlink)
271 + << ") Cert " << std::string(ei->filename, strlen(ei->filename)) << ":";
272 +
273 + if (ei->old_id < bi->num_needed) {
274 + /* Link exists, and is used as-is */
275 + snprintf(buf, buflen, "%08x.%s%d", bi->hash, symlink_extensions[bi->type], ei->old_id);
276 + EVLOG_debug << "link " << std::string(ei->filename, strlen(ei->filename)) << " -> "
277 + << std::string(buf, strlen(buf));
278 + } else if (ei->need_symlink) {
279 + /* New link needed (it may replace something) */
280 + while (bit_isset(idmask, nextid))
281 + nextid++;
282 +
283 + snprintf(buf, buflen, "%s%s%n%08x.%s%d", dirname, pathsep, &n, bi->hash,
284 + symlink_extensions[bi->type], nextid);
285 + EVLOG_debug << "link " << std::string(ei->filename, strlen(ei->filename)) << " -> "
286 + << std::string(buf + n, strlen(buf + n));
287 + unlink(buf);
288 + symlink(ei->filename, buf);
289 + } else {
290 + /* Link to be deleted */
291 + snprintf(buf, buflen, "%s%s%n%08x.%s%d", dirname, pathsep, &n, bi->hash,
292 + symlink_extensions[bi->type], ei->old_id);
293 + EVLOG_debug << "unlink " << std::string(buf + n, strlen(buf + n));
294 + unlink(buf);
295 + }
296 + free(ei->filename);
297 + free(ei);
298 + }
299 + free(bi);
300 + }
301 + hash_table[i] = NULL;
302 + }
303 +
304 + ret = 0;
305 + err:
306 + free(buf);
307 + return ret;
308 + }
309 +
310 + } // namespace evse_security
311 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_supplier.cpp.55b7913e4c156b53e2650b9bde6926b3.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_supplier.cpp.55b7913e4c156b53e2650b9bde6926b3.html new file mode 100644 index 00000000..3ce63c4d --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_supplier.cpp.55b7913e4c156b53e2650b9bde6926b3.html @@ -0,0 +1,973 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/crypto/interface/crypto_supplier.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:0580.0%
Functions:0250.0%
Branches:06400.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #include <everest/logging.hpp>
4 + #include <evse_security/crypto/interface/crypto_supplier.hpp>
5 +
6 + #define default_crypto_supplier_usage_error() \
7 + EVLOG_error << "Invoked default unimplemented crypto function: [" << __func__ << "]";
8 +
9 + namespace evse_security {
10 +
11 + const char* AbstractCryptoSupplier::get_supplier_name() {
12 + return "AbstractCryptoSupplier";
13 + }
14 +
15 + bool AbstractCryptoSupplier::supports_tpm() {
16 + default_crypto_supplier_usage_error() return false;
17 + }
18 +
19 + bool AbstractCryptoSupplier::supports_tpm_key_creation() {
20 + default_crypto_supplier_usage_error() return false;
21 + }
22 +
23 + bool AbstractCryptoSupplier::generate_key(const KeyGenerationInfo& key_info, KeyHandle_ptr& out_key) {
24 + default_crypto_supplier_usage_error() return false;
25 + }
26 +
27 + /// @brief Loads all certificates from the string data that can contain multiple cetifs
28 + std::vector<X509Handle_ptr> AbstractCryptoSupplier::load_certificates(const std::string& data,
29 + const EncodingFormat encoding) {
30 + default_crypto_supplier_usage_error() return {};
31 + }
32 +
33 + std::string AbstractCryptoSupplier::x509_to_string(X509Handle* handle) {
34 + default_crypto_supplier_usage_error() return {};
35 + }
36 +
37 + std::string AbstractCryptoSupplier::x509_get_responder_url(X509Handle* handle) {
38 + default_crypto_supplier_usage_error() return {};
39 + }
40 +
41 + std::string AbstractCryptoSupplier::x509_get_key_hash(X509Handle* handle) {
42 + default_crypto_supplier_usage_error() return {};
43 + }
44 +
45 + std::string AbstractCryptoSupplier::x509_get_serial_number(X509Handle* handle) {
46 + default_crypto_supplier_usage_error() return {};
47 + }
48 +
49 + std::string AbstractCryptoSupplier::x509_get_issuer_name_hash(X509Handle* handle) {
50 + default_crypto_supplier_usage_error() return {};
51 + }
52 +
53 + std::string AbstractCryptoSupplier::x509_get_common_name(X509Handle* handle) {
54 + default_crypto_supplier_usage_error() return {};
55 + }
56 +
57 + bool AbstractCryptoSupplier::x509_get_validity(X509Handle* handle, std::int64_t& out_valid_in,
58 + std::int64_t& out_valid_to) {
59 + default_crypto_supplier_usage_error() return false;
60 + }
61 +
62 + bool AbstractCryptoSupplier::x509_is_selfsigned(X509Handle* handle) {
63 + default_crypto_supplier_usage_error() return false;
64 + }
65 +
66 + bool AbstractCryptoSupplier::x509_is_child(X509Handle* child, X509Handle* parent) {
67 + default_crypto_supplier_usage_error() return false;
68 + }
69 +
70 + bool AbstractCryptoSupplier::x509_is_equal(X509Handle* a, X509Handle* b) {
71 + default_crypto_supplier_usage_error() return (a == b);
72 + }
73 +
74 + static X509Handle_ptr x509_duplicate_unique(X509Handle* handle) {
75 + default_crypto_supplier_usage_error() return {};
76 + }
77 +
78 + CertificateValidationResult AbstractCryptoSupplier::x509_verify_certificate_chain(
79 + X509Handle* target, const std::vector<X509Handle*>& parents, const std::vector<X509Handle*>& untrusted_subcas,
80 + bool allow_future_certificates, const std::optional<fs::path> dir_path, const std::optional<fs::path> file_path) {
81 + default_crypto_supplier_usage_error() return CertificateValidationResult::Unknown;
82 + }
83 +
84 + KeyValidationResult AbstractCryptoSupplier::x509_check_private_key(X509Handle* handle, std::string private_key,
85 + std::optional<std::string> password) {
86 + default_crypto_supplier_usage_error() return KeyValidationResult::Unknown;
87 + }
88 +
89 + bool AbstractCryptoSupplier::x509_verify_signature(X509Handle* handle, const std::vector<std::uint8_t>& signature,
90 + const std::vector<std::uint8_t>& data) {
91 + default_crypto_supplier_usage_error() return false;
92 + }
93 +
94 + CertificateSignRequestResult AbstractCryptoSupplier::x509_generate_csr(const CertificateSigningRequestInfo& csr_info,
95 + std::string& out_csr) {
96 + default_crypto_supplier_usage_error() return CertificateSignRequestResult::Unknown;
97 + }
98 +
99 + bool AbstractCryptoSupplier::digest_file_sha256(const fs::path& path, std::vector<std::uint8_t>& out_digest) {
100 + default_crypto_supplier_usage_error() return false;
101 + }
102 +
103 + bool AbstractCryptoSupplier::base64_decode_to_bytes(const std::string& base64_string,
104 + std::vector<std::uint8_t>& out_decoded) {
105 + default_crypto_supplier_usage_error() return false;
106 + }
107 +
108 + bool AbstractCryptoSupplier::base64_decode_to_string(const std::string& base64_string, std::string& out_decoded) {
109 + default_crypto_supplier_usage_error() return false;
110 + }
111 +
112 + bool AbstractCryptoSupplier::base64_encode_from_bytes(const std::vector<std::uint8_t>& bytes,
113 + std::string& out_encoded) {
114 + default_crypto_supplier_usage_error() return false;
115 + }
116 +
117 + bool AbstractCryptoSupplier::base64_encode_from_string(const std::string& string, std::string& out_encoded) {
118 + default_crypto_supplier_usage_error() return false;
119 + }
120 +
121 + } // namespace evse_security
122 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_types.cpp.c70dd2fc6ec97e26d08c31dbeac6657e.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_types.cpp.c70dd2fc6ec97e26d08c31dbeac6657e.html new file mode 100644 index 00000000..4f3ce1e1 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_types.cpp.c70dd2fc6ec97e26d08c31dbeac6657e.html @@ -0,0 +1,326 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/crypto/interface/crypto_types.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:0150.0%
Functions:010.0%
Branches:0210.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 +
4 + #include <evse_security/crypto/interface/crypto_types.hpp>
5 +
6 + namespace evse_security {
7 +
8 + namespace conversions {
9 +
10 + std::string get_certificate_sign_request_result_to_string(CertificateSignRequestResult e) {
11 +
12 + switch (e) {
13 + case CertificateSignRequestResult::Valid:
14 + return "Valid";
15 + case CertificateSignRequestResult::KeyGenerationError:
16 + return "KeyGenerationError";
17 + case CertificateSignRequestResult::VersioningError:
18 + return "VersioningError";
19 + case CertificateSignRequestResult::PubkeyError:
20 + return "PubkeyError";
21 + case CertificateSignRequestResult::ExtensionsError:
22 + return "ExtensionsError";
23 + case CertificateSignRequestResult::SigningError:
24 + return "SigningError";
25 + }
26 +
27 + throw std::out_of_range("No known string conversion for provided enum of type CertificateSignRequestResult");
28 + }
29 +
30 + } // namespace conversions
31 +
32 + } // namespace evse_security
33 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_types.hpp.c9bad1f0b8470dd682d8c38ea5e3f0c1.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_types.hpp.c9bad1f0b8470dd682d8c38ea5e3f0c1.html new file mode 100644 index 00000000..0fde7b3c --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.crypto_types.hpp.c9bad1f0b8470dd682d8c38ea5e3f0c1.html @@ -0,0 +1,774 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:include/evse_security/crypto/interface/crypto_types.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:22100.0%
Functions:11100.0%
Branches:00-%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #pragma once
4 +
5 + #include <chrono>
6 + #include <memory>
7 + #include <optional>
8 + #include <stdexcept>
9 + #include <string>
10 +
11 + namespace evse_security {
12 +
13 + enum class CryptoKeyType {
14 + EC_prime256v1, // Default EC. P-256, ~equiv to rsa 3072
15 + EC_secp384r1, // P-384, ~equiv to rsa 7680
16 + RSA_2048,
17 + RSA_TPM20 = RSA_2048, // Default TPM RSA, only option allowed for TPM (universal support), 2048 bits
18 + RSA_3072, // Default RSA. Protection lifetime: ~2030
19 + RSA_7680, // Protection lifetime: >2031. Very long generation time 8-40s on 16 core PC
20 + };
21 +
22 + enum class KeyValidationResult {
23 + Valid,
24 + KeyLoadFailure, // The key could not be loaded, might be an password or invalid string
25 + Invalid, // The key is not linked to the specified certificate
26 + Unknown, // Unknown error, not related to provider validation
27 + };
28 +
29 + enum class CertificateSignRequestResult {
30 + Valid,
31 + KeyGenerationError, // Error when generating the key, maybe invalid key type
32 + VersioningError, // The version could not be set
33 + PubkeyError, // The public key could not be attached
34 + ExtensionsError, // The extensions could not be appended
35 + SigningError, // The CSR could not be signed, maybe key or signing algo invalid
36 + Unknown, // Any other error
37 + };
38 +
39 + struct KeyGenerationInfo {
40 + CryptoKeyType key_type;
41 +
42 + /// @brief If the key should be generated using the custom provider. The custom
43 + /// provider can be the TPM if it was so configured. Should check before if
44 + /// the provider supports the operation, or the operation will fail by default
45 + bool generate_on_custom;
46 +
47 + /// @brief If we should export the public key to a file
48 + std::optional<std::string> public_key_file;
49 +
50 + /// @brief If we should export the private key to a file
51 + std::optional<std::string> private_key_file;
52 + /// @brief If we should have a pass for the private key file
53 + std::optional<std::string> private_key_pass;
54 + };
55 +
56 + struct CertificateSigningRequestInfo {
57 + // Minimal mandatory
58 + int n_version;
59 + std::string country;
60 + std::string organization;
61 + std::string commonName;
62 +
63 + /// @brief incude a subjectAlternativeName DNSName
64 + std::optional<std::string> dns_name;
65 + /// @brief incude a subjectAlternativeName IPAddress
66 + std::optional<std::string> ip_address;
67 +
68 + KeyGenerationInfo key_info;
69 + };
70 + class CertificateLoadException : public std::runtime_error {
71 + public:
72 + using std::runtime_error::runtime_error;
73 + };
74 +
75 + struct CryptoHandle {
76 + 14316 virtual ~CryptoHandle() {
77 + 14316 }
78 + };
79 +
80 + /// @brief Handle abstraction to crypto lib X509 certificate
81 + struct X509Handle : public CryptoHandle {};
82 +
83 + /// @brief Handle abstraction to crypto lib key
84 + struct KeyHandle : public CryptoHandle {};
85 +
86 + using X509Handle_ptr = std::unique_ptr<X509Handle>;
87 + using KeyHandle_ptr = std::unique_ptr<KeyHandle>;
88 +
89 + // Transforms a duration of days into seconds
90 + using days_to_seconds = std::chrono::duration<std::int64_t, std::ratio<86400>>;
91 +
92 + namespace conversions {
93 + std::string get_certificate_sign_request_result_to_string(CertificateSignRequestResult e);
94 + }
95 +
96 + } // namespace evse_security
97 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.css b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.css new file mode 100644 index 00000000..5c7a3843 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.css @@ -0,0 +1,620 @@ +:root { + font-family: sans-serif; + --tab_size: 4; +} + +.theme-green, .theme-blue { + --unknown_color: lightgray; + --low_color: #FF6666; + --medium_color: #F9FD63; + --partial_covered_color: var(--medium_color); + --uncovered_color: #FF8C8C; + --warning_color: orangered; + --notTakenBranch_color: red; + --notTakenCondition_color: red; + --uncheckedDecision_color: darkorange; + --notTakenDecision_color: red; + --notInvokedCall_color: red; + --excluded_color: rgb(255, 241, 229); +} + +.theme-green { + --high_color: #85E485; + --covered_color: #85E485; + --takenBranch_color: green; + --takenCondition_color: green; + --takenDecision_color: green; + --invokedCall_color: green; +} + +.theme-blue { + --high_color: #66B4FF; + --covered_color: #66B4Ff; + --takenBranch_color: blue; + --takenCondition_color: blue; + --takenDecision_color: blue; + --invokedCall_color: blue; +} + +body +{ + color: #000000; + background-color: #FFFFFF; +} + +h1 +{ + text-align: center; + margin: 0; + padding-bottom: 10px; + font-size: 20pt; + font-weight: bold; +} + +hr +{ + background-color: navy; + height: 2px; + border: 0; +} + +/* Link formats: use maroon w/underlines */ +a:link +{ + color: navy; + text-decoration: underline; +} +a:visited +{ + color: maroon; + text-decoration: underline; +} + +/*** Summary formats ***/ + +.summary +{ + display: flex; + flex-flow: row wrap; + max-width: 100%; + justify-content: flex-start; +} + +.summary > table +{ + flex: 1 0 7em; + border: 0; +} + +.summary > :last-child { + margin-left: auto; +} + +table.legend +{ + color: black; + display: flex; + flex-flow: row wrap; + justify-content: flex-start; +} + +table.legend th[scope=row] +{ + font-weight: normal; + text-align: right; + white-space: nowrap; +} + +table.legend td +{ + color: blue; + text-align: left; + white-space: nowrap; + padding-left: 5px; +} + +table.legend td.legend +{ + color: black; + font-size: 80%; +} + +table.legend td.warning_text +{ + color: var(--warning_color); +} + +table.coverage td, +table.coverage th +{ + text-align: right; + color: black; + font-weight: normal; + white-space: nowrap; + padding-left: 5px; + padding-right: 4px; +} + +table.coverage td +{ + background-color: LightSteelBlue; +} + +table.coverage th[scope=row] +{ + color: black; + font-weight: normal; + white-space: nowrap; +} + +table.coverage th[scope=col] +{ + color: blue; + font-weight: normal; + white-space: nowrap; +} + +table.legend span +{ + margin-right: 4px; + padding: 2px; +} + +table.legend span.coverage-unknown, +table.legend span.coverage-none, +table.legend span.coverage-low, +table.legend span.coverage-medium, +table.legend span.coverage-high +{ + padding-left: 3px; + padding-right: 3px; +} + +table.legend span.coverage-unknown, +table.coverage td.coverage-unknown, +table.file-list td.coverage-unknown +{ + background-color: var(--unknown_color) !important; +} + +table.legend span.coverage-none, +table.legend span.coverage-low, +table.coverage td.coverage-none, +table.coverage td.coverage-low, +table.file-list td.coverage-none, +table.file-list td.coverage-low +{ + background-color: var(--low_color) !important; +} + +table.legend span.coverage-medium, +table.coverage td.coverage-medium, +table.file-list td.coverage-medium +{ + background-color: var(--medium_color) !important; +} + +table.legend span.coverage-high, +table.coverage td.coverage-high, +table.file-list td.coverage-high +{ + background-color: var(--high_color) !important; +} +/*** End of Summary formats ***/ +/*** Meter formats ***/ + +/* Common */ +meter { + -moz-appearance: none; + + width: 30vw; + min-width: 4em; + max-width: 15em; + height: 0.75em; + padding: 0; + vertical-align: baseline; + margin-top: 3px; + /* Outer background for Mozilla */ + background: none; + background-color: whitesmoke; +} + +/* Webkit */ + +meter::-webkit-meter-bar { + /* Outer background for Webkit */ + background: none; + background-color: whitesmoke; + height: 0.75em; + border-radius: 0px; +} + +meter::-webkit-meter-optimum-value, +meter::-webkit-meter-suboptimum-value, +meter::-webkit-meter-even-less-good-value +{ + /* Inner shadow for Webkit */ + border: solid 1px black; +} + +meter.coverage-none::-webkit-meter-optimum-value, +meter.coverage-low::-webkit-meter-optimum-value +{ + background: var(--low_color); +} + +meter.coverage-medium::-webkit-meter-optimum-value +{ + background: var(--medium_color); +} + +meter.coverage-high::-webkit-meter-optimum-value +{ + background: var(--high_color); +} + +/* Mozilla */ + +meter::-moz-meter-bar +{ + box-sizing: border-box; +} + +meter:-moz-meter-optimum::-moz-meter-bar, +meter:-moz-meter-sub-optimum::-moz-meter-bar, +meter:-moz-meter-sub-sub-optimum::-moz-meter-bar +{ + /* Inner shadow for Mozilla */ + border: solid 1px black; +} + +meter.coverage-none:-moz-meter-optimum::-moz-meter-bar, +meter.coverage-low:-moz-meter-optimum::-moz-meter-bar +{ + background: var(--low_color); +} + +meter.coverage-medium:-moz-meter-optimum::-moz-meter-bar +{ + background: var(--medium_color); +} + +meter.coverage-high:-moz-meter-optimum::-moz-meter-bar +{ + background: var(--high_color); +} + +/*** End of Meter formats ***/ +.file-list td, .file-list th { + padding: 0 10px; + font-weight: bold; +} + +.file-list th[scope^=col] +{ + text-align: center; + color: white; + background-color: SteelBlue; + font-size: 120%; +} + +.file-list th[scope=row] +{ + text-align: left; + color: black; + font-family: monospace; + font-weight: bold; + font-size: 110%; +} + +.file-list tr > td, +.file-list tr > th { + background: aliceblue; +} + +.file-list tr:nth-child(even) > td, +.file-list tr:nth-child(even) > th { + background: LightSteelBlue +} + +.file-list tr:hover > td, +.file-list tr:hover > th[scope=row] +{ + background-color: #ddd; +} +td.CoverValue +{ + text-align: right; + white-space: nowrap; +} + +td.coveredLine, +span.coveredLine +{ + background-color: var(--covered_color) !important; +} + +td.partialCoveredLine, +span.partialCoveredLine +{ + background-color: var(--partial_covered_color) !important; +} + +td.uncoveredLine, +span.uncoveredLine +{ + background-color: var(--uncovered_color) !important; +} + +td.excludedLine, +span.excludedLine +{ + background-color: var(--excluded_color) !important; +} + +.linebranch, .linecondition, .linedecision, .linecall, .linecount +{ + font-family: monospace; + border-right: 1px gray solid; + background-color: lightgray; + text-align: right; +} + + +.linebranchDetails, .lineconditionDetails, .linedecisionDetails, .linecallDetails +{ + position: relative; +} +.linebranchSummary, .lineconditionSummary, .linedecisionSummary, .linecallSummary +{ + cursor: help; +} +.linebranchContents, .lineconditionContents, .linedecisionContents, .linecallContents +{ + font-family: sans-serif; + font-size: small; + text-align: left; + position: absolute; + width: 15em; + padding: 1em; + background: white; + border: solid gray 1px; + box-shadow: 5px 5px 10px gray; + z-index: 1; /* show in front of the table entries */ +} + +.excludedBranch +{ + color: var(--takenBranch_color) !important; +} + +.takenBranch +{ + color: var(--takenBranch_color) !important; +} + +.notTakenBranch +{ + color: var(--notTakenBranch_color) !important; +} + +.takenCondition +{ + color: var(--takenCondition_color) !important; +} + +.notTakenCondition +{ + color: var(--notTakenCondition_color) !important; +} + +.takenDecision +{ + color: var(--takenDecision_color) !important; +} + +.notTakenDecision +{ + color: var(--notTakenDecision_color) !important; +} + +.uncheckedDecision +{ + color: var(--uncheckedDecision_color) !important; +} + +.invokedCall +{ + color: var(--invokedCall_color) !important; +} + +.notInvokedCall +{ + color: var(--notInvokedCall_color) !important; +} + +.src +{ + padding-left: 12px; + text-align: left; + + font-family: monospace; + white-space: pre; + + tab-size: var(--tab_size); + -moz-tab-size: var(--tab_size); +} + +span.takenBranch, +span.notTakenBranch, +span.takenDecision, +span.notTakenDecision, +span.uncheckedDecision +{ + font-family: monospace; + font-weight: bold; +} + +pre +{ + height : 15px; + margin-top: 0; + margin-bottom: 0; +} + +.listOfFunctions td, .listOfFunctions th { + padding: 0 10px; +} +.listOfFunctions th +{ + text-align: center; + color: white; + background-color: SteelBlue; +} +.listOfFunctions tr > td { + background: aliceblue; +} +.listOfFunctions tr:nth-child(even) > td { + background: LightSteelBlue +} +.listOfFunctions tr:hover > td +{ + background-color: #ddd; +} +.listOfFunctions tr > td > a +{ + text-decoration: none; + color: inherit; +} + +.source-line +{ + height : 15px; + margin-top: 0; + margin-bottom: 0; +} + +.lineno +{ + background-color: #EFE383; + border-right: 1px solid #BBB15F; + text-align: right; + unicode-bidi: embed; + font-family: monospace; + white-space: pre; +} + +.lineno > a +{ + text-decoration: none; + color: inherit; +} + +.file-list +{ + margin: 1em auto; + border: 0; + border-spacing: 1px; +} + +.file-source table +{ + border-spacing: 0; +} + +.file-source table td, +.file-source table th +{ + padding: 1px 10px; +} + +.file-source table th +{ + font-family: monospace; + font-weight: bold; +} + +.file-source table td:last-child +{ + width: 100%; +} +footer +{ + text-align: center; + padding-top: 3px; +} + +/* pygments syntax highlighting */ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.hll { background-color: #ffffcc } +.c { color: #3D7B7B; font-style: italic } /* Comment */ +.err { border: 1px solid #FF0000 } /* Error */ +.k { color: #008000; font-weight: bold } /* Keyword */ +.o { color: #666666 } /* Operator */ +.ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.cp { color: #9C6500 } /* Comment.Preproc */ +.cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #E40000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #008400 } /* Generic.Inserted */ +.go { color: #717171 } /* Generic.Output */ +.gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0044DD } /* Generic.Traceback */ +.kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #008000 } /* Keyword.Pseudo */ +.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #B00040 } /* Keyword.Type */ +.m { color: #666666 } /* Literal.Number */ +.s { color: #BA2121 } /* Literal.String */ +.na { color: #687822 } /* Name.Attribute */ +.nb { color: #008000 } /* Name.Builtin */ +.nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.no { color: #880000 } /* Name.Constant */ +.nd { color: #AA22FF } /* Name.Decorator */ +.ni { color: #717171; font-weight: bold } /* Name.Entity */ +.ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.nf { color: #0000FF } /* Name.Function */ +.nl { color: #767600 } /* Name.Label */ +.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.nt { color: #008000; font-weight: bold } /* Name.Tag */ +.nv { color: #19177C } /* Name.Variable */ +.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mb { color: #666666 } /* Literal.Number.Bin */ +.mf { color: #666666 } /* Literal.Number.Float */ +.mh { color: #666666 } /* Literal.Number.Hex */ +.mi { color: #666666 } /* Literal.Number.Integer */ +.mo { color: #666666 } /* Literal.Number.Oct */ +.sa { color: #BA2121 } /* Literal.String.Affix */ +.sb { color: #BA2121 } /* Literal.String.Backtick */ +.sc { color: #BA2121 } /* Literal.String.Char */ +.dl { color: #BA2121 } /* Literal.String.Delimiter */ +.sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.s2 { color: #BA2121 } /* Literal.String.Double */ +.se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.sh { color: #BA2121 } /* Literal.String.Heredoc */ +.si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.sx { color: #008000 } /* Literal.String.Other */ +.sr { color: #A45A77 } /* Literal.String.Regex */ +.s1 { color: #BA2121 } /* Literal.String.Single */ +.ss { color: #19177C } /* Literal.String.Symbol */ +.bp { color: #008000 } /* Name.Builtin.Pseudo */ +.fm { color: #0000FF } /* Name.Function.Magic */ +.vc { color: #19177C } /* Name.Variable.Class */ +.vg { color: #19177C } /* Name.Variable.Global */ +.vi { color: #19177C } /* Name.Variable.Instance */ +.vm { color: #19177C } /* Name.Variable.Magic */ +.il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_filesystem.cpp.cb6f9fc0d503851e5e7df7609144c7bf.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_filesystem.cpp.cb6f9fc0d503851e5e7df7609144c7bf.html new file mode 100644 index 00000000..efa94615 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_filesystem.cpp.cb6f9fc0d503851e5e7df7609144c7bf.html @@ -0,0 +1,2009 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/utils/evse_filesystem.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:6912754.3%
Functions:81080.0%
Branches:7755613.8%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright 2023 Pionix GmbH and Contributors to EVerest
3 + #include <evse_security/evse_types.hpp>
4 + #include <evse_security/utils/evse_filesystem.hpp>
5 +
6 + #include <fstream>
7 + #include <iostream>
8 + #include <limits>
9 + #include <random>
10 +
11 + #include <everest/logging.hpp>
12 +
13 + namespace evse_security::filesystem_utils {
14 +
15 + bool is_subdirectory(const fs::path& base, const fs::path& subdir) {
16 + fs::path relativePath = fs::relative(subdir, base);
17 + return !relativePath.empty();
18 + }
19 +
20 + 112bool delete_file(const fs::path& file_path) {
21 + try {
22 +
+ 2/4 +
+
✓ Branch 1 taken 112 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 112 times.
+
✗ Branch 4 not taken.
+
+
+
112 if (fs::is_regular_file(file_path)) {
23 +
+ 1/2 +
+
✓ Branch 1 taken 112 times.
+
✗ Branch 2 not taken.
+
+
+
112 return fs::remove(file_path);
24 + }
25 + } catch (const std::exception& e) {
26 + EVLOG_error << "Filesystem error: " << e.what();
27 + }
28 +
29 + EVLOG_error << "Error deleting file: " << file_path;
30 + return false;
31 + }
32 +
33 + 1022bool read_from_file(const fs::path& file_path, std::string& out_data) {
34 + try {
35 +
+ 2/4 +
+
✓ Branch 1 taken 1022 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 1022 times.
+
✗ Branch 4 not taken.
+
+
+
1022 if (fs::is_regular_file(file_path)) {
36 +
+ 1/2 +
+
✓ Branch 1 taken 1022 times.
+
✗ Branch 2 not taken.
+
+
+
1022 fsstd::ifstream file(file_path, std::ios::binary);
37 +
38 +
+ 1/2 +
+
✓ Branch 1 taken 1022 times.
+
✗ Branch 2 not taken.
+
+
+
1022 if (file.is_open()) {
39 +
+ 1/2 +
+
✓ Branch 4 taken 1022 times.
+
✗ Branch 5 not taken.
+
+
+
1022 out_data = std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
40 + 1022 return true;
41 + }
42 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 1022 times.
+
+
+
1022 }
43 + } catch (const std::exception& e) {
44 + EVLOG_error << "Error while reading from file: " << e.what();
45 + }
46 +
47 + EVLOG_error << "Error reading file: " << file_path;
48 + return false;
49 + }
50 +
51 + 28bool create_file_if_nonexistent(const fs::path& file_path) {
52 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 28 times.
+
+
+
28 if (file_path.empty()) {
53 + EVLOG_warning << "Provided empty path!";
54 + return false;
55 + }
56 +
57 + try {
58 +
+ 2/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 28 times.
+
+
+
28 if (!fs::exists(file_path)) {
59 + std::ofstream file(file_path);
60 + return true;
61 +
+ 2/4 +
+
✓ Branch 2 taken 28 times.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 28 times.
+
+
+
28 } else if (fs::is_directory(file_path)) {
62 + EVLOG_error << "Attempting to create file over existing directory: " << file_path;
63 + return false;
64 + }
65 + } catch (const std::exception& e) {
66 + EVLOG_error << "Error while creating file: " << e.what();
67 + }
68 +
69 + 28 return true;
70 + }
71 +
72 + 496bool create_file_or_dir_if_nonexistent(const fs::path& path) {
73 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 496 times.
+
+
+
496 if (path.empty()) {
74 + EVLOG_warning << "Provided empty path!";
75 + return false;
76 + }
77 +
78 + try {
79 + // In case the path is missing, create it
80 +
+ 3/4 +
+
✓ Branch 1 taken 496 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 10 times.
+
✓ Branch 4 taken 486 times.
+
+
+
496 if (fs::exists(path) == false) {
81 +
+ 2/2 +
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 8 times.
+
+
+
10 if (path.has_extension()) {
82 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 std::ofstream new_file(path.c_str());
83 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 new_file.close();
84 + 2 return true;
85 + 2 } else {
86 + // Else create a directory
87 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 fs::create_directories(path);
88 + 8 return true;
89 + }
90 + }
91 + } catch (const std::exception& e) {
92 + EVLOG_error << "Error while creating dir/file: " << e.what();
93 + }
94 +
95 + 486 return false;
96 + }
97 +
98 + 42bool write_to_file(const fs::path& file_path, const std::string& data, std::ios::openmode mode) {
99 + try {
100 +
+ 1/2 +
+
✓ Branch 2 taken 42 times.
+
✗ Branch 3 not taken.
+
+
+
42 fsstd::ofstream fs(file_path, mode | std::ios::binary);
101 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 42 times.
+
+
+
42 if (!fs.is_open()) {
102 + EVLOG_error << "Error opening file: " << file_path;
103 + return false;
104 + }
105 +
+ 1/2 +
+
✓ Branch 3 taken 42 times.
+
✗ Branch 4 not taken.
+
+
+
42 fs.write(data.c_str(), data.size());
106 +
107 +
+ 2/4 +
+
✓ Branch 1 taken 42 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 42 times.
+
+
+
42 if (!fs) {
108 + EVLOG_error << "Error writing to file: " << file_path;
109 + return false;
110 + }
111 + 42 return true;
112 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
42 } catch (const std::exception& e) {
113 + EVLOG_error << "Unknown error occurred while writing to file: " << file_path;
114 + return false;
115 + }
116 +
117 + return true;
118 + }
119 +
120 + bool process_file(const fs::path& file_path, size_t buffer_size,
121 + std::function<bool(const std::uint8_t*, std::size_t, bool last_chunk)>&& func) {
122 + std::ifstream file(file_path, std::ios::binary);
123 +
124 + if (!file) {
125 + EVLOG_error << "Error opening file: " << file_path;
126 + return false;
127 + }
128 +
129 + std::vector<std::uint8_t> buffer(buffer_size);
130 + bool interupted = false;
131 +
132 + while (file.read(reinterpret_cast<char*>(buffer.data()), buffer_size)) {
133 + interupted = func(buffer.data(), buffer_size, false);
134 +
135 + if (interupted) {
136 + break;
137 + }
138 + }
139 +
140 + // Process the remaining bytes
141 + if (interupted == false) {
142 + size_t remaining = file.gcount();
143 + func(buffer.data(), remaining, true);
144 + }
145 +
146 + return true;
147 + }
148 +
149 + 26std::string get_random_file_name(const std::string& extension) {
150 +
+ 4/8 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 24 times.
+
✓ Branch 3 taken 2 times.
+
✗ Branch 4 not taken.
+
✓ Branch 6 taken 2 times.
+
✗ Branch 7 not taken.
+
✗ Branch 10 not taken.
+
✗ Branch 11 not taken.
+
+
+
26 static std::random_device rd;
151 +
+ 5/10 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 24 times.
+
✓ Branch 3 taken 2 times.
+
✗ Branch 4 not taken.
+
✓ Branch 6 taken 2 times.
+
✗ Branch 7 not taken.
+
✓ Branch 9 taken 2 times.
+
✗ Branch 10 not taken.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
+
+
26 static std::mt19937 generator(rd());
152 +
+ 4/8 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 24 times.
+
✓ Branch 3 taken 2 times.
+
✗ Branch 4 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✗ Branch 10 not taken.
+
✗ Branch 11 not taken.
+
+
+
26 static std::uniform_int_distribution<int> distribution(1, std::numeric_limits<int>::max());
153 +
154 + static int increment = 0;
155 +
156 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 std::ostringstream buff;
157 +
158 + 26 auto now = std::chrono::system_clock::now();
159 + 26 std::time_t time = std::chrono::system_clock::to_time_t(now);
160 +
+ 4/8 +
+
✓ Branch 3 taken 26 times.
+
✗ Branch 4 not taken.
+
✓ Branch 6 taken 26 times.
+
✗ Branch 7 not taken.
+
✓ Branch 10 taken 26 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 26 times.
+
✗ Branch 14 not taken.
+
+
+
52 buff << std::put_time(std::gmtime(&time), "M%m_D%d_Y%Y_H%H_M%M_S%S_") << "i" << std::to_string(++increment) << "_r"
161 +
+ 3/6 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 26 times.
+
✗ Branch 8 not taken.
+
+
+
26 << distribution(generator) << extension;
162 +
163 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
52 return buff.str();
164 + 26}
165 +
166 + 142bool read_hash_from_file(const fs::path& file_path, CertificateHashData& out_hash) {
167 +
+ 3/4 +
+
✓ Branch 1 taken 142 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 74 times.
+
✓ Branch 6 taken 68 times.
+
+
+
142 if (file_path.extension() == CERT_HASH_EXTENSION) {
168 + try {
169 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 std::ifstream hs(file_path);
170 + 74 std::string algo;
171 +
172 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 hs >> algo;
173 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 hs >> out_hash.issuer_name_hash;
174 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 hs >> out_hash.issuer_key_hash;
175 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 hs >> out_hash.serial_number;
176 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 hs.close();
177 +
178 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 out_hash.hash_algorithm = conversions::string_to_hash_algorithm(algo);
179 +
180 + 74 return true;
181 +
+ 0/2 +
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
+
+
74 } catch (const std::exception& e) {
182 + EVLOG_error << "Unknown error occurred while reading cert hash file: " << file_path << " err: " << e.what();
183 + return false;
184 + }
185 + }
186 +
187 + 68 return false;
188 + }
189 +
190 + 16bool write_hash_to_file(const fs::path& file_path, const CertificateHashData& hash) {
191 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 auto real_path = file_path;
192 +
193 +
+ 5/12 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✓ Branch 8 taken 16 times.
+
✓ Branch 9 taken 16 times.
+
✗ Branch 10 not taken.
+
✗ Branch 12 not taken.
+
✓ Branch 13 taken 16 times.
+
✗ Branch 14 not taken.
+
✗ Branch 15 not taken.
+
+
+
16 if (file_path.has_extension() == false || file_path.extension() != CERT_HASH_EXTENSION) {
194 + real_path.replace_extension(CERT_HASH_EXTENSION);
195 + }
196 +
197 + try {
198 + // Write out the related hash
199 +
+ 1/2 +
+
✓ Branch 2 taken 16 times.
+
✗ Branch 3 not taken.
+
+
+
16 std::ofstream hs(real_path.c_str());
200 +
201 +
+ 3/6 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 16 times.
+
✗ Branch 8 not taken.
+
+
+
16 hs << conversions::hash_algorithm_to_string(hash.hash_algorithm) << "\n";
202 +
+ 2/4 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
+
+
16 hs << hash.issuer_name_hash << "\n";
203 +
+ 2/4 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
+
+
16 hs << hash.issuer_key_hash << "\n";
204 +
+ 2/4 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
+
+
16 hs << hash.serial_number << "\n";
205 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 hs.close();
206 +
207 + 16 return true;
208 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
16 } catch (const std::exception& e) {
209 + EVLOG_error << "Unknown error occurred writing cert hash file: " << file_path << " err: " << e.what();
210 + return false;
211 + }
212 +
213 + return false;
214 + 16}
215 +
216 + } // namespace evse_security::filesystem_utils
217 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_security.cpp.164a7caa96b505df2c8601604105f025.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_security.cpp.164a7caa96b505df2c8601604105f025.html new file mode 100644 index 00000000..78a588ae --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_security.cpp.164a7caa96b505df2c8601604105f025.html @@ -0,0 +1,20347 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/evse_security.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:822121567.7%
Functions:455680.4%
Branches:1356484128.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 +
4 + #include <everest/logging.hpp>
5 +
6 + #include <evse_security/evse_security.hpp>
7 +
8 + #include <algorithm>
9 + #include <fstream>
10 + #include <iostream>
11 + #include <optional>
12 + #include <set>
13 + #include <stdio.h>
14 +
15 + #include <cert_rehash/c_rehash.hpp>
16 +
17 + #include <evse_security/certificate/x509_bundle.hpp>
18 + #include <evse_security/certificate/x509_hierarchy.hpp>
19 + #include <evse_security/certificate/x509_wrapper.hpp>
20 + #include <evse_security/utils/evse_filesystem.hpp>
21 +
22 + namespace evse_security {
23 +
24 + 4static InstallCertificateResult to_install_certificate_result(CertificateValidationResult error) {
25 +
+ 2/7 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 2 times.
+
+
+
4 switch (error) {
26 + case CertificateValidationResult::Valid:
27 + EVLOG_info << "Certificate accepted";
28 + return InstallCertificateResult::Accepted;
29 + 2 case CertificateValidationResult::Expired:
30 +
+ 12/22 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 2 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 2 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 2 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 2 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 2 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 2 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 2 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 2 times.
+
✓ Branch 33 taken 2 times.
+
+
+
4 EVLOG_warning << "Certificate has expired";
31 + 2 return InstallCertificateResult::Expired;
32 + case CertificateValidationResult::InvalidSignature:
33 + EVLOG_warning << "Invalid signature";
34 + return InstallCertificateResult::InvalidSignature;
35 + case CertificateValidationResult::InvalidChain:
36 + EVLOG_warning << "Invalid certificate chain";
37 + return InstallCertificateResult::InvalidCertificateChain;
38 + case CertificateValidationResult::InvalidLeafSignature:
39 + EVLOG_warning << "Unable to verify leaf signature";
40 + return InstallCertificateResult::InvalidSignature;
41 + case CertificateValidationResult::IssuerNotFound:
42 + EVLOG_warning << "Issuer not found";
43 + return InstallCertificateResult::NoRootCertificateInstalled;
44 + 2 default:
45 + 2 return InstallCertificateResult::InvalidFormat;
46 + }
47 + }
48 +
49 + 24static std::vector<CaCertificateType> get_ca_certificate_types(const std::vector<CertificateType> certificate_types) {
50 + 24 std::vector<CaCertificateType> ca_certificate_types;
51 +
+ 2/2 +
+
✓ Branch 5 taken 56 times.
+
✓ Branch 6 taken 24 times.
+
+
+
80 for (const auto& certificate_type : certificate_types) {
52 +
+ 2/2 +
+
✓ Branch 0 taken 10 times.
+
✓ Branch 1 taken 46 times.
+
+
+
56 if (certificate_type == CertificateType::V2GRootCertificate) {
53 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 ca_certificate_types.push_back(CaCertificateType::V2G);
54 + }
55 +
+ 2/2 +
+
✓ Branch 0 taken 10 times.
+
✓ Branch 1 taken 46 times.
+
+
+
56 if (certificate_type == CertificateType::MORootCertificate) {
56 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 ca_certificate_types.push_back(CaCertificateType::MO);
57 + }
58 +
+ 2/2 +
+
✓ Branch 0 taken 16 times.
+
✓ Branch 1 taken 40 times.
+
+
+
56 if (certificate_type == CertificateType::CSMSRootCertificate) {
59 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 ca_certificate_types.push_back(CaCertificateType::CSMS);
60 + }
61 +
+ 2/2 +
+
✓ Branch 0 taken 10 times.
+
✓ Branch 1 taken 46 times.
+
+
+
56 if (certificate_type == CertificateType::MFRootCertificate) {
62 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 ca_certificate_types.push_back(CaCertificateType::MF);
63 + }
64 + }
65 + 24 return ca_certificate_types;
66 + }
67 +
68 + 50static CertificateType get_certificate_type(const CaCertificateType ca_certificate_type) {
69 +
+ 3/5 +
+
✓ Branch 0 taken 12 times.
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 26 times.
+
✓ Branch 3 taken 12 times.
+
✗ Branch 4 not taken.
+
+
+
50 switch (ca_certificate_type) {
70 + 12 case CaCertificateType::V2G:
71 + 12 return CertificateType::V2GRootCertificate;
72 + case CaCertificateType::MO:
73 + return CertificateType::MORootCertificate;
74 + 26 case CaCertificateType::CSMS:
75 + 26 return CertificateType::CSMSRootCertificate;
76 + 12 case CaCertificateType::MF:
77 + 12 return CertificateType::MFRootCertificate;
78 + default:
79 + throw std::runtime_error("Could not convert CaCertificateType to CertificateType");
80 + }
81 + }
82 +
83 + 293static bool is_keyfile(const fs::path& file_path) {
84 +
+ 2/2 +
+
✓ Branch 1 taken 289 times.
+
✓ Branch 2 taken 4 times.
+
+
+
293 if (fs::is_regular_file(file_path)) {
85 +
+ 1/2 +
+
✓ Branch 1 taken 289 times.
+
✗ Branch 2 not taken.
+
+
+
289 if (file_path.has_extension()) {
86 +
+ 1/2 +
+
✓ Branch 1 taken 289 times.
+
✗ Branch 2 not taken.
+
+
+
289 auto extension = file_path.extension();
87 +
+ 5/6 +
+
✓ Branch 1 taken 175 times.
+
✓ Branch 2 taken 114 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 175 times.
+
✓ Branch 6 taken 114 times.
+
✓ Branch 7 taken 175 times.
+
+
+
289 if (extension == KEY_EXTENSION || extension == CUSTOM_KEY_EXTENSION) {
88 + 114 return true;
89 + }
90 +
+ 2/2 +
+
✓ Branch 1 taken 175 times.
+
✓ Branch 2 taken 114 times.
+
+
+
289 }
91 + }
92 +
93 + 179 return false;
94 + }
95 +
96 + /// @brief Searches for the private key linked to the provided certificate
97 + 92static fs::path get_private_key_path_of_certificate(const X509Wrapper& certificate, const fs::path& key_path_directory,
98 + const std::optional<std::string> password) {
99 + // Before iterating the whole dir check by the filename first 'key_path'.key/.tkey
100 +
+ 3/4 +
+
✓ Branch 1 taken 92 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 86 times.
+
✓ Branch 6 taken 6 times.
+
+
+
92 if (certificate.get_file().has_value()) {
101 + // Check normal keyfile & tpm filename
102 +
+ 4/10 +
+
✓ Branch 1 taken 86 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 86 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 106 times.
+
✓ Branch 9 taken 20 times.
+
✗ Branch 11 not taken.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 14 not taken.
+
+
+
384 for (const auto& extension : {KEY_EXTENSION, CUSTOM_KEY_EXTENSION}) {
103 +
+ 2/4 +
+
✓ Branch 1 taken 106 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 106 times.
+
✗ Branch 5 not taken.
+
+
+
106 fs::path potential_keyfile = certificate.get_file().value();
104 +
+ 1/2 +
+
✓ Branch 1 taken 106 times.
+
✗ Branch 2 not taken.
+
+
+
106 potential_keyfile.replace_extension(extension);
105 +
106 +
+ 3/4 +
+
✓ Branch 1 taken 106 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 66 times.
+
✓ Branch 4 taken 40 times.
+
+
+
106 if (fs::exists(potential_keyfile)) {
107 + try {
108 + 66 std::string private_key;
109 +
110 +
+ 2/4 +
+
✓ Branch 1 taken 66 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 66 times.
+
✗ Branch 4 not taken.
+
+
+
66 if (filesystem_utils::read_from_file(potential_keyfile, private_key)) {
111 + 132 if (KeyValidationResult::Valid ==
112 +
+ 4/8 +
+
✓ Branch 1 taken 66 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 66 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 66 times.
+
✗ Branch 9 not taken.
+
✓ Branch 12 taken 66 times.
+
✗ Branch 13 not taken.
+
+
+
66 CryptoSupplier::x509_check_private_key(certificate.get(), private_key, password)) {
113 +
+ 13/24 +
+
✓ Branch 1 taken 66 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 66 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 66 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 66 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 66 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 66 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 66 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 66 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 66 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 66 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 66 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 66 times.
+
✓ Branch 36 taken 66 times.
+
+
+
132 EVLOG_debug << "Key found for certificate at path: " << potential_keyfile;
114 + 66 return potential_keyfile;
115 + }
116 + }
117 +
+ 1/6 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 66 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
+
+
66 } catch (const std::exception& e) {
118 + EVLOG_debug << "Could not load or verify private key at: " << potential_keyfile << ": " << e.what();
119 + }
120 + }
121 +
+ 4/6 +
+
✓ Branch 1 taken 40 times.
+
✓ Branch 2 taken 66 times.
+
✓ Branch 3 taken 172 times.
+
✓ Branch 4 taken 86 times.
+
✗ Branch 6 not taken.
+
✗ Branch 7 not taken.
+
+
+
364 }
122 + }
123 +
124 +
+ 3/6 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 11 taken 157 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 183 times.
+
✗ Branch 15 not taken.
+
+
+
183 for (const auto& entry : fs::recursive_directory_iterator(key_path_directory)) {
125 +
+ 2/4 +
+
✓ Branch 2 taken 183 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 183 times.
+
✗ Branch 5 not taken.
+
+
+
183 if (fs::is_regular_file(entry)) {
126 +
+ 1/2 +
+
✓ Branch 2 taken 183 times.
+
✗ Branch 3 not taken.
+
+
+
183 auto key_file_path = entry.path();
127 +
+ 3/4 +
+
✓ Branch 1 taken 183 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 80 times.
+
✓ Branch 4 taken 103 times.
+
+
+
183 if (is_keyfile(key_file_path)) {
128 + try {
129 + 80 std::string private_key;
130 +
131 +
+ 2/4 +
+
✓ Branch 1 taken 80 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 80 times.
+
✗ Branch 4 not taken.
+
+
+
80 if (filesystem_utils::read_from_file(key_file_path, private_key)) {
132 + 160 if (KeyValidationResult::Valid ==
133 +
+ 5/8 +
+
✓ Branch 1 taken 80 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 80 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 80 times.
+
✗ Branch 9 not taken.
+
✓ Branch 12 taken 26 times.
+
✓ Branch 13 taken 54 times.
+
+
+
80 CryptoSupplier::x509_check_private_key(certificate.get(), private_key, password)) {
134 +
+ 13/24 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 26 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 26 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 26 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 26 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 26 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 26 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 26 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 26 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 26 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 26 times.
+
✓ Branch 36 taken 26 times.
+
+
+
52 EVLOG_debug << "Key found for certificate at path: " << key_file_path;
135 + 26 return key_file_path;
136 + }
137 + }
138 +
+ 2/6 +
+
✓ Branch 1 taken 54 times.
+
✓ Branch 2 taken 26 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
+
+
80 } catch (const std::exception& e) {
139 + EVLOG_debug << "Could not load or verify private key at: " << key_file_path << ": " << e.what();
140 + }
141 + }
142 +
+ 2/2 +
+
✓ Branch 1 taken 157 times.
+
✓ Branch 2 taken 26 times.
+
+
+
183 }
143 +
+ 3/6 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 26 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 26 times.
+
✗ Branch 7 not taken.
+
✓ Branch 8 taken 26 times.
+
+
+
78 }
144 +
145 + std::string error = "Could not find private key for given certificate: ";
146 + error += certificate.get_file().value_or("N/A");
147 + error += " key path: ";
148 + error += key_path_directory;
149 +
150 + throw NoPrivateKeyException(error);
151 + }
152 +
153 + /// @brief Searches for the certificate linked to the provided key
154 + /// @return The files where the certificates were found, more than one can be returned in case it is
155 + /// present in a bundle too
156 + 34static std::set<fs::path> get_certificate_path_of_key(const fs::path& key, const fs::path& certificate_path_directory,
157 + const std::optional<std::string> password) {
158 + 34 std::string private_key;
159 +
160 +
+ 2/4 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 34 times.
+
+
+
34 if (false == filesystem_utils::read_from_file(key, private_key)) {
161 + throw NoPrivateKeyException("Could not read private key from path: " + private_key);
162 + }
163 +
164 + // Before iterating all bundles, check by certificates from key filename
165 +
+ 1/2 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
+
+
34 fs::path cert_filename = key;
166 +
+ 1/2 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
+
+
34 cert_filename.replace_extension(PEM_EXTENSION);
167 +
168 +
+ 3/4 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 26 times.
+
✓ Branch 4 taken 8 times.
+
+
+
34 if (fs::exists(cert_filename)) {
169 + try {
170 + 26 std::set<fs::path> bundles;
171 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 X509CertificateBundle certificate_bundles(cert_filename, EncodingFormat::PEM);
172 +
173 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 certificate_bundles.for_each_chain(
174 + 26 [&](const fs::path& bundle, const std::vector<X509Wrapper>& certificates) {
175 +
+ 2/2 +
+
✓ Branch 5 taken 26 times.
+
✓ Branch 6 taken 26 times.
+
+
+
52 for (const auto& certificate : certificates) {
176 + 52 if (KeyValidationResult::Valid ==
177 +
+ 4/8 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 26 times.
+
✗ Branch 9 not taken.
+
✓ Branch 12 taken 26 times.
+
✗ Branch 13 not taken.
+
+
+
26 CryptoSupplier::x509_check_private_key(certificate.get(), private_key, password)) {
178 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 bundles.emplace(bundle);
179 + }
180 + }
181 +
182 + // Continue iterating
183 + 26 return true;
184 + });
185 +
186 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 if (bundles.empty() == false) {
187 + 26 return bundles;
188 + }
189 +
+ 2/6 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 26 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 26 times.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
+
+
52 } catch (const CertificateLoadException& e) {
190 + EVLOG_debug << "Could not load certificate bundle at: " << certificate_path_directory << ": " << e.what();
191 + }
192 + }
193 +
194 + try {
195 + 8 std::set<fs::path> bundles;
196 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 X509CertificateBundle certificate_bundles(certificate_path_directory, EncodingFormat::PEM);
197 +
198 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 certificate_bundles.for_each_chain([&](const fs::path& bundle, const std::vector<X509Wrapper>& certificates) {
199 +
+ 2/2 +
+
✓ Branch 5 taken 8 times.
+
✓ Branch 6 taken 8 times.
+
+
+
16 for (const auto& certificate : certificates) {
200 + 16 if (KeyValidationResult::Valid ==
201 +
+ 4/8 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 8 times.
+
✗ Branch 9 not taken.
+
✗ Branch 12 not taken.
+
✓ Branch 13 taken 8 times.
+
+
+
8 CryptoSupplier::x509_check_private_key(certificate.get(), private_key, password)) {
202 + bundles.emplace(bundle);
203 + }
204 + }
205 +
206 + // Continue iterating
207 + 8 return true;
208 + });
209 +
210 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 8 times.
+
+
+
8 if (bundles.empty() == false) {
211 + return bundles;
212 + }
213 +
+ 2/8 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
✗ Branch 11 not taken.
+
✗ Branch 12 not taken.
+
+
+
8 } catch (const CertificateLoadException& e) {
214 + EVLOG_debug << "Could not load certificate bundle at: " << certificate_path_directory << ": " << e.what();
215 + }
216 +
217 +
+ 1/2 +
+
✓ Branch 2 taken 8 times.
+
✗ Branch 3 not taken.
+
+
+
8 std::string error = "Could not find certificate for given private key: ";
218 +
+ 2/4 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
+
+
8 error += key;
219 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 error += " certificates path: ";
220 +
+ 2/4 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
+
+
8 error += certificate_path_directory;
221 +
222 +
+ 1/2 +
+
✓ Branch 2 taken 8 times.
+
✗ Branch 3 not taken.
+
+
+
8 throw NoCertificateValidException(error);
223 +
+ 1/2 +
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 8 times.
+
+
+
58}
224 +
225 + // Declared here to avoid requirement of X509Wrapper include in header
226 + static OCSPRequestDataList get_ocsp_request_data_internal(fs::path& root_path, std::vector<X509Wrapper>& leaf_chain);
227 +
228 + std::mutex EvseSecurity::security_mutex;
229 +
230 + 68EvseSecurity::EvseSecurity(const FilePaths& file_paths, const std::optional<std::string>& private_key_password,
231 + const std::optional<std::uintmax_t>& max_fs_usage_bytes,
232 + const std::optional<std::uintmax_t>& max_fs_certificate_store_entries,
233 + const std::optional<std::chrono::seconds>& csr_expiry,
234 + 68 const std::optional<std::chrono::seconds>& garbage_collect_time) :
235 +
+ 2/4 +
+
✓ Branch 5 taken 68 times.
+
✗ Branch 6 not taken.
+
✓ Branch 8 taken 68 times.
+
✗ Branch 9 not taken.
+
+
+
68 private_key_password(private_key_password) {
236 + static_assert(sizeof(std::uint8_t) == 1, "uint8_t not equal to 1 byte!");
237 +
238 + std::vector<fs::path> dirs = {
239 + 68 file_paths.directories.csms_leaf_cert_directory,
240 + 68 file_paths.directories.csms_leaf_key_directory,
241 + 68 file_paths.directories.secc_leaf_cert_directory,
242 + 68 file_paths.directories.secc_leaf_key_directory,
243 +
+ 3/6 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 272 times.
+
✓ Branch 5 taken 68 times.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
+
+
340 };
244 +
245 +
+ 2/2 +
+
✓ Branch 5 taken 272 times.
+
✓ Branch 6 taken 68 times.
+
+
+
340 for (const auto& path : dirs) {
246 +
+ 2/4 +
+
✓ Branch 1 taken 272 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 272 times.
+
+
+
272 if (!fs::exists(path)) {
247 + EVLOG_warning << "Could not find configured leaf directory at: " << path.string()
248 + << " creating default dir!";
249 + if (!fs::create_directories(path)) {
250 + EVLOG_error << "Could not create default dir for path: " << path.string();
251 + }
252 +
+ 2/4 +
+
✓ Branch 1 taken 272 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 272 times.
+
+
+
272 } else if (!fs::is_directory(path)) {
253 + throw std::runtime_error(path.string() + " is not a directory.");
254 + }
255 + }
256 +
257 +
+ 2/4 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 68 times.
+
✗ Branch 5 not taken.
+
+
+
68 this->ca_bundle_path_map[CaCertificateType::CSMS] = file_paths.csms_ca_bundle;
258 +
+ 2/4 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 68 times.
+
✗ Branch 5 not taken.
+
+
+
68 this->ca_bundle_path_map[CaCertificateType::MF] = file_paths.mf_ca_bundle;
259 +
+ 2/4 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 68 times.
+
✗ Branch 5 not taken.
+
+
+
68 this->ca_bundle_path_map[CaCertificateType::MO] = file_paths.mo_ca_bundle;
260 +
+ 2/4 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 68 times.
+
✗ Branch 5 not taken.
+
+
+
68 this->ca_bundle_path_map[CaCertificateType::V2G] = file_paths.v2g_ca_bundle;
261 +
262 +
+ 2/2 +
+
✓ Branch 5 taken 272 times.
+
✓ Branch 6 taken 68 times.
+
+
+
340 for (const auto& pair : this->ca_bundle_path_map) {
263 +
+ 2/4 +
+
✓ Branch 1 taken 272 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 272 times.
+
+
+
272 if (!fs::exists(pair.second)) {
264 + EVLOG_warning << "Could not find configured " << conversions::ca_certificate_type_to_string(pair.first)
265 + << " bundle file at: " + pair.second.string() << ", creating default!";
266 + if (!filesystem_utils::create_file_if_nonexistent(pair.second)) {
267 + EVLOG_error << "Could not create default bundle for path: " << pair.second;
268 + }
269 + }
270 + }
271 +
272 + // Check that the leafs directory is not related to the bundle directory because
273 + // on garbage collect that can delete relevant CA certificates instead of leaf ones
274 +
+ 2/2 +
+
✓ Branch 5 taken 272 times.
+
✓ Branch 6 taken 68 times.
+
+
+
340 for (const auto& leaf_dir : dirs) {
275 +
+ 2/2 +
+
✓ Branch 7 taken 1088 times.
+
✓ Branch 8 taken 272 times.
+
+
+
1360 for (auto const& [certificate_type, ca_bundle_path] : ca_bundle_path_map) {
276 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 1088 times.
+
+
+
1088 if (ca_bundle_path == leaf_dir) {
277 + throw std::runtime_error(leaf_dir.string() +
278 + " leaf directory can not overlap CA directory: " + ca_bundle_path.string());
279 + }
280 + }
281 + }
282 +
283 +
+ 1/2 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
+
+
68 this->directories = file_paths.directories;
284 +
+ 1/2 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
+
+
68 this->links = file_paths.links;
285 +
286 + 68 this->max_fs_usage_bytes = max_fs_usage_bytes.value_or(DEFAULT_MAX_FILESYSTEM_SIZE);
287 + 68 this->max_fs_certificate_store_entries = max_fs_certificate_store_entries.value_or(DEFAULT_MAX_CERTIFICATE_ENTRIES);
288 + 68 this->csr_expiry = csr_expiry.value_or(DEFAULT_CSR_EXPIRY);
289 + 68 this->garbage_collect_time = garbage_collect_time.value_or(DEFAULT_GARBAGE_COLLECT_TIME);
290 +
291 + // Start GC timer
292 +
+ 1/2 +
+
✓ Branch 2 taken 68 times.
+
✗ Branch 3 not taken.
+
+
+
68 garbage_collect_timer.interval([this]() { this->garbage_collect(); }, this->garbage_collect_time);
293 +
+ 4/12 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 68 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 68 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 68 times.
+
✗ Branch 11 not taken.
+
✗ Branch 14 not taken.
+
✗ Branch 15 not taken.
+
✗ Branch 16 not taken.
+
✗ Branch 17 not taken.
+
+
+
136}
294 +
295 + 68EvseSecurity::~EvseSecurity() {
296 + 68}
297 +
298 + 34InstallCertificateResult EvseSecurity::install_ca_certificate(const std::string& certificate,
299 + CaCertificateType certificate_type) {
300 +
+ 1/2 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
+
+
34 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
301 +
302 +
+ 11/20 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 34 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 34 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 34 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 34 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 34 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 34 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 34 times.
+
✗ Branch 24 not taken.
+
✓ Branch 27 taken 34 times.
+
✗ Branch 28 not taken.
+
✓ Branch 30 taken 34 times.
+
✓ Branch 31 taken 34 times.
+
+
+
68 EVLOG_info << "Installing ca certificate: " << conversions::ca_certificate_type_to_string(certificate_type);
303 +
304 +
+ 3/4 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 2 times.
+
✓ Branch 4 taken 32 times.
+
+
+
34 if (is_filesystem_full()) {
305 +
+ 12/22 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 2 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 2 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 2 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 2 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 2 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 2 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 2 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 2 times.
+
✓ Branch 33 taken 2 times.
+
+
+
4 EVLOG_error << "Filesystem full, can't install new CA certificate!";
306 + 2 return InstallCertificateResult::CertificateStoreMaxLengthExceeded;
307 + }
308 +
309 + try {
310 +
+ 2/2 +
+
✓ Branch 1 taken 30 times.
+
✓ Branch 2 taken 2 times.
+
+
+
32 X509Wrapper new_cert(certificate, EncodingFormat::PEM);
311 +
312 +
+ 3/4 +
+
✓ Branch 1 taken 30 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 2 times.
+
✓ Branch 4 taken 28 times.
+
+
+
30 if (!new_cert.is_valid()) {
313 + 2 return InstallCertificateResult::Expired;
314 + }
315 +
316 + // Load existing
317 +
+ 2/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 28 times.
+
✗ Branch 5 not taken.
+
+
+
28 const auto ca_bundle_path = this->ca_bundle_path_map.at(certificate_type);
318 +
319 +
+ 2/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 28 times.
+
✗ Branch 4 not taken.
+
+
+
28 if (!fs::is_directory(ca_bundle_path)) {
320 + // Ensure file exists
321 +
+ 1/2 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
+
+
28 filesystem_utils::create_file_if_nonexistent(ca_bundle_path);
322 + }
323 +
324 +
+ 1/2 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
+
+
28 X509CertificateBundle existing_certs(ca_bundle_path, EncodingFormat::PEM);
325 +
326 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 28 times.
+
+
+
28 if (existing_certs.is_using_directory()) {
327 + std::string filename = conversions::ca_certificate_type_to_string(certificate_type) + "_ROOT_" +
328 + filesystem_utils::get_random_file_name(PEM_EXTENSION.string());
329 + fs::path new_path = ca_bundle_path / filename;
330 +
331 + // Sets the path of the new certificate
332 + new_cert.set_file(new_path);
333 + }
334 +
335 + // Check if cert is already installed
336 +
+ 2/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 28 times.
+
✗ Branch 4 not taken.
+
+
+
28 if (existing_certs.contains_certificate(new_cert) == false) {
337 +
+ 1/2 +
+
✓ Branch 2 taken 28 times.
+
✗ Branch 3 not taken.
+
+
+
28 existing_certs.add_certificate(std::move(new_cert));
338 +
339 +
+ 2/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 28 times.
+
✗ Branch 4 not taken.
+
+
+
28 if (existing_certs.export_certificates()) {
340 + 28 return InstallCertificateResult::Accepted;
341 + } else {
342 + return InstallCertificateResult::WriteError;
343 + }
344 + } else {
345 + // Else, simply update it
346 + if (existing_certs.update_certificate(std::move(new_cert))) {
347 + if (existing_certs.export_certificates()) {
348 + return InstallCertificateResult::Accepted;
349 + } else {
350 + return InstallCertificateResult::WriteError;
351 + }
352 + } else {
353 + return InstallCertificateResult::WriteError;
354 + }
355 + }
356 +
+ 1/2 +
+
✗ Branch 6 not taken.
+
✓ Branch 7 taken 2 times.
+
+
+
32 } catch (const CertificateLoadException& e) {
357 +
+ 13/24 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 2 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 2 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 2 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 2 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 2 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 2 times.
+
✗ Branch 27 not taken.
+
✓ Branch 30 taken 2 times.
+
✗ Branch 31 not taken.
+
✓ Branch 33 taken 2 times.
+
✗ Branch 34 not taken.
+
✓ Branch 36 taken 2 times.
+
✓ Branch 37 taken 2 times.
+
+
+
4 EVLOG_error << "Certificate load error: " << e.what();
358 + 2 return InstallCertificateResult::InvalidFormat;
359 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 }
360 + 34}
361 +
362 + 10DeleteCertificateResult EvseSecurity::delete_certificate(const CertificateHashData& certificate_hash_data) {
363 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
364 +
365 +
+ 10/18 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 10 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 10 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 10 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 10 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 10 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 10 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 10 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 10 times.
+
✓ Branch 27 taken 10 times.
+
+
+
20 EVLOG_info << "Delete CA certificate: " << certificate_hash_data.serial_number;
366 +
367 + 10 auto response = DeleteCertificateResult::NotFound;
368 +
369 + 10 bool found_certificate = false;
370 + 10 bool failed_to_write = false;
371 +
372 + // TODO (ioan): load all the bundles since if it's the V2G root in that case we might have to delete
373 + // whole hierarchies
374 +
+ 2/2 +
+
✓ Branch 7 taken 40 times.
+
✓ Branch 8 taken 10 times.
+
+
+
50 for (auto const& [certificate_type, ca_bundle_path] : ca_bundle_path_map) {
375 + try {
376 +
+ 1/2 +
+
✓ Branch 1 taken 40 times.
+
✗ Branch 2 not taken.
+
+
+
40 X509CertificateBundle ca_bundle(ca_bundle_path, EncodingFormat::PEM);
377 +
378 +
+ 3/4 +
+
✓ Branch 1 taken 40 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 6 times.
+
✓ Branch 4 taken 34 times.
+
+
+
40 if (ca_bundle.delete_certificate(certificate_hash_data, true)) {
379 + 6 found_certificate = true;
380 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 6 times.
+
+
+
6 if (!ca_bundle.export_certificates()) {
381 + failed_to_write = true;
382 + }
383 + }
384 +
385 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
40 } catch (const CertificateLoadException& e) {
386 + EVLOG_warning << "Could not load ca bundle from file: " << ca_bundle_path;
387 + }
388 + }
389 +
390 + 30 for (const auto& leaf_certificate_path :
391 +
+ 4/10 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 10 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 20 times.
+
✓ Branch 9 taken 10 times.
+
✗ Branch 11 not taken.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 14 not taken.
+
+
+
60 {directories.secc_leaf_cert_directory, directories.csms_leaf_cert_directory}) {
392 + try {
393 + 20 bool secc = (leaf_certificate_path == directories.secc_leaf_cert_directory);
394 +
+ 3/4 +
+
✓ Branch 1 taken 10 times.
+
✓ Branch 2 taken 10 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 10 times.
+
+
+
30 bool csms = (leaf_certificate_path == directories.csms_leaf_cert_directory) ||
395 + 10 (directories.csms_leaf_cert_directory == directories.secc_leaf_cert_directory);
396 +
397 + CaCertificateType load;
398 +
399 +
+ 2/2 +
+
✓ Branch 0 taken 10 times.
+
✓ Branch 1 taken 10 times.
+
+
+
20 if (secc)
400 + 10 load = CaCertificateType::V2G;
401 +
+ 1/2 +
+
✓ Branch 0 taken 10 times.
+
✗ Branch 1 not taken.
+
+
+
10 else if (csms)
402 + 10 load = CaCertificateType::CSMS;
403 +
404 + // Also load the roots since we need to build the hierarchy for correct certificate hashes
405 +
+ 2/4 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
+
+
20 X509CertificateBundle root_bundle(ca_bundle_path_map[load], EncodingFormat::PEM);
406 +
+ 1/2 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
+
+
20 X509CertificateBundle leaf_bundle(leaf_certificate_path, EncodingFormat::PEM);
407 +
408 + X509CertificateHierarchy hierarchy =
409 +
+ 3/6 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 20 times.
+
✗ Branch 8 not taken.
+
+
+
20 std::move(X509CertificateHierarchy::build_hierarchy(root_bundle.split(), leaf_bundle.split()));
410 +
411 +
+ 14/26 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 20 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 20 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 20 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 20 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 20 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 20 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 20 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 20 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 20 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 20 times.
+
✗ Branch 36 not taken.
+
✓ Branch 38 taken 20 times.
+
✓ Branch 39 taken 20 times.
+
+
+
60 EVLOG_debug << "Delete hierarchy:(" << leaf_certificate_path.string() << ")\n"
412 +
+ 3/6 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 20 times.
+
✗ Branch 8 not taken.
+
+
+
40 << hierarchy.to_debug_string();
413 +
414 + try {
415 + X509Wrapper to_delete =
416 +
+ 2/2 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 16 times.
+
+
+
20 hierarchy.find_certificate(certificate_hash_data, true /* case-insensitive search */);
417 +
418 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 4 times.
+
✗ Branch 4 not taken.
+
+
+
4 if (leaf_bundle.delete_certificate(to_delete, true)) {
419 + 4 found_certificate = true;
420 +
421 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 2 times.
+
+
+
4 if (csms) {
422 + // Per M04.FR.06 we are not allowed to delete the CSMS (ChargingStationCertificate), we should
423 + // return 'Failed'
424 + 2 failed_to_write = true;
425 +
+ 10/18 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 2 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 2 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 2 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 2 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 2 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 2 times.
+
✓ Branch 27 taken 2 times.
+
+
+
6 EVLOG_error << "Error, not allowed to delete ChargingStationCertificate: "
426 +
+ 4/8 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 11 taken 2 times.
+
✗ Branch 12 not taken.
+
+
+
4 << to_delete.get_common_name();
427 +
+ 2/4 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 2 times.
+
+
+
2 } else if (!leaf_bundle.export_certificates()) {
428 + failed_to_write = true;
429 + EVLOG_error << "Error removing leaf certificate: " << certificate_hash_data.issuer_name_hash;
430 + }
431 + }
432 +
+ 1/2 +
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 16 times.
+
+
+
20 } catch (NoCertificateFound& e) {
433 + // Ignore, case is handled later
434 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 }
435 +
+ 0/2 +
+
✗ Branch 6 not taken.
+
✗ Branch 7 not taken.
+
+
+
20 } catch (const CertificateLoadException& e) {
436 + EVLOG_warning << "Could not load ca bundle from file: " << leaf_certificate_path;
437 + }
438 +
+ 2/4 +
+
✓ Branch 0 taken 20 times.
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
30 }
439 +
440 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 8 times.
+
+
+
10 if (!found_certificate) {
441 + 2 return DeleteCertificateResult::NotFound;
442 + }
443 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 6 times.
+
+
+
8 if (failed_to_write) {
444 + // at least one certificate could not be deleted from the bundle
445 + 2 return DeleteCertificateResult::Failed;
446 + }
447 + 6 return DeleteCertificateResult::Accepted;
448 + 10}
449 +
450 + 12InstallCertificateResult EvseSecurity::update_leaf_certificate(const std::string& certificate_chain,
451 + LeafCertificateType certificate_type) {
452 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
453 +
454 +
+ 2/4 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 12 times.
+
+
+
12 if (is_filesystem_full()) {
455 + EVLOG_error << "Filesystem full, can't install new CA certificate!";
456 + return InstallCertificateResult::CertificateStoreMaxLengthExceeded;
457 + }
458 +
459 +
+ 11/20 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 12 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 12 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 12 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 12 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 12 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 12 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 12 times.
+
✗ Branch 24 not taken.
+
✓ Branch 27 taken 12 times.
+
✗ Branch 28 not taken.
+
✓ Branch 30 taken 12 times.
+
✓ Branch 31 taken 12 times.
+
+
+
24 EVLOG_info << "Updating leaf certificate: " << conversions::leaf_certificate_type_to_string(certificate_type);
460 +
461 + 12 fs::path cert_path;
462 + 12 fs::path key_path;
463 +
464 +
+ 2/2 +
+
✓ Branch 0 taken 4 times.
+
✓ Branch 1 taken 8 times.
+
+
+
12 if (certificate_type == LeafCertificateType::CSMS) {
465 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 cert_path = this->directories.csms_leaf_cert_directory;
466 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 key_path = this->directories.csms_leaf_key_directory;
467 +
+ 1/2 +
+
✓ Branch 0 taken 8 times.
+
✗ Branch 1 not taken.
+
+
+
8 } else if (certificate_type == LeafCertificateType::V2G) {
468 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 cert_path = this->directories.secc_leaf_cert_directory;
469 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 key_path = this->directories.secc_leaf_key_directory;
470 + } else {
471 + EVLOG_error << "Attempt to update leaf certificate for non CSMS/V2G certificate!";
472 + return InstallCertificateResult::WriteError;
473 + }
474 +
475 + try {
476 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 X509CertificateBundle chain_certificate(certificate_chain, EncodingFormat::PEM);
477 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 std::vector<X509Wrapper> _certificate_chain = chain_certificate.split();
478 +
+ 2/2 +
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 10 times.
+
+
+
12 if (_certificate_chain.empty()) {
479 + 2 return InstallCertificateResult::InvalidFormat;
480 + }
481 +
482 + // Internal since we already acquired the lock
483 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 const auto result = this->verify_certificate_internal(certificate_chain, certificate_type);
484 +
+ 2/2 +
+
✓ Branch 0 taken 4 times.
+
✓ Branch 1 taken 6 times.
+
+
+
10 if (result != CertificateValidationResult::Valid) {
485 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 return to_install_certificate_result(result);
486 + }
487 +
488 + // First certificate is always the leaf as per the spec
489 + 6 const auto& leaf_certificate = _certificate_chain[0];
490 +
491 + // Check if a private key belongs to the provided certificate
492 + 6 fs::path private_key_path;
493 +
494 + try {
495 + private_key_path =
496 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
+
+
6 get_private_key_path_of_certificate(leaf_certificate, key_path, this->private_key_password);
497 + } catch (const NoPrivateKeyException& e) {
498 + EVLOG_warning << "Provided certificate does not belong to any private key";
499 + return InstallCertificateResult::WriteError;
500 + }
501 +
502 + // Write certificate to file
503 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
+
+
6 std::string extra_filename = filesystem_utils::get_random_file_name(PEM_EXTENSION.string());
504 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
+
+
6 std::string file_name = conversions::leaf_certificate_type_to_filename(certificate_type) + extra_filename;
505 +
506 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
+
+
6 const auto file_path = cert_path / file_name;
507 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 std::string str_cert = leaf_certificate.get_export_string();
508 +
509 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 6 times.
+
✗ Branch 4 not taken.
+
+
+
6 if (filesystem_utils::write_to_file(file_path, str_cert, std::ios::out)) {
510 +
511 + // Remove from managed certificate keys, the CSR is fulfilled, no need to delete the key
512 + // since it is not orphaned any more
513 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 auto it = managed_csr.find(private_key_path);
514 +
+ 1/2 +
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 6 times.
+
+
+
6 if (it != managed_csr.end()) {
515 + managed_csr.erase(it);
516 + }
517 +
518 + // Do not presume that we received back a chain certificate that requires writing
519 + // there can be no intermediate certificates in between
520 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 6 times.
+
+
+
6 if (_certificate_chain.size() > 1) {
521 + // Attempt to write the chain to file
522 + const auto chain_file_name = std::string("CPO_CERT_") +
523 + conversions::leaf_certificate_type_to_filename(certificate_type) +
524 + "CHAIN_" + extra_filename;
525 +
526 + const auto chain_file_path = cert_path / chain_file_name;
527 + std::string str_chain_cert = chain_certificate.to_export_string();
528 +
529 + if (false == filesystem_utils::write_to_file(chain_file_path, str_chain_cert, std::ios::out)) {
530 + // This is an error, since if we contain SUBCAs those are required for a connection
531 + EVLOG_error << "Could not write leaf certificate chain to file!";
532 + return InstallCertificateResult::WriteError;
533 + }
534 + }
535 +
536 + // TODO(ioan): properly rename key path here for fast retrieval
537 + // @see 'get_private_key_path_of_certificate' and 'get_certificate_path_of_key'
538 +
539 + 6 return InstallCertificateResult::Accepted;
540 + } else {
541 + return InstallCertificateResult::WriteError;
542 + }
543 +
544 +
+ 0/2 +
+
✗ Branch 14 not taken.
+
✗ Branch 15 not taken.
+
+
+
12 } catch (const CertificateLoadException& e) {
545 + EVLOG_warning << "Could not load update leaf certificate because of invalid format";
546 + return InstallCertificateResult::InvalidFormat;
547 + }
548 +
549 + return InstallCertificateResult::Accepted;
550 + 12}
551 +
552 + GetInstalledCertificatesResult EvseSecurity::get_installed_certificate(CertificateType certificate_type) {
553 + return get_installed_certificates({certificate_type});
554 + }
555 +
556 + GetInstalledCertificatesResult
557 + 16EvseSecurity::get_installed_certificates(const std::vector<CertificateType>& certificate_types) {
558 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
559 +
560 + 16 GetInstalledCertificatesResult result;
561 + 16 std::vector<CertificateHashDataChain> certificate_chains;
562 +
+ 2/4 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
+
+
16 const auto ca_certificate_types = get_ca_certificate_types(certificate_types);
563 +
564 + // retrieve ca certificates and chains
565 +
+ 2/2 +
+
✓ Branch 5 taken 40 times.
+
✓ Branch 6 taken 16 times.
+
+
+
56 for (const auto& ca_certificate_type : ca_certificate_types) {
566 +
+ 2/4 +
+
✓ Branch 1 taken 40 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 40 times.
+
✗ Branch 5 not taken.
+
+
+
40 auto ca_bundle_path = this->ca_bundle_path_map.at(ca_certificate_type);
567 + try {
568 +
+ 1/2 +
+
✓ Branch 1 taken 40 times.
+
✗ Branch 2 not taken.
+
+
+
40 X509CertificateBundle ca_bundle(ca_bundle_path, EncodingFormat::PEM);
569 +
+ 1/2 +
+
✓ Branch 1 taken 40 times.
+
✗ Branch 2 not taken.
+
+
+
40 X509CertificateHierarchy& hierarchy = ca_bundle.get_certificate_hierarchy();
570 +
571 +
+ 14/26 +
+
✓ Branch 1 taken 40 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 40 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 40 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 40 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 40 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 40 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 40 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 40 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 40 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 40 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 40 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 40 times.
+
✗ Branch 36 not taken.
+
✓ Branch 38 taken 40 times.
+
✓ Branch 39 taken 40 times.
+
+
+
120 EVLOG_debug << "Hierarchy:(" << conversions::ca_certificate_type_to_string(ca_certificate_type) << ")\n"
572 +
+ 3/6 +
+
✓ Branch 1 taken 40 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 40 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 40 times.
+
✗ Branch 8 not taken.
+
+
+
80 << hierarchy.to_debug_string();
573 +
574 + // Iterate the hierarchy and add all the certificates to their respective locations
575 +
+ 2/2 +
+
✓ Branch 6 taken 50 times.
+
✓ Branch 7 taken 40 times.
+
+
+
90 for (auto& root : hierarchy.get_hierarchy()) {
576 + 50 CertificateHashDataChain certificate_hash_data_chain;
577 +
578 + 50 certificate_hash_data_chain.certificate_type =
579 +
+ 1/2 +
+
✓ Branch 1 taken 50 times.
+
✗ Branch 2 not taken.
+
+
+
50 get_certificate_type(ca_certificate_type); // We always know type
580 +
+ 1/2 +
+
✓ Branch 1 taken 50 times.
+
✗ Branch 2 not taken.
+
+
+
50 certificate_hash_data_chain.certificate_hash_data = root.hash;
581 +
582 + // Add all owned children/certificates in order
583 +
+ 1/2 +
+
✓ Branch 1 taken 50 times.
+
✗ Branch 2 not taken.
+
+
+
50 X509CertificateHierarchy::for_each_descendant(
584 + 74 [&certificate_hash_data_chain](const X509Node& child, int depth) {
585 + 74 certificate_hash_data_chain.child_certificate_hash_data.push_back(child.hash);
586 + 74 },
587 + root);
588 +
589 + // Add to our chains
590 +
+ 1/2 +
+
✓ Branch 1 taken 50 times.
+
✗ Branch 2 not taken.
+
+
+
50 certificate_chains.push_back(certificate_hash_data_chain);
591 + 50 }
592 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
40 } catch (const CertificateLoadException& e) {
593 + EVLOG_warning << "Could not load CA bundle file at: " << ca_bundle_path << " error: " << e.what();
594 + }
595 + 40 }
596 +
597 + // retrieve v2g certificate chain
598 +
+ 1/2 +
+
✓ Branch 3 taken 16 times.
+
✗ Branch 4 not taken.
+
+
+
16 if (std::find(certificate_types.begin(), certificate_types.end(), CertificateType::V2GCertificateChain) !=
599 +
+ 2/2 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 8 times.
+
+
+
32 certificate_types.end()) {
600 +
601 + // Internal since we already acquired the lock
602 + const auto secc_key_pair =
603 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 this->get_leaf_certificate_info_internal(LeafCertificateType::V2G, EncodingFormat::PEM);
604 +
+ 1/2 +
+
✓ Branch 0 taken 8 times.
+
✗ Branch 1 not taken.
+
+
+
8 if (secc_key_pair.status == GetCertificateInfoStatus::Accepted) {
605 + 8 fs::path certificate_path;
606 +
607 +
+ 2/4 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
+
+
8 if (secc_key_pair.info.value().certificate.has_value())
608 +
+ 3/6 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 8 times.
+
✗ Branch 8 not taken.
+
+
+
8 certificate_path = secc_key_pair.info.value().certificate.value();
609 + else
610 + certificate_path = secc_key_pair.info.value().certificate_single.value();
611 +
612 + try {
613 + // Leaf V2G chain
614 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 X509CertificateBundle leaf_bundle(certificate_path, EncodingFormat::PEM);
615 +
616 + // V2G chain
617 +
+ 2/4 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
+
+
8 const auto ca_bundle_path = this->ca_bundle_path_map.at(CaCertificateType::V2G);
618 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 X509CertificateBundle ca_bundle(ca_bundle_path, EncodingFormat::PEM);
619 +
620 + // Merge the bundles
621 +
+ 3/4 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 8 taken 24 times.
+
✓ Branch 9 taken 8 times.
+
+
+
32 for (auto& certif : leaf_bundle.split()) {
622 +
+ 1/2 +
+
✓ Branch 2 taken 24 times.
+
✗ Branch 3 not taken.
+
+
+
24 ca_bundle.add_certificate_unique(std::move(certif));
623 + 8 }
624 +
625 + // Create the certificate hierarchy
626 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 X509CertificateHierarchy& hierarchy = ca_bundle.get_certificate_hierarchy();
627 +
+ 14/26 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 8 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 8 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 8 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 8 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 8 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 8 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 8 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 8 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 8 times.
+
✗ Branch 33 not taken.
+
✓ Branch 36 taken 8 times.
+
✗ Branch 37 not taken.
+
✓ Branch 39 taken 8 times.
+
✓ Branch 40 taken 8 times.
+
+
+
16 EVLOG_debug << "Hierarchy:(V2GCertificateChain)\n" << hierarchy.to_debug_string();
628 +
629 +
+ 2/2 +
+
✓ Branch 6 taken 12 times.
+
✓ Branch 7 taken 8 times.
+
+
+
20 for (auto& root : hierarchy.get_hierarchy()) {
630 + 12 CertificateHashDataChain certificate_hash_data_chain;
631 +
632 + 12 certificate_hash_data_chain.certificate_type = CertificateType::V2GCertificateChain;
633 +
634 + // Since the hierarchy starts with V2G and SubCa1/SubCa2 we have to add:
635 + // the leaf as the first when returning
636 + // * Leaf
637 + // --- SubCa1
638 + // --- SubCa2
639 + 12 std::vector<CertificateHashData> hierarchy_hash_data;
640 +
641 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 X509CertificateHierarchy::for_each_descendant(
642 + 26 [&](const X509Node& child, int depth) { hierarchy_hash_data.push_back(child.hash); }, root);
643 +
644 +
+ 2/2 +
+
✓ Branch 1 taken 10 times.
+
✓ Branch 2 taken 2 times.
+
+
+
12 if (hierarchy_hash_data.size()) {
645 + // Leaf is the last
646 +
+ 1/2 +
+
✓ Branch 2 taken 10 times.
+
✗ Branch 3 not taken.
+
+
+
10 certificate_hash_data_chain.certificate_hash_data = hierarchy_hash_data.back();
647 + 10 hierarchy_hash_data.pop_back();
648 +
649 + // Add others in order, except last
650 +
+ 2/2 +
+
✓ Branch 5 taken 16 times.
+
✓ Branch 6 taken 10 times.
+
+
+
26 for (const auto& hash_data : hierarchy_hash_data) {
651 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 certificate_hash_data_chain.child_certificate_hash_data.push_back(hash_data);
652 + }
653 +
654 + // Add to our chains
655 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 certificate_chains.push_back(certificate_hash_data_chain);
656 + }
657 + 12 }
658 +
+ 0/2 +
+
✗ Branch 6 not taken.
+
✗ Branch 7 not taken.
+
+
+
8 } catch (const CertificateLoadException& e) {
659 + EVLOG_error << "Could not load installed leaf certificates: " << e.what();
660 + }
661 + 8 }
662 + 8 }
663 +
664 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 16 times.
+
+
+
16 if (certificate_chains.empty()) {
665 + result.status = GetInstalledCertificatesStatus::NotFound;
666 + } else {
667 + 16 result.status = GetInstalledCertificatesStatus::Accepted;
668 + }
669 +
670 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 result.certificate_hash_data_chain = certificate_chains;
671 + 32 return result;
672 +
+ 0/2 +
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
+
+
16}
673 +
674 + 8int EvseSecurity::get_count_of_installed_certificates(const std::vector<CertificateType>& certificate_types) {
675 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
676 +
677 + 8 int count = 0;
678 +
679 + 8 std::set<fs::path> directories;
680 +
+ 2/4 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
+
+
8 const auto ca_certificate_types = get_ca_certificate_types(certificate_types);
681 +
682 + // Collect unique directories
683 +
+ 2/2 +
+
✓ Branch 5 taken 6 times.
+
✓ Branch 6 taken 8 times.
+
+
+
14 for (const auto& ca_certificate_type : ca_certificate_types) {
684 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
+
+
6 directories.emplace(this->ca_bundle_path_map.at(ca_certificate_type));
685 + }
686 +
687 +
+ 2/2 +
+
✓ Branch 5 taken 6 times.
+
✓ Branch 6 taken 8 times.
+
+
+
14 for (const auto& unique_dir : directories) {
688 + try {
689 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 X509CertificateBundle ca_bundle(unique_dir, EncodingFormat::PEM);
690 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 count += ca_bundle.get_certificate_count();
691 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
6 } catch (const CertificateLoadException& e) {
692 + EVLOG_error << "Could not load bundle for certificate count: " << e.what();
693 + }
694 + }
695 +
696 + // V2G Chain
697 +
+ 1/2 +
+
✓ Branch 3 taken 8 times.
+
✗ Branch 4 not taken.
+
+
+
8 if (std::find(certificate_types.begin(), certificate_types.end(), CertificateType::V2GCertificateChain) !=
698 +
+ 2/2 +
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 6 times.
+
+
+
16 certificate_types.end()) {
699 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 auto leaf_dir = this->directories.secc_leaf_cert_directory;
700 +
701 + // Load all from chain, including expired/unused
702 + try {
703 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 X509CertificateBundle leaf_bundle(leaf_dir, EncodingFormat::PEM);
704 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 count += leaf_bundle.get_certificate_count();
705 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
2 } catch (const CertificateLoadException& e) {
706 + EVLOG_error << "Could not load bundle for certificate count: " << e.what();
707 + }
708 + 2 }
709 +
710 + 8 return count;
711 + 8}
712 +
713 + 4OCSPRequestDataList EvseSecurity::get_v2g_ocsp_request_data() {
714 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
715 +
716 + try {
717 + const auto secc_key_pair =
718 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 this->get_leaf_certificate_info_internal(LeafCertificateType::V2G, EncodingFormat::PEM);
719 +
720 +
+ 3/6 +
+
✓ Branch 0 taken 4 times.
+
✗ Branch 1 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 4 times.
+
+
+
4 if (secc_key_pair.status != GetCertificateInfoStatus::Accepted or !secc_key_pair.info.has_value()) {
721 + EVLOG_error << "Could not get key pair, for v2g ocsp request!";
722 + return OCSPRequestDataList();
723 + }
724 +
725 + 4 std::vector<X509Wrapper> chain;
726 +
727 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 if (secc_key_pair.info.value().certificate.has_value()) {
728 + 4 chain = std::move(
729 +
+ 4/8 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
+
+
8 X509CertificateBundle(secc_key_pair.info.value().certificate.value(), EncodingFormat::PEM).split());
730 + } else if (secc_key_pair.info.value().certificate_single.has_value()) {
731 + chain = std::move(
732 + X509CertificateBundle(secc_key_pair.info.value().certificate_single.value(), EncodingFormat::PEM)
733 + .split());
734 + } else {
735 + EVLOG_error << "Could not load v2g ocsp cache leaf chain!";
736 + }
737 +
738 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 if (!chain.empty()) {
739 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 return get_ocsp_request_data_internal(this->ca_bundle_path_map.at(CaCertificateType::V2G), chain);
740 + }
741 +
+ 2/8 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 4 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 4 times.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
✗ Branch 11 not taken.
+
✗ Branch 12 not taken.
+
+
+
8 } catch (const CertificateLoadException& e) {
742 + EVLOG_error << "Could not get v2g ocsp cache, certificate load failure: " << e.what();
743 + }
744 +
745 + return OCSPRequestDataList();
746 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
4}
747 +
748 + OCSPRequestDataList EvseSecurity::get_mo_ocsp_request_data(const std::string& certificate_chain) {
749 + std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
750 +
751 + try {
752 + std::vector<X509Wrapper> chain =
753 + std::move(X509CertificateBundle(certificate_chain, EncodingFormat::PEM).split());
754 +
755 + // Find the MO root
756 + return get_ocsp_request_data_internal(this->ca_bundle_path_map.at(CaCertificateType::MO), chain);
757 + } catch (const CertificateLoadException& e) {
758 + EVLOG_error << "Could not get mo ocsp cache, certificate load failure: " << e.what();
759 + }
760 +
761 + return OCSPRequestDataList();
762 + }
763 +
764 + 4OCSPRequestDataList get_ocsp_request_data_internal(fs::path& root_path, std::vector<X509Wrapper>& leaf_chain) {
765 + 4 OCSPRequestDataList response;
766 + 4 std::vector<OCSPRequestData> ocsp_request_data_list;
767 +
768 + try {
769 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 std::vector<X509Wrapper> full_hierarchy = X509CertificateBundle(root_path, EncodingFormat::PEM).split();
770 +
771 + // Build the full hierarchy
772 +
+ 3/6 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
+
+
4 auto hierarchy = std::move(X509CertificateHierarchy::build_hierarchy(full_hierarchy, leaf_chain));
773 +
774 + // Search for the first valid root, and collect all the chain
775 +
+ 1/2 +
+
✓ Branch 6 taken 4 times.
+
✗ Branch 7 not taken.
+
+
+
4 for (auto& root : hierarchy.get_hierarchy()) {
776 +
+ 5/10 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 4 times.
+
✗ Branch 4 not taken.
+
✓ Branch 6 taken 4 times.
+
✗ Branch 7 not taken.
+
✓ Branch 8 taken 4 times.
+
✗ Branch 9 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
+
+
4 if (root.certificate.is_selfsigned() && root.certificate.is_valid()) {
777 + // Collect the chain
778 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 std::vector<X509Wrapper> descendants = hierarchy.collect_descendants(root.certificate);
779 + 4 bool has_proper_descendants = (descendants.size() > 0);
780 +
781 +
+ 2/2 +
+
✓ Branch 5 taken 20 times.
+
✓ Branch 6 taken 4 times.
+
+
+
24 for (auto& certificate : descendants) {
782 +
+ 1/2 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
+
+
20 std::string responder_url = certificate.get_responder_url();
783 +
784 +
+ 2/2 +
+
✓ Branch 1 taken 16 times.
+
✓ Branch 2 taken 4 times.
+
+
+
20 if (!responder_url.empty()) {
785 + try {
786 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 auto certificate_hash_data = hierarchy.get_certificate_hash(certificate);
787 +
788 + // Do not insert duplicate hashes, in case we have multiple SUBCAs in different bundles
789 + auto it =
790 +
+ 1/2 +
+
✓ Branch 3 taken 16 times.
+
✗ Branch 4 not taken.
+
+
+
16 std::find_if(std::begin(ocsp_request_data_list), std::end(ocsp_request_data_list),
791 + 16 [&certificate_hash_data](const OCSPRequestData& existing_data) {
792 + 16 return existing_data.certificate_hash_data == certificate_hash_data;
793 + });
794 +
795 +
+ 2/2 +
+
✓ Branch 2 taken 8 times.
+
✓ Branch 3 taken 8 times.
+
+
+
16 if (it == ocsp_request_data_list.end()) {
796 +
+ 2/6 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
✗ Branch 6 not taken.
+
✗ Branch 7 not taken.
+
+
+
8 OCSPRequestData ocsp_request_data = {certificate_hash_data, responder_url};
797 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 ocsp_request_data_list.push_back(ocsp_request_data);
798 + 8 }
799 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
16 } catch (const NoCertificateFound& e) {
800 + EVLOG_error << "Could not find hash for certificate: " << certificate.get_common_name()
801 + << " with error: " << e.what();
802 + }
803 + }
804 + 20 }
805 +
806 + // If we have collected the descendants we can break
807 + // else we can continue iterating for a proper root
808 +
+ 1/2 +
+
✓ Branch 0 taken 4 times.
+
✗ Branch 1 not taken.
+
+
+
4 if (has_proper_descendants) {
809 + 4 break;
810 + }
811 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 4 times.
+
+
+
4 }
812 + }
813 +
814 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 response.ocsp_request_data_list = ocsp_request_data_list;
815 +
+ 0/3 +
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 6 not taken.
+
+
+
4 } catch (const CertificateLoadException& e) {
816 + EVLOG_error << "Could not get ocsp cache, certificate load failure: " << e.what();
817 + } catch (const NoCertificateFound& e) {
818 + EVLOG_error << "Could not find proper root: " << e.what();
819 + }
820 +
821 + 8 return response;
822 +
+ 0/2 +
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
+
+
4}
823 +
824 + 12void EvseSecurity::update_ocsp_cache(const CertificateHashData& certificate_hash_data,
825 + const std::string& ocsp_response) {
826 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
827 +
828 +
+ 9/16 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 12 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 12 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 12 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 12 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 12 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 12 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 12 times.
+
✓ Branch 24 taken 12 times.
+
+
+
24 EVLOG_info << "Updating OCSP cache";
829 +
830 + // TODO(ioan): shouldn't we also do this for the MO?
831 +
+ 2/4 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 12 times.
+
✗ Branch 5 not taken.
+
+
+
12 const auto ca_bundle_path = this->ca_bundle_path_map.at(CaCertificateType::V2G);
832 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 auto leaf_cert_dir = this->directories.secc_leaf_cert_directory; // V2G leafs
833 +
834 + try {
835 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 X509CertificateBundle ca_bundle(ca_bundle_path, EncodingFormat::PEM);
836 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 X509CertificateBundle leaf_bundle(leaf_cert_dir, EncodingFormat::PEM);
837 +
838 + auto certificate_hierarchy =
839 +
+ 3/6 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 12 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 12 times.
+
✗ Branch 8 not taken.
+
+
+
12 std::move(X509CertificateHierarchy::build_hierarchy(ca_bundle.split(), leaf_bundle.split()));
840 +
841 + // If we already have the hash, over-write, else create a new one
842 + try {
843 + // Find the certificates, can me multiple if we have SUBcas in multiple bundles
844 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 std::vector<X509Wrapper> certs = certificate_hierarchy.find_certificates_multi(certificate_hash_data);
845 +
846 +
+ 2/2 +
+
✓ Branch 5 taken 20 times.
+
✓ Branch 6 taken 8 times.
+
+
+
28 for (auto& cert : certs) {
847 +
+ 12/22 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 20 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 20 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 20 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 20 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 20 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 20 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 20 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 20 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 20 times.
+
✓ Branch 33 taken 20 times.
+
+
+
40 EVLOG_debug << "Writing OCSP Response to filesystem";
848 +
+ 2/4 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 20 times.
+
✗ Branch 6 not taken.
+
+
+
20 if (cert.get_file().has_value()) {
849 +
+ 5/10 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 20 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 20 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 20 times.
+
✗ Branch 14 not taken.
+
+
+
20 const auto ocsp_path = cert.get_file().value().parent_path() / "ocsp";
850 +
851 +
+ 3/4 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 8 times.
+
✓ Branch 4 taken 12 times.
+
+
+
20 if (false == fs::exists(ocsp_path)) {
852 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 filesystem_utils::create_file_or_dir_if_nonexistent(ocsp_path);
853 + } else {
854 + // Iterate existing hashes
855 +
+ 4/6 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✓ Branch 11 taken 25 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 29 times.
+
✓ Branch 15 taken 8 times.
+
+
+
37 for (const auto& hash_entry : fs::directory_iterator(ocsp_path)) {
856 +
+ 2/4 +
+
✓ Branch 1 taken 29 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 29 times.
+
✗ Branch 4 not taken.
+
+
+
29 if (hash_entry.is_regular_file()) {
857 + 29 CertificateHashData read_hash;
858 +
859 +
+ 7/8 +
+
✓ Branch 2 taken 29 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 14 times.
+
✓ Branch 5 taken 15 times.
+
✓ Branch 6 taken 4 times.
+
✓ Branch 7 taken 10 times.
+
✓ Branch 8 taken 4 times.
+
✓ Branch 9 taken 25 times.
+
+
+
43 if (filesystem_utils::read_hash_from_file(hash_entry.path(), read_hash) &&
860 + 14 read_hash == certificate_hash_data) {
861 +
+ 12/22 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 4 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 4 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 4 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 4 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 4 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 4 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 4 times.
+
✓ Branch 33 taken 4 times.
+
+
+
8 EVLOG_debug << "OCSP certificate hash already found, over-writing!";
862 +
863 + // Over-write the data file and return
864 +
+ 1/2 +
+
✓ Branch 2 taken 4 times.
+
✗ Branch 3 not taken.
+
+
+
4 fs::path ocsp_path = hash_entry.path();
865 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 ocsp_path.replace_extension(DER_EXTENSION);
866 +
867 + // Discard previous content
868 +
+ 1/2 +
+
✓ Branch 2 taken 4 times.
+
✗ Branch 3 not taken.
+
+
+
4 std::ofstream fs(ocsp_path.c_str(), std::ios::trunc);
869 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 fs << ocsp_response;
870 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 fs.close();
871 +
872 + 4 return;
873 + 4 }
874 +
+ 2/2 +
+
✓ Branch 1 taken 25 times.
+
✓ Branch 2 taken 4 times.
+
+
+
29 }
875 +
+ 6/6 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 4 times.
+
✓ Branch 4 taken 8 times.
+
✓ Branch 5 taken 4 times.
+
✓ Branch 7 taken 8 times.
+
✓ Branch 8 taken 4 times.
+
+
+
20 }
876 + }
877 +
878 + // Randomize filename, since multiple certificates can be stored in same bundle
879 +
+ 2/4 +
+
✓ Branch 2 taken 16 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 16 times.
+
✗ Branch 6 not taken.
+
+
+
16 const auto name = filesystem_utils::get_random_file_name("_ocsp");
880 +
881 +
+ 4/8 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 16 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 16 times.
+
✗ Branch 11 not taken.
+
+
+
16 const auto ocsp_file_path = (ocsp_path / name) += DER_EXTENSION;
882 +
+ 4/8 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 16 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 16 times.
+
✗ Branch 11 not taken.
+
+
+
16 const auto hash_file_path = (ocsp_path / name) += CERT_HASH_EXTENSION;
883 +
884 + // Write out OCSP data
885 + try {
886 +
+ 1/2 +
+
✓ Branch 2 taken 16 times.
+
✗ Branch 3 not taken.
+
+
+
16 std::ofstream fs(ocsp_file_path.c_str());
887 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 fs << ocsp_response;
888 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 fs.close();
889 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
16 } catch (const std::exception& e) {
890 + EVLOG_error << "Could not write OCSP certificate data!";
891 + }
892 +
893 +
+ 2/4 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 16 times.
+
+
+
16 if (false == filesystem_utils::write_hash_to_file(hash_file_path, certificate_hash_data)) {
894 + EVLOG_error << "Could not write OCSP certificate hash!";
895 + }
896 +
897 +
+ 13/24 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 16 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 16 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 16 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 16 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 16 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 16 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 16 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 16 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 16 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 16 times.
+
✓ Branch 36 taken 16 times.
+
+
+
32 EVLOG_debug << "OCSP certificate hash not found, written at path: " << ocsp_file_path;
898 +
+ 2/2 +
+
✓ Branch 4 taken 16 times.
+
✓ Branch 5 taken 4 times.
+
+
+
20 } else {
899 + EVLOG_error << "Could not find OCSP cache patch directory!";
900 + }
901 + }
902 +
+ 2/4 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 4 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
+
+
12 } catch (const NoCertificateFound& e) {
903 + EVLOG_error << "Could not find any certificate for ocsp cache update: " << e.what();
904 + }
905 +
+ 6/8 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 4 times.
+
✓ Branch 4 taken 8 times.
+
✓ Branch 5 taken 4 times.
+
✓ Branch 7 taken 8 times.
+
✓ Branch 8 taken 4 times.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
+
+
20 } catch (const CertificateLoadException& e) {
906 + EVLOG_error << "Could not update ocsp cache, certificate load failure: " << e.what();
907 + }
908 +
+ 6/6 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 4 times.
+
✓ Branch 4 taken 8 times.
+
✓ Branch 5 taken 4 times.
+
✓ Branch 7 taken 8 times.
+
✓ Branch 8 taken 4 times.
+
+
+
20}
909 +
910 + 12std::optional<fs::path> EvseSecurity::retrieve_ocsp_cache(const CertificateHashData& certificate_hash_data) {
911 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
912 +
913 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
24 return retrieve_ocsp_cache_internal(certificate_hash_data);
914 + 12}
915 +
916 + 18std::optional<fs::path> EvseSecurity::retrieve_ocsp_cache_internal(const CertificateHashData& certificate_hash_data) {
917 + // TODO(ioan): shouldn't we also do this for the MO?
918 +
+ 2/4 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
+
+
18 const auto ca_bundle_path = this->ca_bundle_path_map.at(CaCertificateType::V2G);
919 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 const auto leaf_path = this->directories.secc_leaf_key_directory;
920 +
921 + try {
922 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 X509CertificateBundle ca_bundle(ca_bundle_path, EncodingFormat::PEM);
923 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 X509CertificateBundle leaf_bundle(leaf_path, EncodingFormat::PEM);
924 +
925 + auto certificate_hierarchy =
926 +
+ 3/6 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 18 times.
+
✗ Branch 8 not taken.
+
+
+
18 std::move(X509CertificateHierarchy::build_hierarchy(ca_bundle.split(), leaf_bundle.split()));
927 +
928 + try {
929 + // Find the certificate
930 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 X509Wrapper cert = certificate_hierarchy.find_certificate(certificate_hash_data);
931 +
932 +
+ 12/22 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 18 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 18 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 18 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 18 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 18 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 18 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 18 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 18 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 18 times.
+
✓ Branch 33 taken 18 times.
+
+
+
36 EVLOG_debug << "Reading OCSP Response from filesystem";
933 +
934 +
+ 2/4 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 18 times.
+
✗ Branch 6 not taken.
+
+
+
18 if (cert.get_file().has_value()) {
935 +
+ 5/10 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 18 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 18 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 18 times.
+
✗ Branch 14 not taken.
+
+
+
18 const auto ocsp_path = cert.get_file().value().parent_path() / "ocsp";
936 +
937 + // Search through the OCSP directory and see if we can find any related certificate hash data
938 +
+ 4/6 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 11 taken 41 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 57 times.
+
✓ Branch 15 taken 2 times.
+
+
+
59 for (const auto& ocsp_entry : fs::directory_iterator(ocsp_path)) {
939 +
+ 2/4 +
+
✓ Branch 1 taken 57 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 57 times.
+
✗ Branch 4 not taken.
+
+
+
57 if (ocsp_entry.is_regular_file()) {
940 + 57 CertificateHashData read_hash;
941 +
942 +
+ 7/8 +
+
✓ Branch 2 taken 57 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 28 times.
+
✓ Branch 5 taken 29 times.
+
✓ Branch 6 taken 16 times.
+
✓ Branch 7 taken 12 times.
+
✓ Branch 8 taken 16 times.
+
✓ Branch 9 taken 41 times.
+
+
+
85 if (filesystem_utils::read_hash_from_file(ocsp_entry.path(), read_hash) &&
943 + 28 (read_hash == certificate_hash_data)) {
944 +
+ 1/2 +
+
✓ Branch 2 taken 16 times.
+
✗ Branch 3 not taken.
+
+
+
16 fs::path replaced_ext = ocsp_entry.path();
945 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 replaced_ext.replace_extension(DER_EXTENSION);
946 +
947 + // Return the data file's path
948 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 return std::make_optional<fs::path>(replaced_ext);
949 + 16 }
950 +
+ 2/2 +
+
✓ Branch 1 taken 41 times.
+
✓ Branch 2 taken 16 times.
+
+
+
57 }
951 +
+ 6/6 +
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 16 times.
+
✓ Branch 4 taken 2 times.
+
✓ Branch 5 taken 16 times.
+
✓ Branch 7 taken 2 times.
+
✓ Branch 8 taken 16 times.
+
+
+
50 }
952 +
+ 2/2 +
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 16 times.
+
+
+
18 }
953 +
+ 2/7 +
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 16 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
+
+
18 } catch (const NoCertificateFound& e) {
954 + EVLOG_error << "Could not find any certificate for ocsp cache retrieve: " << e.what();
955 + } catch (const std::filesystem::filesystem_error& e) {
956 + EVLOG_error << "Could not iterate over ocsp cache: " << e.what();
957 + }
958 +
+ 6/10 +
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 16 times.
+
✓ Branch 4 taken 2 times.
+
✓ Branch 5 taken 16 times.
+
✓ Branch 7 taken 2 times.
+
✓ Branch 8 taken 16 times.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 15 not taken.
+
✗ Branch 16 not taken.
+
+
+
50 } catch (const CertificateLoadException& e) {
959 + EVLOG_error << "Could not retrieve ocsp cache, certificate load failure: " << e.what();
960 + }
961 +
962 + 2 return std::nullopt;
963 +
+ 0/2 +
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
+
+
18}
964 +
965 + bool EvseSecurity::is_ca_certificate_installed(CaCertificateType certificate_type) {
966 + std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
967 +
968 + return is_ca_certificate_installed_internal(certificate_type);
969 + }
970 +
971 + 14bool EvseSecurity::is_ca_certificate_installed_internal(CaCertificateType certificate_type) {
972 + try {
973 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
+
+
14 X509CertificateBundle bundle(this->ca_bundle_path_map.at(certificate_type), EncodingFormat::PEM);
974 +
975 + // Search for a valid self-signed root
976 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 auto& hierarchy = bundle.get_certificate_hierarchy();
977 +
978 + // Get all roots and search for a valid self-signed
979 +
+ 1/2 +
+
✓ Branch 6 taken 14 times.
+
✗ Branch 7 not taken.
+
+
+
14 for (auto& root : hierarchy.get_hierarchy()) {
980 +
+ 5/10 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 14 times.
+
✗ Branch 4 not taken.
+
✓ Branch 6 taken 14 times.
+
✗ Branch 7 not taken.
+
✓ Branch 8 taken 14 times.
+
✗ Branch 9 not taken.
+
✓ Branch 10 taken 14 times.
+
✗ Branch 11 not taken.
+
+
+
14 if (root.certificate.is_selfsigned() && root.certificate.is_valid()) {
981 + 14 return true;
982 + }
983 + }
984 +
+ 1/4 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 14 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
+
+
14 } catch (const CertificateLoadException& e) {
985 + EVLOG_error << "Could not load ca certificate type:"
986 + << conversions::ca_certificate_type_to_string(certificate_type);
987 + return false;
988 + }
989 +
990 + return false;
991 + }
992 +
993 + void EvseSecurity::certificate_signing_request_failed(const std::string& csr, LeafCertificateType certificate_type) {
994 + // TODO(ioan): delete the pairing key of the CSR
995 + }
996 +
997 + GetCertificateSignRequestResult
998 + 4EvseSecurity::generate_certificate_signing_request_internal(LeafCertificateType certificate_type,
999 + const CertificateSigningRequestInfo& info) {
1000 + 4 GetCertificateSignRequestResult result{};
1001 +
1002 +
+ 11/20 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 4 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 4 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 4 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 4 times.
+
✗ Branch 24 not taken.
+
✓ Branch 27 taken 4 times.
+
✗ Branch 28 not taken.
+
✓ Branch 30 taken 4 times.
+
✓ Branch 31 taken 4 times.
+
+
+
8 EVLOG_info << "Generating CSR for leaf: " << conversions::leaf_certificate_type_to_string(certificate_type);
1003 +
1004 + 4 std::string csr;
1005 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 CertificateSignRequestResult csr_result = CryptoSupplier::x509_generate_csr(info, csr);
1006 +
1007 +
+ 1/2 +
+
✓ Branch 0 taken 4 times.
+
✗ Branch 1 not taken.
+
+
+
4 if (csr_result == CertificateSignRequestResult::Valid) {
1008 + 4 result.status = GetCertificateSignRequestStatus::Accepted;
1009 + 4 result.csr = std::move(csr);
1010 +
1011 +
+ 13/24 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 4 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 4 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 4 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 4 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 4 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 4 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 4 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 4 times.
+
✓ Branch 36 taken 4 times.
+
+
+
8 EVLOG_debug << "Generated CSR end. CSR: " << csr;
1012 +
1013 + // Add the key to the managed CRS that we will delete if we can't find a certificate pair within the time
1014 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 if (info.key_info.private_key_file.has_value()) {
1015 +
+ 2/4 +
+
✓ Branch 2 taken 4 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 4 times.
+
✗ Branch 6 not taken.
+
+
+
4 managed_csr.emplace(info.key_info.private_key_file.value(), std::chrono::steady_clock::now());
1016 + }
1017 + } else {
1018 + EVLOG_error << "CSR leaf generation error: "
1019 + << conversions::get_certificate_sign_request_result_to_string(csr_result);
1020 +
1021 + if (csr_result == CertificateSignRequestResult::KeyGenerationError) {
1022 + result.status = GetCertificateSignRequestStatus::KeyGenError;
1023 + } else {
1024 + result.status = GetCertificateSignRequestStatus::GenerationError;
1025 + }
1026 + }
1027 +
1028 + 8 return result;
1029 +
+ 0/2 +
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
+
+
4}
1030 +
1031 + 4GetCertificateSignRequestResult EvseSecurity::generate_certificate_signing_request(LeafCertificateType certificate_type,
1032 + const std::string& country,
1033 + const std::string& organization,
1034 + const std::string& common,
1035 + bool use_custom_provider) {
1036 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1037 +
1038 + // Make a difference between normal and tpm keys for identification
1039 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
8 const auto file_name = conversions::leaf_certificate_type_to_filename(certificate_type) +
1040 +
+ 3/8 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 4 times.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 6 taken 4 times.
+
✗ Branch 7 not taken.
+
✓ Branch 9 taken 4 times.
+
✗ Branch 10 not taken.
+
+
+
8 filesystem_utils::get_random_file_name(use_custom_provider ? CUSTOM_KEY_EXTENSION.string()
1041 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 : KEY_EXTENSION.string());
1042 +
1043 + 4 fs::path key_path;
1044 +
+ 1/2 +
+
✓ Branch 0 taken 4 times.
+
✗ Branch 1 not taken.
+
+
+
4 if (certificate_type == LeafCertificateType::CSMS) {
1045 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 key_path = this->directories.csms_leaf_key_directory / file_name;
1046 + } else if (certificate_type == LeafCertificateType::V2G) {
1047 + key_path = this->directories.secc_leaf_key_directory / file_name;
1048 + } else {
1049 + EVLOG_error << "Generate CSR for non CSMS/V2G leafs!";
1050 +
1051 + GetCertificateSignRequestResult result{};
1052 + result.status = GetCertificateSignRequestStatus::InvalidRequestedType;
1053 + return result;
1054 + }
1055 +
1056 + 4 std::string csr;
1057 + 4 CertificateSigningRequestInfo info;
1058 +
1059 + 4 info.n_version = 0;
1060 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 info.commonName = common;
1061 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 info.country = country;
1062 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 info.organization = organization;
1063 + #ifdef CSR_DNS_NAME
1064 + info.dns_name = CSR_DNS_NAME;
1065 + #else
1066 + 4 info.dns_name = std::nullopt;
1067 + #endif
1068 + #ifdef CSR_IP_ADDRESS
1069 + info.ip_address = CSR_IP_ADDRESS;
1070 + #else
1071 + 4 info.ip_address = std::nullopt;
1072 + #endif
1073 +
1074 + 4 info.key_info.key_type = CryptoKeyType::EC_prime256v1;
1075 + 4 info.key_info.generate_on_custom = use_custom_provider;
1076 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 info.key_info.private_key_file = key_path;
1077 +
1078 +
+ 3/6 +
+
✓ Branch 0 taken 4 times.
+
✗ Branch 1 not taken.
+
✓ Branch 3 taken 4 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 4 times.
+
✗ Branch 6 not taken.
+
+
+
4 if ((use_custom_provider == false) && private_key_password.has_value()) {
1079 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 info.key_info.private_key_pass = private_key_password;
1080 + }
1081 +
1082 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 return generate_certificate_signing_request_internal(certificate_type, info);
1083 +
+ 0/2 +
+
✗ Branch 10 not taken.
+
✗ Branch 11 not taken.
+
+
+
4}
1084 +
1085 + 4GetCertificateSignRequestResult EvseSecurity::generate_certificate_signing_request(LeafCertificateType certificate_type,
1086 + const std::string& country,
1087 + const std::string& organization,
1088 + const std::string& common) {
1089 + 4 return generate_certificate_signing_request(certificate_type, country, organization, common, false);
1090 + }
1091 +
1092 + 2GetCertificateFullInfoResult EvseSecurity::get_all_valid_certificates_info(LeafCertificateType certificate_type,
1093 + EncodingFormat encoding, bool include_ocsp) {
1094 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1095 +
1096 + GetCertificateFullInfoResult result =
1097 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 get_full_leaf_certificate_info_internal(certificate_type, encoding, include_ocsp, true, true);
1098 +
1099 + // If we failed, simply return the result
1100 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 2 times.
+
+
+
2 if (result.status != GetCertificateInfoStatus::Accepted) {
1101 + return result;
1102 + }
1103 +
1104 + 2 GetCertificateFullInfoResult filtered_results;
1105 + 2 filtered_results.status = result.status;
1106 +
1107 + // Filter the certificates to return only the ones that have a unique
1108 + // root, and from those that have a unique root, return only the newest
1109 + 2 std::set<std::string> unique_roots;
1110 +
1111 + // The newest are the first, that's how 'get_leaf_certificate_info_internal'
1112 + // returns them
1113 +
+ 2/2 +
+
✓ Branch 5 taken 4 times.
+
✓ Branch 6 taken 2 times.
+
+
+
6 for (const auto& chain : result.info) {
1114 + // Ignore non-root items
1115 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 4 times.
+
+
+
4 if (!chain.certificate_root.has_value()) {
1116 + continue;
1117 + }
1118 +
1119 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 const std::string& root = chain.certificate_root.value();
1120 +
1121 + // If we don't contain the unique root yet, it is the newest leaf for that root
1122 +
+ 2/4 +
+
✓ Branch 2 taken 4 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 4 times.
+
✗ Branch 6 not taken.
+
+
+
4 if (unique_roots.find(root) == unique_roots.end()) {
1123 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 filtered_results.info.push_back(chain);
1124 +
1125 + // Add it to the roots list, adding only unique roots
1126 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 unique_roots.insert(root);
1127 + }
1128 + }
1129 +
1130 + 2 return filtered_results;
1131 + 2}
1132 +
1133 + 6GetCertificateInfoResult EvseSecurity::get_leaf_certificate_info(LeafCertificateType certificate_type,
1134 + EncodingFormat encoding, bool include_ocsp) {
1135 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1136 +
1137 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
12 return get_leaf_certificate_info_internal(certificate_type, encoding, include_ocsp);
1138 + 6}
1139 +
1140 + 18GetCertificateInfoResult EvseSecurity::get_leaf_certificate_info_internal(LeafCertificateType certificate_type,
1141 + EncodingFormat encoding, bool include_ocsp) {
1142 + GetCertificateFullInfoResult result =
1143 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 get_full_leaf_certificate_info_internal(certificate_type, encoding, include_ocsp, false, false);
1144 + 18 GetCertificateInfoResult internal_result;
1145 +
1146 + 18 internal_result.status = result.status;
1147 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 if (!result.info.empty()) {
1148 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 internal_result.info = std::move(result.info.at(0));
1149 + }
1150 +
1151 + 36 return internal_result;
1152 + 18}
1153 +
1154 + 20GetCertificateFullInfoResult EvseSecurity::get_full_leaf_certificate_info_internal(LeafCertificateType certificate_type,
1155 + EncodingFormat encoding,
1156 + bool include_ocsp, bool include_root,
1157 + bool include_all_valid) {
1158 +
+ 7/12 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 20 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 20 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 20 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 20 times.
+
✓ Branch 18 taken 20 times.
+
+
+
60 EVLOG_info << "Requesting leaf certificate info: "
1159 +
+ 4/8 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 20 times.
+
✗ Branch 8 not taken.
+
✓ Branch 11 taken 20 times.
+
✗ Branch 12 not taken.
+
+
+
40 << conversions::leaf_certificate_type_to_string(certificate_type);
1160 +
1161 + 20 GetCertificateFullInfoResult result;
1162 +
1163 + 20 fs::path key_dir;
1164 + 20 fs::path cert_dir;
1165 + CaCertificateType root_type;
1166 +
1167 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 18 times.
+
+
+
20 if (certificate_type == LeafCertificateType::CSMS) {
1168 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 key_dir = this->directories.csms_leaf_key_directory;
1169 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 cert_dir = this->directories.csms_leaf_cert_directory;
1170 + 2 root_type = CaCertificateType::CSMS;
1171 +
+ 1/2 +
+
✓ Branch 0 taken 18 times.
+
✗ Branch 1 not taken.
+
+
+
18 } else if (certificate_type == LeafCertificateType::V2G) {
1172 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 key_dir = this->directories.secc_leaf_key_directory;
1173 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 cert_dir = this->directories.secc_leaf_cert_directory;
1174 + 18 root_type = CaCertificateType::V2G;
1175 + } else {
1176 + EVLOG_warning << "Rejected attempt to retrieve non CSMS/V2G key pair";
1177 + result.status = GetCertificateInfoStatus::Rejected;
1178 + return result;
1179 + }
1180 +
1181 +
+ 2/4 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
+
+
20 fs::path root_dir = ca_bundle_path_map[root_type];
1182 +
1183 + // choose appropriate cert (valid_from / valid_to)
1184 + try {
1185 +
+ 1/2 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
+
+
20 auto leaf_certificates = X509CertificateBundle(cert_dir, EncodingFormat::PEM);
1186 +
1187 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 20 times.
+
+
+
20 if (leaf_certificates.empty()) {
1188 + EVLOG_warning << "Could not find any key pair";
1189 + result.status = GetCertificateInfoStatus::NotFound;
1190 + return result;
1191 + }
1192 +
1193 + struct KeyPairInternal {
1194 + X509Wrapper certificate;
1195 + fs::path certificate_key;
1196 + };
1197 +
1198 + 20 std::vector<KeyPairInternal> valid_leafs;
1199 +
1200 + 20 bool any_valid_certificate = false;
1201 + 20 bool any_valid_key = false;
1202 +
1203 + // Iterate all certificates from newest to the oldest
1204 +
+ 1/2 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
+
+
20 leaf_certificates.for_each_chain_ordered(
1205 + 24 [&](const fs::path& file, const std::vector<X509Wrapper>& chain) {
1206 + // Search for the first valid where we can find a key
1207 +
+ 5/6 +
+
✓ Branch 1 taken 24 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 22 times.
+
✓ Branch 6 taken 2 times.
+
✓ Branch 7 taken 22 times.
+
✓ Branch 8 taken 2 times.
+
+
+
24 if (not chain.empty() && chain.at(0).is_valid()) {
1208 + 22 any_valid_certificate = true;
1209 +
1210 + try {
1211 + // Search for the private key
1212 + auto priv_key_path =
1213 +
+ 3/6 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 22 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 22 times.
+
✗ Branch 8 not taken.
+
+
+
22 get_private_key_path_of_certificate(chain.at(0), key_dir, this->private_key_password);
1214 +
1215 + // Found at least one valid key
1216 + 22 any_valid_key = true;
1217 +
1218 + // Copy to latest valid
1219 +
+ 3/8 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 22 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 22 times.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
✗ Branch 10 not taken.
+
+
+
22 KeyPairInternal key_pair{chain.at(0), priv_key_path};
1220 +
+ 1/2 +
+
✓ Branch 2 taken 22 times.
+
✗ Branch 3 not taken.
+
+
+
22 valid_leafs.emplace_back(std::move(key_pair));
1221 +
1222 + // We found, break
1223 +
+ 14/26 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 22 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 22 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 22 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 22 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 22 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 22 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 22 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 22 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 22 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 22 times.
+
✗ Branch 33 not taken.
+
✓ Branch 36 taken 22 times.
+
✗ Branch 37 not taken.
+
✓ Branch 39 taken 22 times.
+
✓ Branch 40 taken 22 times.
+
+
+
44 EVLOG_info << "Found valid leaf: [" << chain.at(0).get_file().value() << "]";
1224 +
1225 + // Collect all if we don't include valid only
1226 +
+ 2/2 +
+
✓ Branch 0 taken 18 times.
+
✓ Branch 1 taken 4 times.
+
+
+
22 if (include_all_valid == false) {
1227 +
+ 9/16 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 18 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 18 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 18 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 18 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 18 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 18 times.
+
✓ Branch 24 taken 18 times.
+
+
+
36 EVLOG_info << "Not requiring all valid leafs, returning";
1228 + 18 return false;
1229 + }
1230 +
+ 4/6 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 18 times.
+
✓ Branch 4 taken 4 times.
+
✓ Branch 5 taken 18 times.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
+
+
40 } catch (const NoPrivateKeyException& e) {
1231 + }
1232 + }
1233 +
1234 + 6 return true;
1235 + },
1236 + 50 [](const std::vector<X509Wrapper>& a, const std::vector<X509Wrapper>& b) {
1237 + // Order from newest to oldest
1238 +
+ 5/6 +
+
✓ Branch 1 taken 42 times.
+
✓ Branch 2 taken 8 times.
+
✓ Branch 4 taken 42 times.
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 42 times.
+
✓ Branch 7 taken 8 times.
+
+
+
50 if (not a.empty() && not b.empty()) {
1239 + 42 return a.at(0).get_valid_to() > b.at(0).get_valid_to();
1240 + } else {
1241 + 8 return false;
1242 + }
1243 + });
1244 +
1245 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 20 times.
+
+
+
20 if (!any_valid_certificate) {
1246 + EVLOG_warning << "Could not find valid certificate";
1247 + result.status = GetCertificateInfoStatus::NotFoundValid;
1248 + return result;
1249 + }
1250 +
1251 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 20 times.
+
+
+
20 if (!any_valid_key) {
1252 + EVLOG_warning << "Could not find private key for the valid certificate";
1253 + result.status = GetCertificateInfoStatus::PrivateKeyNotFound;
1254 + return result;
1255 + }
1256 +
1257 +
+ 2/2 +
+
✓ Branch 5 taken 22 times.
+
✓ Branch 6 taken 20 times.
+
+
+
42 for (const auto& valid_leaf : valid_leafs) {
1258 + // Key path doesn't change
1259 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 fs::path key_file = valid_leaf.certificate_key;
1260 + 22 auto& certificate = valid_leaf.certificate;
1261 +
1262 + // Paths to search
1263 + 22 std::optional<fs::path> certificate_file;
1264 + 22 std::optional<fs::path> chain_file;
1265 +
1266 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 X509CertificateBundle leaf_directory(cert_dir, EncodingFormat::PEM);
1267 +
1268 + 22 const std::vector<X509Wrapper>* leaf_fullchain = nullptr;
1269 + 22 const std::vector<X509Wrapper>* leaf_single = nullptr;
1270 + 22 int chain_len = 1; // Defaults to 1, single certificate
1271 +
1272 + // We are searching for both the full leaf bundle, containing the leaf and the cso1/2 and the single leaf
1273 + // without the cso1/2
1274 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 leaf_directory.for_each_chain(
1275 + 44 [&](const std::filesystem::path& path, const std::vector<X509Wrapper>& chain) {
1276 + // If we contain the latest valid, we found our generated bundle
1277 +
+ 1/2 +
+
✓ Branch 4 taken 44 times.
+
✗ Branch 5 not taken.
+
+
+
44 bool leaf_found = (std::find(chain.begin(), chain.end(), certificate) != chain.end());
1278 +
1279 +
+ 2/2 +
+
✓ Branch 0 taken 40 times.
+
✓ Branch 1 taken 4 times.
+
+
+
44 if (leaf_found) {
1280 +
+ 2/2 +
+
✓ Branch 1 taken 18 times.
+
✓ Branch 2 taken 22 times.
+
+
+
40 if (chain.size() > 1) {
1281 + 62 leaf_fullchain = &chain;
1282 + 18 chain_len = chain.size();
1283 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 } else if (chain.size() == 1) {
1284 + 22 leaf_single = &chain;
1285 + }
1286 + }
1287 +
1288 + // Found both, break
1289 +
+ 4/4 +
+
✓ Branch 0 taken 36 times.
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 18 times.
+
✓ Branch 3 taken 18 times.
+
+
+
44 if (leaf_fullchain != nullptr && leaf_single != nullptr)
1290 + 18 return false;
1291 +
1292 + 26 return true;
1293 + });
1294 +
1295 + 22 std::vector<CertificateOCSP> certificate_ocsp{};
1296 + 22 std::optional<std::string> leafs_root = std::nullopt;
1297 +
1298 + // None were found
1299 +
+ 1/4 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
22 if (leaf_single == nullptr && leaf_fullchain == nullptr) {
1300 + EVLOG_error << "Could not find any leaf certificate for:"
1301 + << conversions::leaf_certificate_type_to_string(certificate_type);
1302 + // Move onto next valid leaf, and attempt a search there
1303 + continue;
1304 + }
1305 +
1306 +
+ 2/2 +
+
✓ Branch 0 taken 18 times.
+
✓ Branch 1 taken 4 times.
+
+
+
22 if (leaf_fullchain != nullptr) {
1307 +
+ 2/4 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
+
+
18 chain_file = leaf_fullchain->at(0).get_file();
1308 +
+ 15/28 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 18 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 18 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 18 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 18 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 18 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 18 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 18 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 18 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 18 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 18 times.
+
✗ Branch 36 not taken.
+
✓ Branch 39 taken 18 times.
+
✗ Branch 40 not taken.
+
✓ Branch 42 taken 18 times.
+
✓ Branch 43 taken 18 times.
+
+
+
36 EVLOG_debug << "Leaf fullchain: [" << chain_file.value_or("INVALID") << "]";
1309 + } else {
1310 +
+ 13/24 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 4 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 4 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 4 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 4 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 4 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 4 times.
+
✗ Branch 30 not taken.
+
✓ Branch 33 taken 4 times.
+
✗ Branch 34 not taken.
+
✓ Branch 36 taken 4 times.
+
✓ Branch 37 taken 4 times.
+
+
+
12 EVLOG_debug << conversions::leaf_certificate_type_to_string(certificate_type)
1311 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
8 << " leaf requires full bundle, but full bundle not found at path: " << cert_dir;
1312 + }
1313 +
1314 +
+ 1/2 +
+
✓ Branch 0 taken 22 times.
+
✗ Branch 1 not taken.
+
+
+
22 if (leaf_single != nullptr) {
1315 +
+ 2/4 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 22 times.
+
✗ Branch 5 not taken.
+
+
+
22 certificate_file = leaf_single->at(0).get_file();
1316 +
+ 15/28 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 22 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 22 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 22 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 22 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 22 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 22 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 22 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 22 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 22 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 22 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 22 times.
+
✗ Branch 36 not taken.
+
✓ Branch 39 taken 22 times.
+
✗ Branch 40 not taken.
+
✓ Branch 42 taken 22 times.
+
✓ Branch 43 taken 22 times.
+
+
+
44 EVLOG_debug << "Leaf single: [" << certificate_file.value_or("INVALID") << "]";
1317 + } else {
1318 + EVLOG_debug << conversions::leaf_certificate_type_to_string(certificate_type)
1319 + << " single leaf not found at path: " << cert_dir;
1320 + }
1321 +
1322 + // Both require the hierarchy build
1323 +
+ 4/4 +
+
✓ Branch 0 taken 20 times.
+
✓ Branch 1 taken 2 times.
+
✓ Branch 2 taken 4 times.
+
✓ Branch 3 taken 16 times.
+
+
+
22 if (include_ocsp || include_root) {
1324 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 X509CertificateBundle root_bundle(root_dir, EncodingFormat::PEM); // Required for hierarchy
1325 +
1326 + // The hierarchy is required for both roots and the OCSP cache
1327 +
+ 3/6 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 6 times.
+
✗ Branch 8 not taken.
+
+
+
6 auto hierarchy = X509CertificateHierarchy::build_hierarchy(root_bundle.split(), leaf_directory.split());
1328 +
+ 14/26 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 6 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 6 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 6 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 6 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 6 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 6 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 6 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 6 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 6 times.
+
✗ Branch 33 not taken.
+
✓ Branch 36 taken 6 times.
+
✗ Branch 37 not taken.
+
✓ Branch 39 taken 6 times.
+
✓ Branch 40 taken 6 times.
+
+
+
12 EVLOG_debug << "Hierarchy for root/OCSP data: \n" << hierarchy.to_debug_string();
1329 +
1330 + // Include OCSP data if possible
1331 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 4 times.
+
+
+
6 if (include_ocsp) {
1332 + // Search for OCSP data for each certificate
1333 +
+ 1/2 +
+
✓ Branch 0 taken 2 times.
+
✗ Branch 1 not taken.
+
+
+
2 if (leaf_fullchain != nullptr) {
1334 +
+ 2/2 +
+
✓ Branch 5 taken 6 times.
+
✓ Branch 6 taken 2 times.
+
+
+
8 for (const auto& chain_certif : *leaf_fullchain) {
1335 + try {
1336 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 CertificateHashData hash = hierarchy.get_certificate_hash(chain_certif);
1337 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 std::optional<fs::path> data = retrieve_ocsp_cache_internal(hash);
1338 +
1339 + 6 certificate_ocsp.push_back({hash, data});
1340 +
+ 0/4 +
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
+
+
6 } catch (const NoCertificateFound& e) {
1341 + // Always add to preserve file order
1342 + certificate_ocsp.push_back({{}, std::nullopt});
1343 + }
1344 + }
1345 + } else {
1346 + try {
1347 + CertificateHashData hash = hierarchy.get_certificate_hash(leaf_single->at(0));
1348 + certificate_ocsp.push_back({hash, retrieve_ocsp_cache_internal(hash)});
1349 + } catch (const NoCertificateFound& e) {
1350 + }
1351 + }
1352 + }
1353 +
1354 + // Include root data if possible
1355 +
+ 2/2 +
+
✓ Branch 0 taken 4 times.
+
✓ Branch 1 taken 2 times.
+
+
+
6 if (include_root) {
1356 + // Search for the root of any of the leafs
1357 + // present either in the chain or single
1358 + try {
1359 + X509Wrapper leafs_root_cert = hierarchy.find_certificate_root(
1360 +
+ 3/8 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 4 times.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 6 taken 4 times.
+
✗ Branch 7 not taken.
+
✓ Branch 9 taken 4 times.
+
✗ Branch 10 not taken.
+
+
+
4 leaf_fullchain != nullptr ? leaf_fullchain->at(0) : leaf_single->at(0));
1361 +
1362 + // Append the root
1363 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 leafs_root = leafs_root_cert.get_export_string();
1364 +
+ 0/4 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 6 not taken.
+
+
+
4 } catch (const NoCertificateFound& e) {
1365 + EVLOG_warning << "Root required for ["
1366 + << conversions::leaf_certificate_type_to_string(certificate_type)
1367 + << "] leaf certificate, but no root could be found";
1368 + }
1369 + }
1370 + 6 }
1371 +
1372 + 22 CertificateInfo info;
1373 +
1374 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 info.key = key_file;
1375 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 info.certificate = chain_file;
1376 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 info.certificate_single = certificate_file;
1377 + 22 info.certificate_count = chain_len;
1378 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 info.password = this->private_key_password;
1379 +
1380 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 20 times.
+
+
+
22 if (include_ocsp) {
1381 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 info.ocsp = certificate_ocsp;
1382 + }
1383 +
1384 +
+ 5/6 +
+
✓ Branch 0 taken 4 times.
+
✓ Branch 1 taken 18 times.
+
✓ Branch 3 taken 4 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 4 times.
+
✓ Branch 6 taken 18 times.
+
+
+
22 if (include_root && leafs_root.has_value()) {
1385 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 info.certificate_root = leafs_root.value();
1386 + }
1387 +
1388 + // Add it to the returned result list
1389 +
+ 1/2 +
+
✓ Branch 1 taken 22 times.
+
✗ Branch 2 not taken.
+
+
+
22 result.info.push_back(info);
1390 + 22 result.status = GetCertificateInfoStatus::Accepted;
1391 +
+ 6/12 +
+
✓ Branch 2 taken 22 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 22 times.
+
✗ Branch 6 not taken.
+
✓ Branch 8 taken 22 times.
+
✗ Branch 9 not taken.
+
✓ Branch 11 taken 22 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 22 times.
+
✗ Branch 15 not taken.
+
✓ Branch 17 taken 22 times.
+
✗ Branch 18 not taken.
+
+
+
22 } // End valid leaf iteration
1392 +
1393 + 20 return result;
1394 +
+ 0/4 +
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
+
+
20 } catch (const CertificateLoadException& e) {
1395 + EVLOG_warning << "Leaf certificate load exception";
1396 + result.status = GetCertificateInfoStatus::NotFound;
1397 + return result;
1398 + }
1399 +
1400 + result.status = GetCertificateInfoStatus::NotFound;
1401 + return result;
1402 +
+ 3/26 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 6 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 6 times.
+
✗ Branch 8 not taken.
+
✗ Branch 10 not taken.
+
✗ Branch 11 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 14 not taken.
+
✗ Branch 16 not taken.
+
✗ Branch 17 not taken.
+
✗ Branch 21 not taken.
+
✗ Branch 22 not taken.
+
✗ Branch 28 not taken.
+
✗ Branch 29 not taken.
+
✗ Branch 30 not taken.
+
✗ Branch 31 not taken.
+
✗ Branch 33 not taken.
+
✗ Branch 34 not taken.
+
✗ Branch 36 not taken.
+
✗ Branch 37 not taken.
+
✗ Branch 39 not taken.
+
✗ Branch 40 not taken.
+
✗ Branch 46 not taken.
+
✗ Branch 47 not taken.
+
+
+
26}
1403 +
1404 + bool EvseSecurity::update_certificate_links(LeafCertificateType certificate_type) {
1405 + bool changed = false;
1406 +
1407 + if (certificate_type != LeafCertificateType::V2G) {
1408 + throw std::runtime_error("Link updating only supported for V2G certificates");
1409 + }
1410 +
1411 + std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1412 +
1413 + fs::path cert_link_path = this->links.secc_leaf_cert_link;
1414 + fs::path key_link_path = this->links.secc_leaf_key_link;
1415 + fs::path chain_link_path = this->links.cpo_cert_chain_link;
1416 +
1417 + // Get the most recent valid certificate (internal since we already locked mutex)
1418 + const auto key_pair = this->get_leaf_certificate_info_internal(certificate_type, EncodingFormat::PEM);
1419 + if ((key_pair.status == GetCertificateInfoStatus::Accepted) && key_pair.info.has_value()) {
1420 +
1421 + // Create or update symlinks to SECC leaf cert
1422 + if (!cert_link_path.empty()) {
1423 + std::optional<fs::path> cert_path = key_pair.info.value().certificate_single;
1424 +
1425 + if (cert_path.has_value()) {
1426 + if (fs::is_symlink(cert_link_path)) {
1427 + if (fs::read_symlink(cert_link_path) != cert_path.value()) {
1428 + fs::remove(cert_link_path);
1429 + changed = true;
1430 + }
1431 + }
1432 + if (!fs::exists(cert_link_path)) {
1433 + EVLOG_debug << "SECC cert link: " << cert_link_path << " -> " << cert_path.value();
1434 + fs::create_symlink(cert_path.value(), cert_link_path);
1435 + changed = true;
1436 + }
1437 + }
1438 + }
1439 +
1440 + // Create or update symlinks to SECC leaf key
1441 + if (!key_link_path.empty()) {
1442 + fs::path key_path = key_pair.info.value().key;
1443 + if (fs::is_symlink(key_link_path)) {
1444 + if (fs::read_symlink(key_link_path) != key_path) {
1445 + fs::remove(key_link_path);
1446 + changed = true;
1447 + }
1448 + }
1449 + if (!fs::exists(key_link_path)) {
1450 + EVLOG_debug << "SECC key link: " << key_link_path << " -> " << key_path;
1451 + fs::create_symlink(key_path, key_link_path);
1452 + changed = true;
1453 + }
1454 + }
1455 +
1456 + // Create or update symlinks to CPO chain
1457 + if (key_pair.info.value().certificate.has_value()) {
1458 + fs::path chain_path = key_pair.info.value().certificate.value();
1459 + if (!chain_link_path.empty()) {
1460 + if (fs::is_symlink(chain_link_path)) {
1461 + if (fs::read_symlink(chain_link_path) != chain_path) {
1462 + fs::remove(chain_link_path);
1463 + changed = true;
1464 + }
1465 + }
1466 + if (!fs::exists(chain_link_path)) {
1467 + EVLOG_debug << "CPO cert chain link: " << chain_link_path << " -> " << chain_path;
1468 + fs::create_symlink(chain_path, chain_link_path);
1469 + changed = true;
1470 + }
1471 + }
1472 + }
1473 + } else {
1474 + // Remove existing symlinks if no valid certificate is found
1475 + if (!cert_link_path.empty() && fs::is_symlink(cert_link_path)) {
1476 + fs::remove(cert_link_path);
1477 + changed = true;
1478 + }
1479 + if (!key_link_path.empty() && fs::is_symlink(key_link_path)) {
1480 + fs::remove(key_link_path);
1481 + changed = true;
1482 + }
1483 + if (!chain_link_path.empty() && fs::is_symlink(chain_link_path)) {
1484 + fs::remove(chain_link_path);
1485 + changed = true;
1486 + }
1487 + }
1488 +
1489 + return changed;
1490 + }
1491 +
1492 + 4GetCertificateInfoResult EvseSecurity::get_ca_certificate_info_internal(CaCertificateType certificate_type) {
1493 + 4 GetCertificateInfoResult result{};
1494 +
1495 + try {
1496 + // Support bundle files, in case the certificates contain
1497 + // multiple entries (should be 3) as per the specification
1498 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 X509CertificateBundle verify_file(this->ca_bundle_path_map.at(certificate_type), EncodingFormat::PEM);
1499 +
1500 +
+ 11/20 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 4 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 4 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 4 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 4 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 4 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 4 times.
+
✓ Branch 30 taken 4 times.
+
+
+
12 EVLOG_info << "Requesting certificate file: [" << conversions::ca_certificate_type_to_string(certificate_type)
1501 +
+ 3/6 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
+
+
8 << "] file:" << verify_file.get_path();
1502 +
1503 + // If we are using a directory, search for the first valid root file
1504 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 4 times.
+
+
+
4 if (verify_file.is_using_directory()) {
1505 + auto& hierarchy = verify_file.get_certificate_hierarchy();
1506 +
1507 + // Get all roots and search for a valid self-signed
1508 + for (auto& root : hierarchy.get_hierarchy()) {
1509 + if (root.certificate.is_selfsigned() && root.certificate.is_valid()) {
1510 + CertificateInfo info;
1511 + info.certificate = root.certificate.get_file().value();
1512 + info.certificate_single = root.certificate.get_file().value();
1513 +
1514 + result.info = info;
1515 + result.status = GetCertificateInfoStatus::Accepted;
1516 + return result;
1517 + }
1518 + }
1519 + } else {
1520 + 4 CertificateInfo info;
1521 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 info.certificate = verify_file.get_path();
1522 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 info.certificate_single = verify_file.get_path();
1523 +
1524 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 result.info = info;
1525 + 4 result.status = GetCertificateInfoStatus::Accepted;
1526 + 4 return result;
1527 + 4 }
1528 +
+ 1/6 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 4 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
+
+
4 } catch (const CertificateLoadException& e) {
1529 + EVLOG_error << "Could not obtain verify file, wrong format for certificate: "
1530 + << this->ca_bundle_path_map.at(certificate_type) << " with error: " << e.what();
1531 + }
1532 +
1533 + EVLOG_error << "Could not find any CA certificate for: "
1534 + << conversions::ca_certificate_type_to_string(certificate_type);
1535 +
1536 + result.status = GetCertificateInfoStatus::NotFound;
1537 + return result;
1538 + }
1539 +
1540 + GetCertificateInfoResult EvseSecurity::get_ca_certificate_info(CaCertificateType certificate_type) {
1541 + std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1542 +
1543 + return get_ca_certificate_info_internal(certificate_type);
1544 + }
1545 +
1546 + 4std::string EvseSecurity::get_verify_file(CaCertificateType certificate_type) {
1547 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1548 +
1549 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 auto result = get_ca_certificate_info_internal(certificate_type);
1550 +
1551 +
+ 3/6 +
+
✓ Branch 0 taken 4 times.
+
✗ Branch 1 not taken.
+
✓ Branch 3 taken 4 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 4 times.
+
✗ Branch 6 not taken.
+
+
+
4 if (result.status == GetCertificateInfoStatus::Accepted && result.info.has_value()) {
1552 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 if (result.info.value().certificate.has_value()) {
1553 +
+ 3/6 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
+
+
4 return result.info.value().certificate.value().string();
1554 + }
1555 + }
1556 +
1557 + return {};
1558 + 4}
1559 +
1560 + 2std::string EvseSecurity::get_verify_location(CaCertificateType certificate_type) {
1561 +
1562 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1563 +
1564 + try {
1565 + // Support bundle files, in case the certificates contain
1566 + // multiple entries (should be 3) as per the specification
1567 +
+ 2/4 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
+
+
2 X509CertificateBundle verify_location(this->ca_bundle_path_map.at(certificate_type), EncodingFormat::PEM);
1568 +
1569 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 const auto location_path = verify_location.get_path();
1570 +
1571 +
+ 7/12 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 2 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 2 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 2 times.
+
✓ Branch 18 taken 2 times.
+
+
+
6 EVLOG_info << "Requesting certificate location: ["
1572 +
+ 6/12 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 2 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 2 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 2 times.
+
✗ Branch 18 not taken.
+
+
+
4 << conversions::ca_certificate_type_to_string(certificate_type) << "] location:" << location_path;
1573 +
1574 +
+ 2/4 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 2 times.
+
✗ Branch 4 not taken.
+
+
+
4 if (!verify_location.empty() &&
1575 +
+ 1/6 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 2 times.
+
✗ Branch 5 not taken.
+
✗ Branch 6 not taken.
+
✗ Branch 7 not taken.
+
✗ Branch 8 not taken.
+
+
+
2 (!verify_location.is_using_directory() || hash_dir(location_path.c_str()) == 0)) {
1576 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 return location_path;
1577 + }
1578 +
1579 +
+ 2/8 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 2 times.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 2 times.
+
✗ Branch 8 not taken.
+
✗ Branch 9 not taken.
+
✗ Branch 11 not taken.
+
✗ Branch 12 not taken.
+
+
+
4 } catch (const CertificateLoadException& e) {
1580 + EVLOG_error << "Could not obtain verify location, wrong format for certificate: "
1581 + << this->ca_bundle_path_map.at(certificate_type) << " with error: " << e.what();
1582 + }
1583 +
1584 + EVLOG_error << "Could not find any CA certificate for: "
1585 + << conversions::ca_certificate_type_to_string(certificate_type);
1586 +
1587 + return {};
1588 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
2}
1589 +
1590 + int EvseSecurity::get_leaf_expiry_days_count(LeafCertificateType certificate_type) {
1591 + std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1592 +
1593 + EVLOG_info << "Requesting certificate expiry: " << conversions::leaf_certificate_type_to_string(certificate_type);
1594 +
1595 + // Internal since we already locked mutex
1596 + const auto key_pair = this->get_leaf_certificate_info_internal(certificate_type, EncodingFormat::PEM, false);
1597 + if (key_pair.status == GetCertificateInfoStatus::Accepted) {
1598 + try {
1599 + fs::path certificate_path;
1600 +
1601 + if (key_pair.info.has_value()) {
1602 + if (key_pair.info.value().certificate.has_value()) {
1603 + certificate_path = key_pair.info.value().certificate.value();
1604 + } else {
1605 + certificate_path = key_pair.info.value().certificate_single.value();
1606 + }
1607 + }
1608 +
1609 + if (certificate_path.empty() == false) {
1610 + // In case it is a bundle, we know the leaf is always the first
1611 + X509CertificateBundle cert(certificate_path, EncodingFormat::PEM);
1612 +
1613 + int64_t seconds = cert.split().at(0).get_valid_to();
1614 + return std::chrono::duration_cast<days_to_seconds>(std::chrono::seconds(seconds)).count();
1615 + }
1616 + } catch (const CertificateLoadException& e) {
1617 + EVLOG_error << "Could not obtain leaf expiry certificate: " << e.what();
1618 + }
1619 + }
1620 +
1621 + return 0;
1622 + }
1623 +
1624 + bool EvseSecurity::verify_file_signature(const fs::path& path, const std::string& signing_certificate,
1625 + const std::string signature) {
1626 + std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1627 +
1628 + EVLOG_info << "Verifying file signature for " << path.string();
1629 +
1630 + std::vector<std::uint8_t> sha256_digest;
1631 +
1632 + if (false == CryptoSupplier::digest_file_sha256(path, sha256_digest)) {
1633 + EVLOG_error << "Error during digesting file: " << path;
1634 + return false;
1635 + }
1636 +
1637 + std::vector<std::uint8_t> signature_decoded;
1638 +
1639 + if (false == CryptoSupplier::base64_decode_to_bytes(signature, signature_decoded)) {
1640 + EVLOG_error << "Error during decoding signature: " << signature;
1641 + return false;
1642 + }
1643 +
1644 + try {
1645 + X509Wrapper x509_signing_cerificate(signing_certificate, EncodingFormat::PEM);
1646 +
1647 + if (CryptoSupplier::x509_verify_signature(x509_signing_cerificate.get(), signature_decoded, sha256_digest)) {
1648 + EVLOG_debug << "Signature successful verification";
1649 + return true;
1650 + } else {
1651 + EVLOG_error << "Failure to verify signature";
1652 + return false;
1653 + }
1654 + } catch (const CertificateLoadException& e) {
1655 + EVLOG_error << "Could not parse signing certificate: " << e.what();
1656 + return false;
1657 + }
1658 +
1659 + return false;
1660 + }
1661 +
1662 + std::vector<std::uint8_t> EvseSecurity::base64_decode_to_bytes(const std::string& base64_string) {
1663 + std::vector<std::uint8_t> decoded_bytes;
1664 +
1665 + if (false == CryptoSupplier::base64_decode_to_bytes(base64_string, decoded_bytes)) {
1666 + return {};
1667 + }
1668 +
1669 + return decoded_bytes;
1670 + }
1671 +
1672 + 2std::string EvseSecurity::base64_decode_to_string(const std::string& base64_string) {
1673 + 2 std::string decoded_string;
1674 +
1675 +
+ 2/4 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 2 times.
+
+
+
2 if (false == CryptoSupplier::base64_decode_to_string(base64_string, decoded_string)) {
1676 + return {};
1677 + }
1678 +
1679 + 2 return decoded_string;
1680 + 2}
1681 +
1682 + std::string EvseSecurity::base64_encode_from_bytes(const std::vector<std::uint8_t>& bytes) {
1683 + std::string encoded_string;
1684 +
1685 + if (false == CryptoSupplier::base64_encode_from_bytes(bytes, encoded_string)) {
1686 + return {};
1687 + }
1688 +
1689 + return encoded_string;
1690 + }
1691 +
1692 + 2std::string EvseSecurity::base64_encode_from_string(const std::string& string) {
1693 + 2 std::string encoded_string;
1694 +
1695 +
+ 2/4 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 2 times.
+
+
+
2 if (false == CryptoSupplier::base64_encode_from_string(string, encoded_string)) {
1696 + return {};
1697 + }
1698 +
1699 + 2 return encoded_string;
1700 + 2}
1701 +
1702 + 4CertificateValidationResult EvseSecurity::verify_certificate(const std::string& certificate_chain,
1703 + LeafCertificateType certificate_type) {
1704 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1705 +
1706 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
8 return verify_certificate_internal(certificate_chain, certificate_type);
1707 + 4}
1708 +
1709 + 14CertificateValidationResult EvseSecurity::verify_certificate_internal(const std::string& certificate_chain,
1710 + LeafCertificateType certificate_type) {
1711 +
+ 11/20 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 14 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 14 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 14 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 14 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 14 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 14 times.
+
✗ Branch 24 not taken.
+
✓ Branch 27 taken 14 times.
+
✗ Branch 28 not taken.
+
✓ Branch 30 taken 14 times.
+
✓ Branch 31 taken 14 times.
+
+
+
28 EVLOG_info << "Verifying leaf certificate: " << conversions::leaf_certificate_type_to_string(certificate_type);
1712 +
1713 + CaCertificateType ca_certificate_type;
1714 +
1715 +
+ 2/2 +
+
✓ Branch 0 taken 6 times.
+
✓ Branch 1 taken 8 times.
+
+
+
14 if (certificate_type == LeafCertificateType::CSMS) {
1716 + 6 ca_certificate_type = CaCertificateType::CSMS;
1717 +
+ 1/2 +
+
✓ Branch 0 taken 8 times.
+
✗ Branch 1 not taken.
+
+
+
8 } else if (certificate_type == LeafCertificateType::V2G) {
1718 + 8 ca_certificate_type = CaCertificateType::V2G;
1719 + } else if (certificate_type == LeafCertificateType::MF)
1720 + ca_certificate_type = CaCertificateType::MF;
1721 + else if (certificate_type == LeafCertificateType::MO) {
1722 + ca_certificate_type = CaCertificateType::MO;
1723 + } else {
1724 + throw std::runtime_error("Could not convert LeafCertificateType to CaCertificateType during verification!");
1725 + }
1726 +
1727 + // If we don't have a root certificate installed, return that we can't find an issuer
1728 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 14 times.
+
+
+
14 if (false == is_ca_certificate_installed_internal(ca_certificate_type)) {
1729 + return CertificateValidationResult::IssuerNotFound;
1730 + }
1731 +
1732 + try {
1733 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509CertificateBundle certificate(certificate_chain, EncodingFormat::PEM);
1734 +
1735 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 std::vector<X509Wrapper> _certificate_chain = certificate.split();
1736 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 14 times.
+
+
+
14 if (_certificate_chain.empty()) {
1737 + return CertificateValidationResult::Unknown;
1738 + }
1739 +
1740 + // The leaf is to be verified
1741 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
+
+
14 const auto leaf_certificate = _certificate_chain.at(0);
1742 +
1743 + // Retrieve the hierarchy in order to check if the chain contains a root certificate
1744 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509CertificateHierarchy& hierarchy = certificate.get_certificate_hierarchy();
1745 +
1746 + // Build all untrusted intermediary certificates, and exclude any root
1747 + 14 std::vector<X509Handle*> untrusted_subcas;
1748 +
1749 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 14 times.
+
+
+
14 if (_certificate_chain.size() > 1) {
1750 + for (size_t i = 1; i < _certificate_chain.size(); i++) {
1751 + const auto& cert = _certificate_chain[i];
1752 + // Ignore the received certificate is somehow self-signed
1753 + if (cert.is_selfsigned()) {
1754 + EVLOG_warning << "Ignore root certificate: " << cert.get_common_name();
1755 + } else {
1756 + untrusted_subcas.emplace_back(cert.get());
1757 + }
1758 + }
1759 + }
1760 +
1761 + // Build the trusted parent certificates from our internal store
1762 + 14 std::vector<X509Handle*> trusted_parent_certificates;
1763 +
1764 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
+
+
14 fs::path root_store = this->ca_bundle_path_map.at(ca_certificate_type);
1765 + 14 CertificateValidationResult validated{};
1766 +
1767 +
+ 3/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 2 times.
+
✓ Branch 4 taken 12 times.
+
+
+
14 if (fs::is_directory(root_store)) {
1768 + // In case of a directory load the certificates manually and add them
1769 + // to the parent certificates
1770 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 X509CertificateBundle roots(root_store, EncodingFormat::PEM);
1771 +
1772 + // We use a root chain instead of relying on OpenSSL since that requires to have
1773 + // the name of the certificates in the format "hash.0", hash being the subject hash
1774 + // or to have symlinks in the mentioned format to the certificates in the directory
1775 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 std::vector<X509Wrapper> root_chain{roots.split()};
1776 +
1777 +
+ 2/2 +
+
✓ Branch 1 taken 10 times.
+
✓ Branch 2 taken 2 times.
+
+
+
12 for (size_t i = 0; i < root_chain.size(); i++) {
1778 +
+ 1/2 +
+
✓ Branch 3 taken 10 times.
+
✗ Branch 4 not taken.
+
+
+
10 trusted_parent_certificates.emplace_back(root_chain[i].get());
1779 + }
1780 +
1781 + // The root_chain stores the X509Handler pointers, if this goes out of scope then
1782 + // parent_certificates will point to nothing.
1783 + validated =
1784 +
+ 1/2 +
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
+
+
2 CryptoSupplier::x509_verify_certificate_chain(leaf_certificate.get(), trusted_parent_certificates,
1785 + untrusted_subcas, true, std::nullopt, std::nullopt);
1786 + 2 } else {
1787 +
+ 2/4 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✓ Branch 6 taken 12 times.
+
✗ Branch 7 not taken.
+
+
+
12 validated = CryptoSupplier::x509_verify_certificate_chain(
1788 + leaf_certificate.get(), trusted_parent_certificates, untrusted_subcas, true, std::nullopt, root_store);
1789 + }
1790 +
1791 + 14 return validated;
1792 +
+ 0/2 +
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
+
+
14 } catch (const CertificateLoadException& e) {
1793 + EVLOG_warning << "Could not validate certificate chain because of invalid format";
1794 + return CertificateValidationResult::Unknown;
1795 + }
1796 + }
1797 +
1798 + 14void EvseSecurity::garbage_collect() {
1799 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 std::lock_guard<std::mutex> guard(EvseSecurity::security_mutex);
1800 +
1801 + // Only garbage collect if we are full
1802 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 14 times.
+
+
+
14 if (is_filesystem_full() == false) {
1803 + EVLOG_debug << "Garbage collect postponed, filesystem is not full";
1804 + return;
1805 + }
1806 +
1807 +
+ 9/16 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 14 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 14 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 14 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 14 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 14 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 14 times.
+
✓ Branch 24 taken 14 times.
+
+
+
28 EVLOG_info << "Starting garbage collect!";
1808 +
1809 + 14 std::vector<std::tuple<fs::path, fs::path, CaCertificateType>> leaf_paths;
1810 +
1811 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 leaf_paths.push_back(std::make_tuple(this->directories.csms_leaf_cert_directory,
1812 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 this->directories.csms_leaf_key_directory, CaCertificateType::CSMS));
1813 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 leaf_paths.push_back(std::make_tuple(this->directories.secc_leaf_cert_directory,
1814 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 this->directories.secc_leaf_key_directory, CaCertificateType::V2G));
1815 +
1816 + // Delete certificates first, give the option to cleanup the dangling keys afterwards
1817 + 14 std::set<fs::path> invalid_certificate_files;
1818 +
1819 + // Private keys that are linked to the skipped certificates and that will not be deleted regardless
1820 + 14 std::set<fs::path> protected_private_keys;
1821 +
1822 + // Order by latest valid, and keep newest with a safety limit
1823 +
+ 2/2 +
+
✓ Branch 8 taken 28 times.
+
✓ Branch 9 taken 14 times.
+
+
+
42 for (auto const& [cert_dir, key_dir, ca_type] : leaf_paths) {
1824 + // Root bundle required for hash of OCSP cache
1825 + try {
1826 +
+ 2/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 28 times.
+
✗ Branch 5 not taken.
+
+
+
28 X509CertificateBundle root_bundle(ca_bundle_path_map[ca_type], EncodingFormat::PEM);
1827 +
+ 1/2 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
+
+
28 X509CertificateBundle expired_certs(cert_dir, EncodingFormat::PEM);
1828 +
1829 + // Only handle if we have more than the minimum certificates entry
1830 +
+ 3/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 2 times.
+
✓ Branch 4 taken 26 times.
+
+
+
28 if (expired_certs.get_certificate_chains_count() > DEFAULT_MINIMUM_CERTIFICATE_ENTRIES) {
1831 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 fs::path key_directory = key_dir;
1832 + 2 int skipped = 0;
1833 +
1834 + // Order by expiry date, and keep even expired certificates with a minimum of 10 certificates
1835 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 expired_certs.for_each_chain_ordered(
1836 + 64 [this, &invalid_certificate_files, &skipped, &key_directory, &protected_private_keys,
1837 + 384 &root_bundle](const fs::path& file, const std::vector<X509Wrapper>& chain) {
1838 + // By default delete all empty
1839 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 64 times.
+
+
+
64 if (chain.size() <= 0) {
1840 + invalid_certificate_files.emplace(file);
1841 + }
1842 +
1843 +
+ 2/2 +
+
✓ Branch 0 taken 44 times.
+
✓ Branch 1 taken 20 times.
+
+
+
64 if (++skipped > DEFAULT_MINIMUM_CERTIFICATE_ENTRIES) {
1844 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 44 times.
+
+
+
44 if (chain.empty()) {
1845 + return true;
1846 + }
1847 +
1848 + // If the chain contains the first expired (leafs are the first)
1849 +
+ 1/2 +
+
✓ Branch 2 taken 44 times.
+
✗ Branch 3 not taken.
+
+
+
44 if (chain[0].is_expired()) {
1850 +
+ 1/2 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
+
+
44 invalid_certificate_files.emplace(file);
1851 +
1852 + // Also attempt to add the key for deletion
1853 + try {
1854 + 44 fs::path key_file = get_private_key_path_of_certificate(chain[0], key_directory,
1855 +
+ 2/4 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 44 times.
+
✗ Branch 5 not taken.
+
+
+
88 this->private_key_password);
1856 +
+ 1/2 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
+
+
44 invalid_certificate_files.emplace(key_file);
1857 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
44 } catch (NoPrivateKeyException& e) {
1858 + }
1859 +
1860 +
+ 1/2 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
+
+
44 auto leaf_chain = chain;
1861 + 44 X509CertificateHierarchy hierarchy = std::move(
1862 +
+ 3/6 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 44 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 44 times.
+
✗ Branch 8 not taken.
+
+
+
88 X509CertificateHierarchy::build_hierarchy(root_bundle.split(), leaf_chain));
1863 +
1864 + try {
1865 +
+ 1/2 +
+
✓ Branch 2 taken 44 times.
+
✗ Branch 3 not taken.
+
+
+
44 CertificateHashData ocsp_hash = hierarchy.get_certificate_hash(chain[0]);
1866 +
1867 + // Find OCSP cache with hash
1868 +
+ 2/4 +
+
✓ Branch 2 taken 44 times.
+
✗ Branch 3 not taken.
+
✓ Branch 6 taken 44 times.
+
✗ Branch 7 not taken.
+
+
+
44 if (chain[0].get_file().has_value()) {
1869 +
+ 5/10 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 44 times.
+
✗ Branch 6 not taken.
+
✓ Branch 8 taken 44 times.
+
✗ Branch 9 not taken.
+
✓ Branch 11 taken 44 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 44 times.
+
✗ Branch 15 not taken.
+
+
+
44 const auto ocsp_path = chain[0].get_file().value().parent_path() / "ocsp";
1870 +
1871 +
+ 2/4 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 44 times.
+
+
+
44 if (fs::exists(ocsp_path)) {
1872 + for (const auto& hash_entry : fs::directory_iterator(ocsp_path)) {
1873 + if (hash_entry.is_regular_file() == false) {
1874 + continue;
1875 + }
1876 + // Attempt hash read
1877 + CertificateHashData read_hash;
1878 +
1879 + if (filesystem_utils::read_hash_from_file(hash_entry.path(),
1880 + read_hash) &&
1881 + read_hash == ocsp_hash) {
1882 +
1883 + auto oscp_data_path = hash_entry.path();
1884 + oscp_data_path.replace_extension(DER_EXTENSION);
1885 +
1886 + invalid_certificate_files.emplace(hash_entry.path());
1887 + invalid_certificate_files.emplace(oscp_data_path);
1888 + }
1889 + }
1890 + }
1891 + 44 }
1892 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
44 } catch (const NoCertificateFound& e) {
1893 + }
1894 + 44 }
1895 + } else {
1896 + // Add to protected certificate list
1897 + try {
1898 + 20 fs::path key_file = get_private_key_path_of_certificate(chain[0], key_directory,
1899 +
+ 2/4 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 20 times.
+
✗ Branch 5 not taken.
+
+
+
40 this->private_key_password);
1900 +
+ 1/2 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
+
+
20 protected_private_keys.emplace(key_file);
1901 +
1902 + // Erase all protected keys from the managed CRSs
1903 +
+ 1/2 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
+
+
20 auto it = managed_csr.find(key_file);
1904 +
+ 1/2 +
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 20 times.
+
+
+
20 if (it != managed_csr.end()) {
1905 + managed_csr.erase(it);
1906 + }
1907 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
20 } catch (NoPrivateKeyException& e) {
1908 + }
1909 + }
1910 +
1911 + 64 return true;
1912 + },
1913 + 322 [](const std::vector<X509Wrapper>& a, const std::vector<X509Wrapper>& b) {
1914 + // Order from newest to oldest (newest DEFAULT_MINIMUM_CERTIFICATE_ENTRIES) are kept
1915 + // even if they are expired
1916 +
+ 3/6 +
+
✓ Branch 1 taken 322 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 322 times.
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 322 times.
+
✗ Branch 7 not taken.
+
+
+
322 if (a.size() && b.size()) {
1917 + 322 return a.at(0).get_valid_to() > b.at(0).get_valid_to();
1918 + } else {
1919 + return false;
1920 + }
1921 + });
1922 + 2 }
1923 +
+ 0/2 +
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
+
+
28 } catch (const CertificateLoadException& e) {
1924 + EVLOG_warning << "Could not load bundle from file: " << e.what();
1925 + }
1926 + } // End leaf for iteration
1927 +
1928 +
+ 2/2 +
+
✓ Branch 5 taken 88 times.
+
✓ Branch 6 taken 14 times.
+
+
+
102 for (const auto& expired_certificate_file : invalid_certificate_files) {
1929 +
+ 2/4 +
+
✓ Branch 1 taken 88 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 88 times.
+
✗ Branch 4 not taken.
+
+
+
88 if (filesystem_utils::delete_file(expired_certificate_file))
1930 +
+ 10/18 +
+
✓ Branch 1 taken 88 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 88 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 88 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 88 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 88 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 88 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 88 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 88 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 88 times.
+
✓ Branch 27 taken 88 times.
+
+
+
176 EVLOG_info << "Deleted expired certificate file: " << expired_certificate_file;
1931 + else
1932 + EVLOG_warning << "Error deleting expired certificate file: " << expired_certificate_file;
1933 + }
1934 +
1935 + // In case of a reset, the managed CSRs can be lost. In that case add them back to the list
1936 + // to give the change of a CSR to be fulfilled. Eventually the GC will delete those CSRs
1937 + // at a further invocation after the GC timer will elapse a few times. This behavior
1938 + // was added so that if we have a reset and the CSMS sends us a CSR response while we were
1939 + // down it should still be processed when we boot up and NOT delete the CSRs
1940 +
+ 2/2 +
+
✓ Branch 8 taken 28 times.
+
✓ Branch 9 taken 14 times.
+
+
+
42 for (auto const& [cert_dir, keys_dir, ca_type] : leaf_paths) {
1941 +
+ 1/2 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
+
+
28 fs::path cert_path = cert_dir;
1942 +
+ 1/2 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
+
+
28 fs::path key_path = keys_dir;
1943 +
1944 +
+ 4/6 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 11 taken 128 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 128 times.
+
✓ Branch 15 taken 28 times.
+
+
+
156 for (const auto& key_entry : fs::recursive_directory_iterator(key_path)) {
1945 +
+ 1/2 +
+
✓ Branch 2 taken 128 times.
+
✗ Branch 3 not taken.
+
+
+
128 auto key_file_path = key_entry.path();
1946 +
1947 + // Skip protected keys
1948 +
+ 3/4 +
+
✓ Branch 2 taken 128 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 18 times.
+
✓ Branch 6 taken 110 times.
+
+
+
128 if (protected_private_keys.find(key_file_path) != protected_private_keys.end()) {
1949 + 18 continue;
1950 + }
1951 +
1952 +
+ 3/4 +
+
✓ Branch 1 taken 110 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 34 times.
+
✓ Branch 4 taken 76 times.
+
+
+
110 if (is_keyfile(key_file_path)) {
1953 + 34 bool error = false;
1954 +
1955 + try {
1956 + // Check if we have found any matching certificate
1957 +
+ 3/4 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✓ Branch 5 taken 8 times.
+
+
+
42 get_certificate_path_of_key(key_file_path, keys_dir, this->private_key_password);
1958 +
+ 1/3 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 } catch (const NoCertificateValidException& e) {
1959 + // If we did not found, add to the potential delete list
1960 +
+ 13/24 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 8 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 8 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 8 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 8 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 8 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 8 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 8 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 8 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 8 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 8 times.
+
✓ Branch 36 taken 8 times.
+
+
+
24 EVLOG_debug << "Could not find matching certificate for key: " << key_file_path
1961 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
16 << " adding to potential deletes";
1962 + 8 error = true;
1963 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 } catch (const NoPrivateKeyException& e) {
1964 + EVLOG_debug << "Could not load private key: " << key_file_path << " adding to potential deletes";
1965 + error = true;
1966 + }
1967 +
1968 +
+ 2/2 +
+
✓ Branch 0 taken 8 times.
+
✓ Branch 1 taken 26 times.
+
+
+
34 if (error) {
1969 + // Give a chance to be fulfilled by the CSMS
1970 +
+ 3/4 +
+
✓ Branch 2 taken 8 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 2 times.
+
✓ Branch 6 taken 6 times.
+
+
+
8 if (managed_csr.find(key_file_path) == managed_csr.end()) {
1971 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 managed_csr.emplace(key_file_path, std::chrono::steady_clock::now());
1972 + }
1973 + }
1974 + }
1975 +
+ 2/2 +
+
✓ Branch 1 taken 110 times.
+
✓ Branch 2 taken 18 times.
+
+
+
156 }
1976 + 28 }
1977 +
1978 + // Delete all managed private keys of a CSR that we did not had a response to
1979 + 14 auto now_timepoint = std::chrono::steady_clock::now();
1980 +
1981 + // The update_leaf_certificate function is responsible for removing responded CSRs from this managed list
1982 +
+ 2/2 +
+
✓ Branch 3 taken 8 times.
+
✓ Branch 4 taken 14 times.
+
+
+
22 for (auto it = managed_csr.begin(); it != managed_csr.end();) {
1983 +
+ 2/4 +
+
✓ Branch 2 taken 8 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 8 times.
+
✗ Branch 6 not taken.
+
+
+
8 std::chrono::seconds elapsed = std::chrono::duration_cast<std::chrono::seconds>(now_timepoint - it->second);
1984 +
1985 +
+ 3/4 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 4 times.
+
✓ Branch 4 taken 4 times.
+
+
+
8 if (elapsed > csr_expiry) {
1986 +
+ 13/24 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 4 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 4 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 4 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 4 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 4 times.
+
✗ Branch 27 not taken.
+
✓ Branch 30 taken 4 times.
+
✗ Branch 31 not taken.
+
✓ Branch 33 taken 4 times.
+
✗ Branch 34 not taken.
+
✓ Branch 36 taken 4 times.
+
✓ Branch 37 taken 4 times.
+
+
+
8 EVLOG_debug << "Found expired csr key, deleting: " << it->first;
1987 +
+ 1/2 +
+
✓ Branch 2 taken 4 times.
+
✗ Branch 3 not taken.
+
+
+
4 filesystem_utils::delete_file(it->first);
1988 +
1989 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 it = managed_csr.erase(it);
1990 + } else {
1991 + 4 ++it;
1992 + }
1993 + }
1994 +
1995 + 14 std::set<fs::path> invalid_ocsp_files;
1996 +
1997 + // Delete all non-owned OCSP data
1998 + 42 for (const auto& leaf_certificate_path :
1999 +
+ 4/10 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 28 times.
+
✓ Branch 9 taken 14 times.
+
✗ Branch 11 not taken.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 14 not taken.
+
+
+
84 {directories.secc_leaf_cert_directory, directories.csms_leaf_cert_directory}) {
2000 + try {
2001 + 28 bool secc = (leaf_certificate_path == directories.secc_leaf_cert_directory);
2002 +
+ 3/4 +
+
✓ Branch 1 taken 14 times.
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 14 times.
+
+
+
42 bool csms = (leaf_certificate_path == directories.csms_leaf_cert_directory) ||
2003 + 14 (directories.csms_leaf_cert_directory == directories.secc_leaf_cert_directory);
2004 +
2005 + CaCertificateType load;
2006 +
2007 +
+ 2/2 +
+
✓ Branch 0 taken 14 times.
+
✓ Branch 1 taken 14 times.
+
+
+
28 if (secc)
2008 + 14 load = CaCertificateType::V2G;
2009 +
+ 1/2 +
+
✓ Branch 0 taken 14 times.
+
✗ Branch 1 not taken.
+
+
+
14 else if (csms)
2010 + 14 load = CaCertificateType::CSMS;
2011 +
2012 + // Also load the roots since we need to build the hierarchy for correct certificate hashes
2013 +
+ 2/4 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 28 times.
+
✗ Branch 5 not taken.
+
+
+
28 X509CertificateBundle root_bundle(ca_bundle_path_map[load], EncodingFormat::PEM);
2014 +
+ 1/2 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
+
+
28 X509CertificateBundle leaf_bundle(leaf_certificate_path, EncodingFormat::PEM);
2015 +
2016 + 28 fs::path leaf_ocsp;
2017 + 28 fs::path root_ocsp;
2018 +
2019 +
+ 1/2 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
+
+
28 if (root_bundle.is_using_bundle_file()) {
2020 +
+ 4/8 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 28 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 28 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 28 times.
+
✗ Branch 11 not taken.
+
+
+
28 root_ocsp = root_bundle.get_path().parent_path() / "ocsp";
2021 + } else {
2022 + root_ocsp = root_bundle.get_path() / "ocsp";
2023 + }
2024 +
2025 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 28 times.
+
+
+
28 if (leaf_bundle.is_using_bundle_file()) {
2026 + leaf_ocsp = leaf_bundle.get_path().parent_path() / "ocsp";
2027 + } else {
2028 +
+ 3/6 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 28 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 28 times.
+
✗ Branch 8 not taken.
+
+
+
28 leaf_ocsp = leaf_bundle.get_path() / "ocsp";
2029 + }
2030 +
2031 + X509CertificateHierarchy hierarchy =
2032 +
+ 3/6 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 28 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 28 times.
+
✗ Branch 8 not taken.
+
+
+
28 std::move(X509CertificateHierarchy::build_hierarchy(root_bundle.split(), leaf_bundle.split()));
2033 +
2034 + // Iterate all hashes folders and see if any are missing
2035 +
+ 4/10 +
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 28 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 56 times.
+
✓ Branch 9 taken 28 times.
+
✗ Branch 11 not taken.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 14 not taken.
+
+
+
140 for (auto& ocsp_dir : {leaf_ocsp, root_ocsp}) {
2036 +
+ 3/4 +
+
✓ Branch 1 taken 56 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 12 times.
+
✓ Branch 4 taken 44 times.
+
+
+
56 if (fs::exists(ocsp_dir)) {
2037 +
+ 4/6 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
✓ Branch 11 taken 48 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 48 times.
+
✓ Branch 15 taken 12 times.
+
+
+
60 for (auto& ocsp_entry : fs::directory_iterator(ocsp_dir)) {
2038 +
+ 2/4 +
+
✓ Branch 1 taken 48 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 48 times.
+
+
+
48 if (ocsp_entry.is_regular_file() == false) {
2039 + continue;
2040 + }
2041 +
2042 + // Attempt hash read
2043 + 48 CertificateHashData read_hash;
2044 +
2045 +
+ 3/4 +
+
✓ Branch 2 taken 48 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 24 times.
+
✓ Branch 5 taken 24 times.
+
+
+
48 if (filesystem_utils::read_hash_from_file(ocsp_entry.path(), read_hash)) {
2046 + // If we can't find the has, it means it was deleted somehow, add to delete list
2047 +
+ 3/4 +
+
✓ Branch 1 taken 24 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 12 times.
+
✓ Branch 4 taken 12 times.
+
+
+
24 if (hierarchy.contains_certificate_hash(read_hash) == false) {
2048 +
+ 1/2 +
+
✓ Branch 2 taken 12 times.
+
✗ Branch 3 not taken.
+
+
+
12 auto oscp_data_path = ocsp_entry.path();
2049 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 oscp_data_path.replace_extension(DER_EXTENSION);
2050 +
2051 +
+ 1/2 +
+
✓ Branch 2 taken 12 times.
+
✗ Branch 3 not taken.
+
+
+
12 invalid_ocsp_files.emplace(ocsp_entry.path());
2052 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 invalid_ocsp_files.emplace(oscp_data_path);
2053 + 12 }
2054 + }
2055 + 60 }
2056 + }
2057 +
+ 2/4 +
+
✓ Branch 0 taken 56 times.
+
✓ Branch 1 taken 28 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
84 }
2058 +
+ 0/2 +
+
✗ Branch 10 not taken.
+
✗ Branch 11 not taken.
+
+
+
28 } catch (const CertificateLoadException& e) {
2059 + EVLOG_warning << "Could not load ca bundle from file: " << leaf_certificate_path;
2060 + }
2061 +
+ 2/4 +
+
✓ Branch 0 taken 28 times.
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
42 }
2062 +
2063 +
+ 2/2 +
+
✓ Branch 5 taken 16 times.
+
✓ Branch 6 taken 14 times.
+
+
+
30 for (const auto& invalid_ocsp : invalid_ocsp_files) {
2064 +
+ 2/4 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 16 times.
+
✗ Branch 4 not taken.
+
+
+
16 if (filesystem_utils::delete_file(invalid_ocsp))
2065 +
+ 10/18 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 16 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 16 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 16 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 16 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 16 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 16 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 16 times.
+
✓ Branch 27 taken 16 times.
+
+
+
32 EVLOG_info << "Deleted invalid ocsp file: " << invalid_ocsp;
2066 + else
2067 + EVLOG_warning << "Error deleting invalid ocsp file: " << invalid_ocsp;
2068 + }
2069 +
+ 1/2 +
+
✓ Branch 5 taken 14 times.
+
✗ Branch 6 not taken.
+
+
+
14}
2070 +
2071 + 72bool EvseSecurity::is_filesystem_full() {
2072 + 72 std::set<fs::path> unique_paths;
2073 +
2074 + // Collect all bundles
2075 +
+ 2/2 +
+
✓ Branch 7 taken 288 times.
+
✓ Branch 8 taken 72 times.
+
+
+
360 for (auto const& [certificate_type, ca_bundle_path] : ca_bundle_path_map) {
2076 +
+ 3/4 +
+
✓ Branch 1 taken 288 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 282 times.
+
✓ Branch 4 taken 6 times.
+
+
+
288 if (fs::is_regular_file(ca_bundle_path)) {
2077 +
+ 1/2 +
+
✓ Branch 1 taken 282 times.
+
✗ Branch 2 not taken.
+
+
+
282 unique_paths.emplace(ca_bundle_path);
2078 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 6 times.
+
+
+
6 } else if (fs::is_directory(ca_bundle_path)) {
2079 + for (const auto& entry : fs::recursive_directory_iterator(ca_bundle_path)) {
2080 + if (fs::is_regular_file(entry)) {
2081 + unique_paths.emplace(entry);
2082 + }
2083 + }
2084 + }
2085 + }
2086 +
2087 + // Collect all key/leafs
2088 + 72 std::vector<fs::path> key_pairs;
2089 +
2090 +
+ 1/2 +
+
✓ Branch 1 taken 72 times.
+
✗ Branch 2 not taken.
+
+
+
72 key_pairs.push_back(directories.csms_leaf_cert_directory);
2091 +
+ 1/2 +
+
✓ Branch 1 taken 72 times.
+
✗ Branch 2 not taken.
+
+
+
72 key_pairs.push_back(directories.csms_leaf_key_directory);
2092 +
+ 1/2 +
+
✓ Branch 1 taken 72 times.
+
✗ Branch 2 not taken.
+
+
+
72 key_pairs.push_back(directories.secc_leaf_cert_directory);
2093 +
+ 1/2 +
+
✓ Branch 1 taken 72 times.
+
✗ Branch 2 not taken.
+
+
+
72 key_pairs.push_back(directories.secc_leaf_key_directory);
2094 +
2095 +
+ 2/2 +
+
✓ Branch 5 taken 288 times.
+
✓ Branch 6 taken 72 times.
+
+
+
360 for (auto const& directory : key_pairs) {
2096 +
+ 2/4 +
+
✓ Branch 1 taken 288 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 288 times.
+
+
+
288 if (fs::is_regular_file(directory)) {
2097 + unique_paths.emplace(directory);
2098 +
+ 2/4 +
+
✓ Branch 1 taken 288 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 288 times.
+
✗ Branch 4 not taken.
+
+
+
288 } else if (fs::is_directory(directory)) {
2099 +
+ 4/6 +
+
✓ Branch 1 taken 288 times.
+
✗ Branch 2 not taken.
+
✓ Branch 11 taken 1520 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 1520 times.
+
✓ Branch 15 taken 288 times.
+
+
+
1808 for (const auto& entry : fs::recursive_directory_iterator(directory)) {
2100 +
+ 3/4 +
+
✓ Branch 2 taken 1520 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 1508 times.
+
✓ Branch 5 taken 12 times.
+
+
+
1520 if (fs::is_regular_file(entry)) {
2101 +
+ 1/2 +
+
✓ Branch 1 taken 1508 times.
+
✗ Branch 2 not taken.
+
+
+
1508 unique_paths.emplace(entry);
2102 + }
2103 + 288 }
2104 + }
2105 + }
2106 +
2107 + 72 uintmax_t total_entries = unique_paths.size();
2108 +
+ 13/24 +
+
✓ Branch 1 taken 72 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 72 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 72 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 72 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 72 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 72 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 72 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 72 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 72 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 72 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 72 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 72 times.
+
✓ Branch 36 taken 72 times.
+
+
+
144 EVLOG_debug << "Total entries used: " << total_entries;
2109 +
2110 +
+ 2/2 +
+
✓ Branch 0 taken 10 times.
+
✓ Branch 1 taken 62 times.
+
+
+
72 if (total_entries > max_fs_certificate_store_entries) {
2111 +
+ 15/28 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 10 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 10 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 10 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 10 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 10 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 10 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 10 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 10 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 10 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 10 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 10 times.
+
✗ Branch 36 not taken.
+
✓ Branch 38 taken 10 times.
+
✗ Branch 39 not taken.
+
✓ Branch 41 taken 10 times.
+
✓ Branch 42 taken 10 times.
+
+
+
30 EVLOG_warning << "Exceeded maximum entries: " << max_fs_certificate_store_entries << " with :" << total_entries
2112 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
20 << " total entries";
2113 + 10 return true;
2114 + }
2115 +
2116 + 62 uintmax_t total_size_bytes = 0;
2117 +
+ 2/2 +
+
✓ Branch 4 taken 566 times.
+
✓ Branch 5 taken 62 times.
+
+
+
628 for (const auto& path : unique_paths) {
2118 +
+ 1/2 +
+
✓ Branch 1 taken 566 times.
+
✗ Branch 2 not taken.
+
+
+
566 total_size_bytes = fs::file_size(path);
2119 + }
2120 +
2121 +
+ 13/24 +
+
✓ Branch 1 taken 62 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 62 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 62 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 62 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 62 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 62 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 62 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 62 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 62 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 62 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 62 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 62 times.
+
✓ Branch 36 taken 62 times.
+
+
+
124 EVLOG_debug << "Total bytes used: " << total_size_bytes;
2122 +
+ 2/2 +
+
✓ Branch 0 taken 14 times.
+
✓ Branch 1 taken 48 times.
+
+
+
62 if (total_size_bytes >= max_fs_usage_bytes) {
2123 +
+ 13/24 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 14 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 14 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 14 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 14 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 14 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 14 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 14 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 14 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 14 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 14 times.
+
✓ Branch 36 taken 14 times.
+
+
+
28 EVLOG_warning << "Exceeded maximum byte size: " << total_size_bytes;
2124 + 14 return true;
2125 + }
2126 +
2127 + 48 return false;
2128 + 72}
2129 +
2130 + } // namespace evse_security
2131 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_types.cpp.77aefae0ad63c42f7ad2e0e682b58829.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_types.cpp.77aefae0ad63c42f7ad2e0e682b58829.html new file mode 100644 index 00000000..41c88494 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_types.cpp.77aefae0ad63c42f7ad2e0e682b58829.html @@ -0,0 +1,1446 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/evse_types.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:2913022.3%
Functions:51145.5%
Branches:1916111.8%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 +
4 + #include <evse_security/evse_types.hpp>
5 +
6 + namespace evse_security {
7 +
8 + namespace conversions {
9 +
10 + std::string encoding_format_to_string(EncodingFormat e) {
11 + switch (e) {
12 + case EncodingFormat::DER:
13 + return "DER";
14 + case EncodingFormat::PEM:
15 + return "PEM";
16 + default:
17 + throw std::out_of_range("Could not convert EncodingFormat to string");
18 + }
19 + };
20 +
21 + 80std::string ca_certificate_type_to_string(CaCertificateType e) {
22 +
+ 4/5 +
+
✓ Branch 0 taken 20 times.
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 44 times.
+
✓ Branch 3 taken 8 times.
+
✗ Branch 4 not taken.
+
+
+
80 switch (e) {
23 + 20 case CaCertificateType::V2G:
24 +
+ 1/2 +
+
✓ Branch 2 taken 20 times.
+
✗ Branch 3 not taken.
+
+
+
20 return "V2G";
25 + 8 case CaCertificateType::MO:
26 +
+ 1/2 +
+
✓ Branch 2 taken 8 times.
+
✗ Branch 3 not taken.
+
+
+
8 return "MO";
27 + 44 case CaCertificateType::CSMS:
28 +
+ 1/2 +
+
✓ Branch 2 taken 44 times.
+
✗ Branch 3 not taken.
+
+
+
44 return "CSMS";
29 + 8 case CaCertificateType::MF:
30 +
+ 1/2 +
+
✓ Branch 2 taken 8 times.
+
✗ Branch 3 not taken.
+
+
+
8 return "MF";
31 + default:
32 + throw std::out_of_range("Could not convert CaCertificateType to string");
33 + }
34 + };
35 +
36 + 54std::string leaf_certificate_type_to_string(LeafCertificateType e) {
37 +
+ 2/5 +
+
✓ Branch 0 taken 20 times.
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
+
+
54 switch (e) {
38 + 20 case LeafCertificateType::CSMS:
39 +
+ 1/2 +
+
✓ Branch 2 taken 20 times.
+
✗ Branch 3 not taken.
+
+
+
20 return "CSMS";
40 + 34 case LeafCertificateType::V2G:
41 +
+ 1/2 +
+
✓ Branch 2 taken 34 times.
+
✗ Branch 3 not taken.
+
+
+
34 return "V2G";
42 + case LeafCertificateType::MF:
43 + return "MF";
44 + case LeafCertificateType::MO:
45 + return "MO";
46 + default:
47 + throw std::out_of_range("Could not convert LeafCertificateType to string");
48 + }
49 + };
50 +
51 + 10std::string leaf_certificate_type_to_filename(LeafCertificateType e) {
52 +
+ 2/5 +
+
✓ Branch 0 taken 6 times.
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
+
+
10 switch (e) {
53 + 6 case LeafCertificateType::CSMS:
54 +
+ 1/2 +
+
✓ Branch 2 taken 6 times.
+
✗ Branch 3 not taken.
+
+
+
6 return "CSMS_LEAF_";
55 + 4 case LeafCertificateType::V2G:
56 +
+ 1/2 +
+
✓ Branch 2 taken 4 times.
+
✗ Branch 3 not taken.
+
+
+
4 return "SECC_LEAF_";
57 + case LeafCertificateType::MF:
58 + return "MF_LEAF_";
59 + case LeafCertificateType::MO:
60 + return "MO_LEAF_";
61 + default:
62 + throw std::out_of_range("Could not convert LeafCertificateType to string");
63 + }
64 + }
65 +
66 + std::string certificate_type_to_string(CertificateType e) {
67 + switch (e) {
68 + case CertificateType::V2GRootCertificate:
69 + return "V2GRootCertificate";
70 + case CertificateType::MORootCertificate:
71 + return "MORootCertificate";
72 + case CertificateType::CSMSRootCertificate:
73 + return "CSMSRootCertificate";
74 + case CertificateType::V2GCertificateChain:
75 + return "V2GCertificateChain";
76 + case CertificateType::MFRootCertificate:
77 + return "MFRootCertificate";
78 + default:
79 + throw std::out_of_range("Could not convert CertificateType to string");
80 + }
81 + };
82 +
83 + 16std::string hash_algorithm_to_string(HashAlgorithm e) {
84 +
+ 1/4 +
+
✓ Branch 0 taken 16 times.
+
✗ Branch 1 not taken.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
16 switch (e) {
85 + 16 case HashAlgorithm::SHA256:
86 +
+ 1/2 +
+
✓ Branch 2 taken 16 times.
+
✗ Branch 3 not taken.
+
+
+
16 return "SHA256";
87 + case HashAlgorithm::SHA384:
88 + return "SHA384";
89 + case HashAlgorithm::SHA512:
90 + return "SHA512";
91 + default:
92 + throw std::out_of_range("Could not convert HashAlgorithm to string");
93 + }
94 + };
95 +
96 + 74HashAlgorithm string_to_hash_algorithm(const std::string& s) {
97 +
+ 1/2 +
+
✓ Branch 1 taken 74 times.
+
✗ Branch 2 not taken.
+
+
+
74 if (s == "SHA256")
98 + 74 return HashAlgorithm::SHA256;
99 + else if (s == "SHA384")
100 + return HashAlgorithm::SHA384;
101 + else if (s == "SHA512")
102 + return HashAlgorithm::SHA512;
103 +
104 + throw std::out_of_range("Could not convert string to HashAlgorithm");
105 + }
106 +
107 + std::string install_certificate_result_to_string(InstallCertificateResult e) {
108 + switch (e) {
109 + case InstallCertificateResult::InvalidSignature:
110 + return "InvalidSignature";
111 + case InstallCertificateResult::InvalidCertificateChain:
112 + return "InvalidCertificateChain";
113 + case InstallCertificateResult::InvalidFormat:
114 + return "InvalidFormat";
115 + case InstallCertificateResult::InvalidCommonName:
116 + return "InvalidCommonName";
117 + case InstallCertificateResult::NoRootCertificateInstalled:
118 + return "NoRootCertificateInstalled";
119 + case InstallCertificateResult::Expired:
120 + return "Expired";
121 + case InstallCertificateResult::CertificateStoreMaxLengthExceeded:
122 + return "CertificateStoreMaxLengthExceeded";
123 + case InstallCertificateResult::WriteError:
124 + return "WriteError";
125 + case InstallCertificateResult::Accepted:
126 + return "Accepted";
127 + default:
128 + throw std::out_of_range("Could not convert InstallCertificateResult to string");
129 + }
130 + };
131 +
132 + std::string delete_certificate_result_to_string(DeleteCertificateResult e) {
133 + switch (e) {
134 + case DeleteCertificateResult::Accepted:
135 + return "Accepted";
136 + case DeleteCertificateResult::Failed:
137 + return "Failed";
138 + case DeleteCertificateResult::NotFound:
139 + return "NotFound";
140 + default:
141 + throw std::out_of_range("Could not convert DeleteCertificateResult to string");
142 + }
143 + };
144 +
145 + std::string get_installed_certificates_status_to_string(GetInstalledCertificatesStatus e) {
146 + switch (e) {
147 + case GetInstalledCertificatesStatus::Accepted:
148 + return "Accepted";
149 + case GetInstalledCertificatesStatus::NotFound:
150 + return "NotFound";
151 + default:
152 + throw std::out_of_range("Could not convert GetInstalledCertificatesStatus to string");
153 + }
154 + };
155 +
156 + std::string get_key_pair_status_to_string(GetCertificateInfoStatus e) {
157 + switch (e) {
158 + case GetCertificateInfoStatus::Accepted:
159 + return "Accepted";
160 + case GetCertificateInfoStatus::Rejected:
161 + return "Rejected";
162 + case GetCertificateInfoStatus::NotFound:
163 + return "NotFound";
164 + case GetCertificateInfoStatus::NotFoundValid:
165 + return "NotFoundValid";
166 + case GetCertificateInfoStatus::PrivateKeyNotFound:
167 + return "PrivateKeyNotFound";
168 + default:
169 + throw std::out_of_range("Could not convert GetCertificateInfoStatus to string");
170 + }
171 + };
172 +
173 + } // namespace conversions
174 +
175 + } // namespace evse_security
176 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_types.hpp.9ba57d272e27e4fa70c5972f3ae3e76b.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_types.hpp.9ba57d272e27e4fa70c5972f3ae3e76b.html new file mode 100644 index 00000000..5c6dd5a8 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.evse_types.hpp.9ba57d272e27e4fa70c5972f3ae3e76b.html @@ -0,0 +1,1684 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:include/evse_security/evse_types.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:171989.5%
Functions:44100.0%
Branches:151883.3%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #pragma once
4 +
5 + #include <algorithm>
6 + #include <cctype>
7 + #include <optional>
8 + #include <string>
9 + #include <vector>
10 +
11 + #include <evse_security/utils/evse_filesystem_types.hpp>
12 +
13 + namespace evse_security {
14 +
15 + const fs::path PEM_EXTENSION = ".pem";
16 + const fs::path DER_EXTENSION = ".der";
17 + const fs::path KEY_EXTENSION = ".key";
18 + const fs::path CUSTOM_KEY_EXTENSION = ".tkey";
19 + const fs::path CERT_HASH_EXTENSION = ".hash";
20 +
21 + enum class EncodingFormat {
22 + DER,
23 + PEM,
24 + };
25 +
26 + enum class CaCertificateType {
27 + V2G,
28 + MO,
29 + CSMS,
30 + MF,
31 + };
32 +
33 + enum class LeafCertificateType {
34 + CSMS,
35 + V2G,
36 + MF,
37 + MO,
38 + };
39 +
40 + enum class CertificateType {
41 + V2GRootCertificate,
42 + MORootCertificate,
43 + CSMSRootCertificate,
44 + V2GCertificateChain,
45 + MFRootCertificate,
46 + };
47 +
48 + enum class HashAlgorithm {
49 + SHA256,
50 + SHA384,
51 + SHA512,
52 + };
53 +
54 + enum class CertificateValidationResult {
55 + Valid,
56 + Expired,
57 + InvalidSignature,
58 + IssuerNotFound,
59 + InvalidLeafSignature,
60 + InvalidChain,
61 + Unknown,
62 + };
63 +
64 + enum class InstallCertificateResult {
65 + InvalidSignature,
66 + InvalidCertificateChain,
67 + InvalidFormat,
68 + InvalidCommonName,
69 + NoRootCertificateInstalled,
70 + Expired,
71 + CertificateStoreMaxLengthExceeded,
72 + WriteError,
73 + Accepted,
74 + };
75 +
76 + enum class DeleteCertificateResult {
77 + Accepted,
78 + Failed,
79 + NotFound,
80 + };
81 +
82 + enum class GetInstalledCertificatesStatus {
83 + Accepted,
84 + NotFound,
85 + };
86 +
87 + enum class GetCertificateInfoStatus {
88 + Accepted,
89 + Rejected,
90 + NotFound,
91 + NotFoundValid,
92 + PrivateKeyNotFound,
93 + };
94 +
95 + enum class GetCertificateSignRequestStatus {
96 + Accepted,
97 + InvalidRequestedType, ///< Requested a CSR for non CSMS/V2G leafs
98 + KeyGenError, ///< The key could not be generated with the requested/default parameters
99 + GenerationError, ///< Any other error when creating the CSR
100 + };
101 +
102 + 246inline bool str_cast_insensitive_cmp(const std::string& a, const std::string& b) {
103 +
+ 2/2 +
+
✓ Branch 2 taken 6 times.
+
✓ Branch 3 taken 240 times.
+
+
+
246 if (a.size() != b.size()) {
104 + 6 return false;
105 + }
106 +
107 + 240 return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](std::string::value_type a, std::string::value_type b) {
108 + 3100 return (std::tolower(a) == std::tolower(b));
109 + 240 });
110 + }
111 +
112 + // types of evse_security
113 +
114 + struct CertificateHashData {
115 + HashAlgorithm hash_algorithm; ///< Algorithm used for the hashes provided
116 + std::string issuer_name_hash; ///< The hash of the issuer's distinguished name (DN), calculated over the DER
117 + ///< encoding of the issuer's name field.
118 + std::string issuer_key_hash; ///< The hash of the DER encoded public key: the value (excluding tag and length) of
119 + ///< the subject public key field
120 + std::string serial_number; ///< The string representation of the hexadecimal value of the serial number without the
121 + ///< prefix "0x" and without leading zeroes.
122 +
123 + 354 bool operator==(const CertificateHashData& Other) const {
124 +
+ 4/4 +
+
✓ Branch 1 taken 138 times.
+
✓ Branch 2 taken 216 times.
+
✓ Branch 3 taken 136 times.
+
✓ Branch 4 taken 2 times.
+
+
+
492 return hash_algorithm == Other.hash_algorithm && issuer_name_hash == Other.issuer_name_hash &&
125 +
+ 3/4 +
+
✓ Branch 0 taken 354 times.
+
✗ Branch 1 not taken.
+
✓ Branch 4 taken 110 times.
+
✓ Branch 5 taken 26 times.
+
+
+
846 issuer_key_hash == Other.issuer_key_hash && serial_number == Other.serial_number;
126 + }
127 +
128 + 202 bool case_insensitive_comparison(const CertificateHashData& Other) const {
129 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 202 times.
+
+
+
202 if (hash_algorithm != Other.hash_algorithm) {
130 + return false;
131 + }
132 +
133 +
+ 2/2 +
+
✓ Branch 1 taken 180 times.
+
✓ Branch 2 taken 22 times.
+
+
+
202 if (false == str_cast_insensitive_cmp(issuer_name_hash, Other.issuer_name_hash)) {
134 + 180 return false;
135 + }
136 +
137 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 22 times.
+
+
+
22 if (false == str_cast_insensitive_cmp(issuer_key_hash, Other.issuer_key_hash)) {
138 + return false;
139 + }
140 +
141 +
+ 2/2 +
+
✓ Branch 1 taken 10 times.
+
✓ Branch 2 taken 12 times.
+
+
+
22 if (false == str_cast_insensitive_cmp(serial_number, Other.serial_number)) {
142 + 10 return false;
143 + }
144 +
145 + 12 return true;
146 + }
147 +
148 + bool is_valid() {
149 + return (false == issuer_name_hash.empty()) && (false == issuer_key_hash.empty()) &&
150 + (false == serial_number.empty());
151 + }
152 + };
153 + struct CertificateHashDataChain {
154 + CertificateType certificate_type; ///< Indicates the type of the certificate for which the hash data is provided
155 + CertificateHashData certificate_hash_data; ///< Contains the hash data of the certificate
156 + std::vector<CertificateHashData>
157 + child_certificate_hash_data; ///< Contains the hash data of the child's certificates
158 + };
159 + struct GetInstalledCertificatesResult {
160 + GetInstalledCertificatesStatus status; ///< Indicates the status of the request
161 + std::vector<CertificateHashDataChain>
162 + certificate_hash_data_chain; ///< the hashed certificate data for each requested certificates
163 + };
164 + struct OCSPRequestData {
165 + std::optional<CertificateHashData> certificate_hash_data; ///< Contains the hash data of the certificate
166 + std::optional<std::string> responder_url; ///< Contains the responder URL
167 + };
168 + struct OCSPRequestDataList {
169 + std::vector<OCSPRequestData> ocsp_request_data_list; ///< A list of OCSP request data
170 + };
171 +
172 + struct CertificateOCSP {
173 + CertificateHashData hash; ///< Hash of the certificate for which the OCSP data is held
174 + std::optional<fs::path> ocsp_path; ///< Path to the file in which the certificate OCSP data is held
175 + };
176 +
177 + struct CertificateInfo {
178 + fs::path key; ///< The path of the PEM or DER encoded private key
179 + std::optional<std::string> certificate_root; ///< The PEM of root certificate used by the leaf, has a value only
180 + /// when using 'get_all_valid_certificates_info'
181 + std::optional<fs::path> certificate; ///< The path of the PEM or DER encoded certificate chain if found
182 + std::optional<fs::path> certificate_single; ///< The path of the PEM or DER encoded single certificate if found
183 + int certificate_count; ///< The count of certificates, if the chain is available, or 1 if single
184 + /// (the root is not taken into account because of the OCSP cache)
185 + std::optional<std::string> password; ///< Specifies the password for the private key if encrypted
186 + std::vector<CertificateOCSP> ocsp; ///< The ordered list of OCSP certificate data based on the chain file order
187 + };
188 +
189 + struct GetCertificateInfoResult {
190 + GetCertificateInfoStatus status;
191 + std::optional<CertificateInfo> info;
192 + };
193 +
194 + struct GetCertificateFullInfoResult {
195 + GetCertificateInfoStatus status;
196 + std::vector<CertificateInfo> info;
197 + };
198 +
199 + struct GetCertificateSignRequestResult {
200 + GetCertificateSignRequestStatus status;
201 + std::optional<std::string> csr;
202 + };
203 +
204 + namespace conversions {
205 + std::string encoding_format_to_string(EncodingFormat e);
206 + std::string ca_certificate_type_to_string(CaCertificateType e);
207 + std::string leaf_certificate_type_to_string(LeafCertificateType e);
208 + std::string leaf_certificate_type_to_filename(LeafCertificateType e);
209 + std::string certificate_type_to_string(CertificateType e);
210 + std::string hash_algorithm_to_string(HashAlgorithm e);
211 + HashAlgorithm string_to_hash_algorithm(const std::string& s);
212 + std::string install_certificate_result_to_string(InstallCertificateResult e);
213 + std::string delete_certificate_result_to_string(DeleteCertificateResult e);
214 + std::string get_installed_certificates_status_to_string(GetInstalledCertificatesStatus e);
215 + std::string get_certificate_info_status_to_string(GetCertificateInfoStatus e);
216 + } // namespace conversions
217 +
218 + } // namespace evse_security
219 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.functions.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.functions.html new file mode 100644 index 00000000..00d454a6 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.functions.html @@ -0,0 +1,354 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + +
Directory:./
Date:2024-12-18 15:37:36
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:1758288860.9%
Functions:19427670.3%
Branches:2195915824.0%
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function (File:Line)Call countBlock coverage
auto evse_security::X509CertificateBundle::delete_certificate(evse_security::X509Wrapper const&, bool)::{lambda(auto:1 const&)#1}::operator()<evse_security::X509Wrapper>(evse_security::X509Wrapper const&) const (lib/evse_security/certificate/x509_bundle.cpp:183)called 44 times91.0%
bool evse_security::base64_decode<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:810)called 2 times16.0%
bool evse_security::base64_decode<std::vector<unsigned char, std::allocator<unsigned char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<unsigned char, std::allocator<unsigned char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:810)not called0.0%
evse_security::AbstractCryptoSupplier::base64_decode_to_bytes(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<unsigned char, std::allocator<unsigned char> >&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:103)not called0.0%
evse_security::AbstractCryptoSupplier::base64_decode_to_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:108)not called0.0%
evse_security::AbstractCryptoSupplier::base64_encode_from_bytes(std::vector<unsigned char, std::allocator<unsigned char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:112)not called0.0%
evse_security::AbstractCryptoSupplier::base64_encode_from_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:117)not called0.0%
evse_security::AbstractCryptoSupplier::digest_file_sha256(std::filesystem::__cxx11::path const&, std::vector<unsigned char, std::allocator<unsigned char> >&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:99)not called0.0%
evse_security::AbstractCryptoSupplier::generate_key(evse_security::KeyGenerationInfo const&, std::unique_ptr<evse_security::KeyHandle, std::default_delete<evse_security::KeyHandle> >&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:23)not called0.0%
evse_security::AbstractCryptoSupplier::get_supplier_name() (lib/evse_security/crypto/interface/crypto_supplier.cpp:11)not called0.0%
evse_security::AbstractCryptoSupplier::load_certificates(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::EncodingFormat) (lib/evse_security/crypto/interface/crypto_supplier.cpp:28)not called0.0%
evse_security::AbstractCryptoSupplier::supports_tpm() (lib/evse_security/crypto/interface/crypto_supplier.cpp:15)not called0.0%
evse_security::AbstractCryptoSupplier::supports_tpm_key_creation() (lib/evse_security/crypto/interface/crypto_supplier.cpp:19)not called0.0%
evse_security::AbstractCryptoSupplier::x509_check_private_key(evse_security::X509Handle*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >) (lib/evse_security/crypto/interface/crypto_supplier.cpp:84)not called0.0%
evse_security::AbstractCryptoSupplier::x509_generate_csr(evse_security::CertificateSigningRequestInfo const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:94)not called0.0%
evse_security::AbstractCryptoSupplier::x509_get_common_name[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:53)not called0.0%
evse_security::AbstractCryptoSupplier::x509_get_issuer_name_hash[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:49)not called0.0%
evse_security::AbstractCryptoSupplier::x509_get_key_hash[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:41)not called0.0%
evse_security::AbstractCryptoSupplier::x509_get_responder_url[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:37)not called0.0%
evse_security::AbstractCryptoSupplier::x509_get_serial_number[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:45)not called0.0%
evse_security::AbstractCryptoSupplier::x509_get_validity(evse_security::X509Handle*, long&, long&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:57)not called0.0%
evse_security::AbstractCryptoSupplier::x509_is_child(evse_security::X509Handle*, evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:66)not called0.0%
evse_security::AbstractCryptoSupplier::x509_is_equal(evse_security::X509Handle*, evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:70)not called0.0%
evse_security::AbstractCryptoSupplier::x509_is_selfsigned(evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:62)not called0.0%
evse_security::AbstractCryptoSupplier::x509_to_string[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:33)not called0.0%
evse_security::AbstractCryptoSupplier::x509_verify_certificate_chain(evse_security::X509Handle*, std::vector<evse_security::X509Handle*, std::allocator<evse_security::X509Handle*> > const&, std::vector<evse_security::X509Handle*, std::allocator<evse_security::X509Handle*> > const&, bool, std::optional<std::filesystem::__cxx11::path>, std::optional<std::filesystem::__cxx11::path>) (lib/evse_security/crypto/interface/crypto_supplier.cpp:78)not called0.0%
evse_security::AbstractCryptoSupplier::x509_verify_signature(evse_security::X509Handle*, std::vector<unsigned char, std::allocator<unsigned char> > const&, std::vector<unsigned char, std::allocator<unsigned char> > const&) (lib/evse_security/crypto/interface/crypto_supplier.cpp:89)not called0.0%
evse_security::CertificateHashData::case_insensitive_comparison(evse_security::CertificateHashData const&) const (include/evse_security/evse_types.hpp:128)called 202 times85.0%
evse_security::CertificateHashData::operator==(evse_security::CertificateHashData const&) const (include/evse_security/evse_types.hpp:123)called 354 times100.0%
evse_security::CryptoHandle::~CryptoHandle() (include/evse_security/crypto/interface/crypto_types.hpp:76)called 7158 times100.0%
evse_security::EvseSecurity::EvseSecurity(evse_security::FilePaths const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, std::optional<unsigned long> const&, std::optional<unsigned long> const&, std::optional<std::chrono::duration<long, std::ratio<1l, 1l> > > const&, std::optional<std::chrono::duration<long, std::ratio<1l, 1l> > > const&) (lib/evse_security/evse_security.cpp:230)called 68 times23.0%
evse_security::EvseSecurity::EvseSecurity(evse_security::FilePaths const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, std::optional<unsigned long> const&, std::optional<unsigned long> const&, std::optional<std::chrono::duration<long, std::ratio<1l, 1l> > > const&, std::optional<std::chrono::duration<long, std::ratio<1l, 1l> > > const&)::{lambda()#1}::operator()() const (lib/evse_security/evse_security.cpp:292)not called0.0%
evse_security::EvseSecurity::base64_decode_to_bytes(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/evse_security.cpp:1662)not called0.0%
evse_security::EvseSecurity::base64_decode_to_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/evse_security.cpp:1672)called 2 times73.0%
evse_security::EvseSecurity::base64_encode_from_bytes[abi:cxx11](std::vector<unsigned char, std::allocator<unsigned char> > const&) (lib/evse_security/evse_security.cpp:1682)not called0.0%
evse_security::EvseSecurity::base64_encode_from_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/evse_security.cpp:1692)called 2 times73.0%
evse_security::EvseSecurity::certificate_signing_request_failed(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::LeafCertificateType) (lib/evse_security/evse_security.cpp:993)not called0.0%
evse_security::EvseSecurity::delete_certificate(evse_security::CertificateHashData const&) (lib/evse_security/evse_security.cpp:362)called 10 times42.0%
evse_security::EvseSecurity::garbage_collect() (lib/evse_security/evse_security.cpp:1798)called 14 times44.0%
evse_security::EvseSecurity::garbage_collect()::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}::operator()(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) const (lib/evse_security/evse_security.cpp:1836)called 64 times38.0%
evse_security::EvseSecurity::garbage_collect()::{lambda(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}::operator()(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) const (lib/evse_security/evse_security.cpp:1913)called 322 times86.0%
evse_security::EvseSecurity::generate_certificate_signing_request(evse_security::LeafCertificateType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/evse_security.cpp:1085)called 4 times100.0%
evse_security::EvseSecurity::generate_certificate_signing_request(evse_security::LeafCertificateType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool) (lib/evse_security/evse_security.cpp:1031)called 4 times35.0%
evse_security::EvseSecurity::generate_certificate_signing_request_internal(evse_security::LeafCertificateType, evse_security::CertificateSigningRequestInfo const&) (lib/evse_security/evse_security.cpp:998)called 4 times41.0%
evse_security::EvseSecurity::get_all_valid_certificates_info(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool) (lib/evse_security/evse_security.cpp:1092)called 2 times68.0%
evse_security::EvseSecurity::get_ca_certificate_info(evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:1540)not called0.0%
evse_security::EvseSecurity::get_ca_certificate_info_internal(evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:1492)called 4 times21.0%
evse_security::EvseSecurity::get_count_of_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&) (lib/evse_security/evse_security.cpp:674)called 8 times29.0%
evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool) (lib/evse_security/evse_security.cpp:1154)called 20 times32.0%
evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}::operator()(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) const (lib/evse_security/evse_security.cpp:1205)called 24 times64.0%
evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#3}::operator()(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) const (lib/evse_security/evse_security.cpp:1275)called 44 times95.0%
evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}::operator()(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) const (lib/evse_security/evse_security.cpp:1236)called 50 times100.0%
evse_security::EvseSecurity::get_installed_certificate(evse_security::CertificateType) (lib/evse_security/evse_security.cpp:552)not called0.0%
evse_security::EvseSecurity::get_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&) (lib/evse_security/evse_security.cpp:557)called 16 times47.0%
evse_security::EvseSecurity::get_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&)::{lambda(evse_security::X509Node const&, int)#1}::operator()(evse_security::X509Node const&, int) const (lib/evse_security/evse_security.cpp:584)called 74 times100.0%
evse_security::EvseSecurity::get_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&)::{lambda(evse_security::X509Node const&, int)#2}::operator()(evse_security::X509Node const&, int) const (lib/evse_security/evse_security.cpp:642)called 26 times100.0%
evse_security::EvseSecurity::get_leaf_certificate_info(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool) (lib/evse_security/evse_security.cpp:1133)called 6 times78.0%
evse_security::EvseSecurity::get_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool) (lib/evse_security/evse_security.cpp:1140)called 18 times75.0%
evse_security::EvseSecurity::get_leaf_expiry_days_count(evse_security::LeafCertificateType) (lib/evse_security/evse_security.cpp:1590)not called0.0%
evse_security::EvseSecurity::get_mo_ocsp_request_data(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/evse_security.cpp:748)not called0.0%
evse_security::EvseSecurity::get_v2g_ocsp_request_data() (lib/evse_security/evse_security.cpp:713)called 4 times19.0%
evse_security::EvseSecurity::get_verify_file[abi:cxx11](evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:1546)called 4 times76.0%
evse_security::EvseSecurity::get_verify_location[abi:cxx11](evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:1560)called 2 times27.0%
evse_security::EvseSecurity::install_ca_certificate(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:298)called 34 times49.0%
evse_security::EvseSecurity::is_ca_certificate_installed(evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:965)not called0.0%
evse_security::EvseSecurity::is_ca_certificate_installed_internal(evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:971)called 14 times29.0%
evse_security::EvseSecurity::is_filesystem_full() (lib/evse_security/evse_security.cpp:2071)called 72 times61.0%
evse_security::EvseSecurity::retrieve_ocsp_cache[abi:cxx11](evse_security::CertificateHashData const&) (lib/evse_security/evse_security.cpp:910)called 12 times78.0%
evse_security::EvseSecurity::retrieve_ocsp_cache_internal[abi:cxx11](evse_security::CertificateHashData const&) (lib/evse_security/evse_security.cpp:916)called 18 times40.0%
evse_security::EvseSecurity::update_certificate_links(evse_security::LeafCertificateType) (lib/evse_security/evse_security.cpp:1404)not called0.0%
evse_security::EvseSecurity::update_leaf_certificate(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::LeafCertificateType) (lib/evse_security/evse_security.cpp:450)called 12 times22.0%
evse_security::EvseSecurity::update_ocsp_cache(evse_security::CertificateHashData const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/evse_security.cpp:824)called 12 times43.0%
evse_security::EvseSecurity::verify_certificate(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::LeafCertificateType) (lib/evse_security/evse_security.cpp:1702)called 4 times78.0%
evse_security::EvseSecurity::verify_certificate_internal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::LeafCertificateType) (lib/evse_security/evse_security.cpp:1709)called 14 times34.0%
evse_security::EvseSecurity::verify_file_signature(std::filesystem::__cxx11::path const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (lib/evse_security/evse_security.cpp:1624)not called0.0%
evse_security::EvseSecurity::~EvseSecurity() (lib/evse_security/evse_security.cpp:295)called 68 times100.0%
evse_security::KeyHandleOpenSSL::KeyHandleOpenSSL(evp_pkey_st*) (include/evse_security/crypto/openssl/openssl_types.hpp:111)called 26 times100.0%
evse_security::KeyHandleOpenSSL::get() (include/evse_security/crypto/openssl/openssl_types.hpp:114)called 14 times100.0%
evse_security::OpenSSLProvider::OpenSSLProvider() (lib/evse_security/crypto/openssl/openssl_provider.cpp:254)called 244 times100.0%
evse_security::OpenSSLProvider::cleanup() (lib/evse_security/crypto/openssl/openssl_provider.cpp:276)not called0.0%
evse_security::OpenSSLProvider::load(ossl_provider_st*&, ossl_provider_st*&, ossl_lib_ctx_st*, evse_security::OpenSSLProvider::mode_t) (lib/evse_security/crypto/openssl/openssl_provider.cpp:260)not called0.0%
evse_security::OpenSSLProvider::propquery(evse_security::OpenSSLProvider::mode_t) const (lib/evse_security/crypto/openssl/openssl_provider.cpp:272)not called0.0%
evse_security::OpenSSLProvider::set_global_mode(evse_security::OpenSSLProvider::mode_t) (include/evse_security/crypto/openssl/openssl_provider.hpp:128)called 244 times100.0%
evse_security::OpenSSLProvider::set_mode(ossl_lib_ctx_st*, evse_security::OpenSSLProvider::mode_t) (lib/evse_security/crypto/openssl/openssl_provider.cpp:268)called 244 times100.0%
evse_security::OpenSSLProvider::set_propstr(ossl_lib_ctx_st*, evse_security::OpenSSLProvider::mode_t) (lib/evse_security/crypto/openssl/openssl_provider.cpp:264)not called0.0%
evse_security::OpenSSLProvider::supports_provider_custom() (lib/evse_security/crypto/openssl/openssl_provider.cpp:283)not called0.0%
evse_security::OpenSSLProvider::supports_provider_tpm() (lib/evse_security/crypto/openssl/openssl_provider.cpp:279)not called0.0%
evse_security::OpenSSLProvider::~OpenSSLProvider() (lib/evse_security/crypto/openssl/openssl_provider.cpp:257)called 244 times100.0%
evse_security::OpenSSLSupplier::base64_decode_to_bytes(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<unsigned char, std::allocator<unsigned char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:886)not called0.0%
evse_security::OpenSSLSupplier::base64_decode_to_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:890)called 2 times100.0%
evse_security::OpenSSLSupplier::base64_encode_from_bytes(std::vector<unsigned char, std::allocator<unsigned char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:894)not called0.0%
evse_security::OpenSSLSupplier::base64_encode_from_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:898)called 2 times100.0%
evse_security::OpenSSLSupplier::digest_file_sha256(std::filesystem::__cxx11::path const&, std::vector<unsigned char, std::allocator<unsigned char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:758)not called0.0%
evse_security::OpenSSLSupplier::digest_file_sha256(std::filesystem::__cxx11::path const&, std::vector<unsigned char, std::allocator<unsigned char> >&)::{lambda(unsigned char const*, unsigned long, bool)#1}::operator()(unsigned char const*, unsigned long, bool) const (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:778)not called0.0%
evse_security::OpenSSLSupplier::generate_key(evse_security::KeyGenerationInfo const&, std::unique_ptr<evse_security::KeyHandle, std::default_delete<evse_security::KeyHandle> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:214)called 12 times22.0%
evse_security::OpenSSLSupplier::get_supplier_name() (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:64)not called0.0%
evse_security::OpenSSLSupplier::load_certificates(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::EncodingFormat) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:234)called 950 times51.0%
evse_security::OpenSSLSupplier::supports_tpm_key_creation() (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:68)not called0.0%
evse_security::OpenSSLSupplier::x509_check_private_key(evse_security::X509Handle*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:556)called 202 times44.0%
evse_security::OpenSSLSupplier::x509_duplicate_unique(evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:480)called 5488 times89.0%
evse_security::OpenSSLSupplier::x509_generate_csr(evse_security::CertificateSigningRequestInfo const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:643)called 14 times34.0%
evse_security::OpenSSLSupplier::x509_generate_csr(evse_security::CertificateSigningRequestInfo const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)::{lambda(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)#1}::operator()(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:717)called 2 times80.0%
evse_security::OpenSSLSupplier::x509_get_common_name[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:293)called 324 times76.0%
evse_security::OpenSSLSupplier::x509_get_issuer_name_hash[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:319)called 1106 times88.0%
evse_security::OpenSSLSupplier::x509_get_key_hash[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:374)called 1104 times87.0%
evse_security::OpenSSLSupplier::x509_get_responder_url[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:390)called 20 times67.0%
evse_security::OpenSSLSupplier::x509_get_serial_number[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:336)called 1104 times67.0%
evse_security::OpenSSLSupplier::x509_get_validity(evse_security::X509Handle*, long&, long&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:409)called 1632 times76.0%
evse_security::OpenSSLSupplier::x509_is_child(evse_security::X509Handle*, evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:431)called 3066 times72.0%
evse_security::OpenSSLSupplier::x509_is_equal(evse_security::X509Handle*, evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:476)called 392 times100.0%
evse_security::OpenSSLSupplier::x509_is_selfsigned(evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:467)called 4592 times83.0%
evse_security::OpenSSLSupplier::x509_to_string[abi:cxx11](evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:276)called 170 times66.0%
evse_security::OpenSSLSupplier::x509_verify_certificate_chain(evse_security::X509Handle*, std::vector<evse_security::X509Handle*, std::allocator<evse_security::X509Handle*> > const&, std::vector<evse_security::X509Handle*, std::allocator<evse_security::X509Handle*> > const&, bool, std::optional<std::filesystem::__cxx11::path>, std::optional<std::filesystem::__cxx11::path>) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:484)called 16 times29.0%
evse_security::OpenSSLSupplier::x509_verify_signature(evse_security::X509Handle*, std::vector<unsigned char, std::allocator<unsigned char> > const&, std::vector<unsigned char, std::allocator<unsigned char> > const&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:594)not called0.0%
evse_security::X509CertificateBundle::X509CertificateBundle(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::EncodingFormat) (lib/evse_security/certificate/x509_bundle.cpp:39)called 28 times50.0%
evse_security::X509CertificateBundle::X509CertificateBundle(std::filesystem::__cxx11::path const&, evse_security::EncodingFormat) (lib/evse_security/certificate/x509_bundle.cpp:44)called 488 times56.0%
evse_security::X509CertificateBundle::add_certificate(evse_security::X509Wrapper&&) (lib/evse_security/certificate/x509_bundle.cpp:220)called 36 times18.0%
evse_security::X509CertificateBundle::add_certificate_unique(evse_security::X509Wrapper&&) (lib/evse_security/certificate/x509_bundle.cpp:239)called 24 times100.0%
evse_security::X509CertificateBundle::add_certificates(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::EncodingFormat, std::optional<std::filesystem::__cxx11::path> const&) (lib/evse_security/certificate/x509_bundle.cpp:98)called 870 times71.0%
evse_security::X509CertificateBundle::contains_certificate(evse_security::CertificateHashData const&) (lib/evse_security/certificate/x509_bundle.cpp:123)not called0.0%
evse_security::X509CertificateBundle::contains_certificate(evse_security::CertificateHashData const&)::{lambda(evse_security::X509Wrapper const&)#1}::operator()(evse_security::X509Wrapper const&) const (lib/evse_security/certificate/x509_bundle.cpp:126)not called0.0%
evse_security::X509CertificateBundle::contains_certificate(evse_security::X509Wrapper const&) (lib/evse_security/certificate/x509_bundle.cpp:111)called 54 times95.0%
evse_security::X509CertificateBundle::delete_all_certificates() (lib/evse_security/certificate/x509_bundle.cpp:216)not called0.0%
evse_security::X509CertificateBundle::delete_certificate(evse_security::CertificateHashData const&, bool) (lib/evse_security/certificate/x509_bundle.cpp:203)called 42 times80.0%
evse_security::X509CertificateBundle::delete_certificate(evse_security::X509Wrapper const&, bool) (lib/evse_security/certificate/x509_bundle.cpp:166)called 12 times77.0%
evse_security::X509CertificateBundle::empty() const (include/evse_security/certificate/x509_bundle.hpp:63)called 22 times100.0%
evse_security::X509CertificateBundle::export_certificates() (lib/evse_security/certificate/x509_bundle.cpp:261)called 36 times26.0%
evse_security::X509CertificateBundle::find_certificate(evse_security::CertificateHashData const&, bool) (lib/evse_security/certificate/x509_bundle.cpp:139)not called0.0%
evse_security::X509CertificateBundle::get_certificate_chains_count() const (lib/evse_security/certificate/x509_bundle.cpp:94)called 32 times100.0%
evse_security::X509CertificateBundle::get_certificate_count() const (lib/evse_security/certificate/x509_bundle.cpp:85)called 8 times100.0%
evse_security::X509CertificateBundle::get_certificate_hierarchy() (lib/evse_security/certificate/x509_bundle.cpp:359)called 140 times64.0%
evse_security::X509CertificateBundle::get_latest_valid_certificate() (lib/evse_security/certificate/x509_bundle.cpp:351)not called0.0%
evse_security::X509CertificateBundle::get_latest_valid_certificate(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) (lib/evse_security/certificate/x509_bundle.cpp:17)not called0.0%
evse_security::X509CertificateBundle::get_latest_valid_certificate(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)::{lambda(evse_security::X509Wrapper const&, evse_security::X509Wrapper const&)#1}::operator()(evse_security::X509Wrapper const&, evse_security::X509Wrapper const&) const (lib/evse_security/certificate/x509_bundle.cpp:34)not called0.0%
evse_security::X509CertificateBundle::get_path[abi:cxx11]() const (include/evse_security/certificate/x509_bundle.hpp:73)called 70 times100.0%
evse_security::X509CertificateBundle::invalidate_hierarchy() (lib/evse_security/certificate/x509_bundle.cpp:355)called 48 times100.0%
evse_security::X509CertificateBundle::is_certificate_file(std::filesystem::__cxx11::path const&) (include/evse_security/certificate/x509_bundle.hpp:181)called 1320 times71.0%
evse_security::X509CertificateBundle::is_using_bundle_file() const (include/evse_security/certificate/x509_bundle.hpp:48)called 58 times100.0%
evse_security::X509CertificateBundle::is_using_directory() const (include/evse_security/certificate/x509_bundle.hpp:54)called 34 times100.0%
evse_security::X509CertificateBundle::split() (lib/evse_security/certificate/x509_bundle.cpp:73)called 406 times85.0%
evse_security::X509CertificateBundle::sync_to_certificate_store() (lib/evse_security/certificate/x509_bundle.cpp:297)called 38 times44.0%
evse_security::X509CertificateBundle::to_export_string(std::filesystem::__cxx11::path const&) const (lib/evse_security/certificate/x509_bundle.cpp:383)called 2 times77.0%
evse_security::X509CertificateBundle::to_export_string[abi:cxx11]() const (lib/evse_security/certificate/x509_bundle.cpp:371)called 34 times76.0%
evse_security::X509CertificateBundle::update_certificate(evse_security::X509Wrapper&&) (lib/evse_security/certificate/x509_bundle.cpp:246)not called0.0%
evse_security::X509CertificateHierarchy evse_security::X509CertificateHierarchy::build_hierarchy<std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > >(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >) (include/evse_security/certificate/x509_hierarchy.hpp:122)called 132 times92.0%
evse_security::X509CertificateHierarchy::build_hierarchy(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >&) (lib/evse_security/certificate/x509_hierarchy.cpp:317)called 126 times92.0%
evse_security::X509CertificateHierarchy::build_hierarchy<std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > >(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >)::{lambda(evse_security::X509Wrapper&)#1}::operator()(evse_security::X509Wrapper&) const (include/evse_security/certificate/x509_hierarchy.hpp:126)called 388 times100.0%
evse_security::X509CertificateHierarchy::build_hierarchy<std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > >(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >)::{lambda(evse_security::X509Wrapper&)#2}::operator()(evse_security::X509Wrapper&) const (include/evse_security/certificate/x509_hierarchy.hpp:126)called 322 times100.0%
evse_security::X509CertificateHierarchy::collect_descendants(evse_security::X509Wrapper const&) (lib/evse_security/certificate/x509_hierarchy.cpp:19)called 16 times83.0%
evse_security::X509CertificateHierarchy::collect_descendants(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&)#1}::operator()(evse_security::X509Node const&) const (lib/evse_security/certificate/x509_hierarchy.cpp:22)called 34 times100.0%
evse_security::X509CertificateHierarchy::collect_descendants(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&)#1}::operator()(evse_security::X509Node const&) const::{lambda(evse_security::X509Node const&, int)#1}::operator()(evse_security::X509Node const&, int) const (lib/evse_security/certificate/x509_hierarchy.cpp:28)called 26 times100.0%
evse_security::X509CertificateHierarchy::contains_certificate_hash(evse_security::CertificateHashData const&) (lib/evse_security/certificate/x509_hierarchy.cpp:67)called 24 times100.0%
evse_security::X509CertificateHierarchy::contains_certificate_hash(evse_security::CertificateHashData const&)::{lambda(evse_security::X509Node const&)#1}::operator()(evse_security::X509Node const&) const (lib/evse_security/certificate/x509_hierarchy.cpp:70)called 46 times100.0%
evse_security::X509CertificateHierarchy::find_certificate(evse_security::CertificateHashData const&, bool) (lib/evse_security/certificate/x509_hierarchy.cpp:104)called 80 times71.0%
evse_security::X509CertificateHierarchy::find_certificate(evse_security::CertificateHashData const&, bool)::{lambda(evse_security::X509Node&)#1}::operator()(evse_security::X509Node&) const (lib/evse_security/certificate/x509_hierarchy.cpp:108)called 262 times100.0%
evse_security::X509CertificateHierarchy::find_certificate_root(evse_security::X509Wrapper const&) (lib/evse_security/certificate/x509_hierarchy.cpp:82)called 4 times47.0%
evse_security::X509CertificateHierarchy::find_certificate_root(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&, int)#1}::operator()(evse_security::X509Node const&, int) const (lib/evse_security/certificate/x509_hierarchy.cpp:88)called 16 times100.0%
evse_security::X509CertificateHierarchy::find_certificates_multi(evse_security::CertificateHashData const&) (lib/evse_security/certificate/x509_hierarchy.cpp:131)called 12 times83.0%
evse_security::X509CertificateHierarchy::find_certificates_multi(evse_security::CertificateHashData const&)::{lambda(evse_security::X509Node&)#1}::operator()(evse_security::X509Node&) const (lib/evse_security/certificate/x509_hierarchy.cpp:134)called 84 times100.0%
evse_security::X509CertificateHierarchy::get_certificate_hash(evse_security::X509Wrapper const&) (lib/evse_security/certificate/x509_hierarchy.cpp:41)called 68 times41.0%
evse_security::X509CertificateHierarchy::get_certificate_hash(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&)#1}::operator()(evse_security::X509Node const&) const (lib/evse_security/certificate/x509_hierarchy.cpp:50)called 76 times100.0%
evse_security::X509CertificateHierarchy::get_hierarchy() const (include/evse_security/certificate/x509_hierarchy.hpp:43)called 68 times100.0%
evse_security::X509CertificateHierarchy::insert(evse_security::X509Wrapper&&) (lib/evse_security/certificate/x509_hierarchy.cpp:169)called 1060 times62.0%
evse_security::X509CertificateHierarchy::insert(evse_security::X509Wrapper&&)::{lambda(evse_security::X509Node&)#1}::operator()(evse_security::X509Node&) const (lib/evse_security/certificate/x509_hierarchy.cpp:181)called 1146 times66.0%
evse_security::X509CertificateHierarchy::is_internal_root(evse_security::X509Wrapper const&) const (lib/evse_security/certificate/x509_hierarchy.cpp:9)not called0.0%
evse_security::X509CertificateHierarchy::is_internal_root(evse_security::X509Wrapper const&) const::{lambda(evse_security::X509Node const&)#1}::operator()(evse_security::X509Node const&) const (lib/evse_security/certificate/x509_hierarchy.cpp:11)not called0.0%
evse_security::X509CertificateHierarchy::prune() (lib/evse_security/certificate/x509_hierarchy.cpp:276)called 258 times89.0%
evse_security::X509CertificateHierarchy::prune()::{lambda(evse_security::X509Node&)#1}::operator()(evse_security::X509Node&) const (lib/evse_security/certificate/x509_hierarchy.cpp:291)called 50 times93.0%
evse_security::X509CertificateHierarchy::to_debug_string[abi:cxx11]() (lib/evse_security/certificate/x509_hierarchy.cpp:145)called 80 times79.0%
evse_security::X509CertificateHierarchy::to_debug_string[abi:cxx11]()::{lambda(evse_security::X509Node const&, int)#1}::operator()(evse_security::X509Node const&, int) const (lib/evse_security/certificate/x509_hierarchy.cpp:157)called 216 times83.0%
evse_security::X509HandleOpenSSL::X509HandleOpenSSL(x509_st*) (include/evse_security/crypto/openssl/openssl_types.hpp:99)called 7132 times100.0%
evse_security::X509HandleOpenSSL::get() (include/evse_security/crypto/openssl/openssl_types.hpp:102)called 22704 times100.0%
evse_security::X509Wrapper::X509Wrapper(evse_security::X509Wrapper const&) (lib/evse_security/certificate/x509_wrapper.cpp:62)called 5488 times80.0%
evse_security::X509Wrapper::X509Wrapper(evse_security::X509Wrapper&&) (include/evse_security/certificate/x509_wrapper.hpp:34)called 4724 times100.0%
evse_security::X509Wrapper::X509Wrapper(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, evse_security::EncodingFormat) (lib/evse_security/certificate/x509_wrapper.cpp:37)called 72 times41.0%
evse_security::X509Wrapper::X509Wrapper(std::filesystem::__cxx11::path const&, evse_security::EncodingFormat) (lib/evse_security/certificate/x509_wrapper.cpp:16)not called0.0%
evse_security::X509Wrapper::X509Wrapper(std::unique_ptr<evse_security::X509Handle, std::default_delete<evse_security::X509Handle> >&&) (lib/evse_security/certificate/x509_wrapper.cpp:50)called 32 times67.0%
evse_security::X509Wrapper::X509Wrapper(std::unique_ptr<evse_security::X509Handle, std::default_delete<evse_security::X509Handle> >&&, std::filesystem::__cxx11::path const&) (lib/evse_security/certificate/x509_wrapper.cpp:54)called 1530 times50.0%
evse_security::X509Wrapper::get() const (include/evse_security/certificate/x509_wrapper.hpp:47)called 19614 times100.0%
evse_security::X509Wrapper::get_certificate_hash_data() const (lib/evse_security/certificate/x509_wrapper.cpp:159)called 380 times88.0%
evse_security::X509Wrapper::get_certificate_hash_data(evse_security::X509Wrapper const&) const (lib/evse_security/certificate/x509_wrapper.cpp:168)called 728 times77.0%
evse_security::X509Wrapper::get_common_name[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:134)called 324 times100.0%
evse_security::X509Wrapper::get_export_string[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:193)called 170 times100.0%
evse_security::X509Wrapper::get_file[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:115)called 488 times100.0%
evse_security::X509Wrapper::get_issuer_key_hash[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:146)called 380 times89.0%
evse_security::X509Wrapper::get_issuer_name_hash[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:138)called 1106 times100.0%
evse_security::X509Wrapper::get_key_hash[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:155)called 1104 times100.0%
evse_security::X509Wrapper::get_responder_url[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:189)called 20 times100.0%
evse_security::X509Wrapper::get_serial_number[abi:cxx11]() const (lib/evse_security/certificate/x509_wrapper.cpp:142)called 1104 times100.0%
evse_security::X509Wrapper::get_source() const (lib/evse_security/certificate/x509_wrapper.cpp:126)not called0.0%
evse_security::X509Wrapper::get_valid_in() const (lib/evse_security/certificate/x509_wrapper.cpp:97)called 72 times100.0%
evse_security::X509Wrapper::get_valid_to() const (lib/evse_security/certificate/x509_wrapper.cpp:102)called 1486 times100.0%
evse_security::X509Wrapper::is_child(evse_security::X509Wrapper const&) const (lib/evse_security/certificate/x509_wrapper.cpp:85)called 2350 times100.0%
evse_security::X509Wrapper::is_expired() const (lib/evse_security/certificate/x509_wrapper.cpp:111)called 44 times100.0%
evse_security::X509Wrapper::is_selfsigned() const (lib/evse_security/certificate/x509_wrapper.cpp:93)called 1526 times100.0%
evse_security::X509Wrapper::is_valid() const (lib/evse_security/certificate/x509_wrapper.cpp:106)called 72 times100.0%
evse_security::X509Wrapper::operator=(evse_security::X509Wrapper&&) (include/evse_security/certificate/x509_wrapper.hpp:117)called 1510 times100.0%
evse_security::X509Wrapper::operator==(evse_security::CertificateHashData const&) const (include/evse_security/certificate/x509_wrapper.hpp:122)not called0.0%
evse_security::X509Wrapper::operator==(evse_security::X509Wrapper const&) const (lib/evse_security/certificate/x509_wrapper.cpp:72)called 396 times100.0%
evse_security::X509Wrapper::set_file(std::filesystem::__cxx11::path&) (lib/evse_security/certificate/x509_wrapper.cpp:119)not called0.0%
evse_security::X509Wrapper::update_validity() (lib/evse_security/certificate/x509_wrapper.cpp:79)called 1632 times12.0%
evse_security::X509Wrapper::~X509Wrapper() (lib/evse_security/certificate/x509_wrapper.cpp:69)called 11844 times100.0%
evse_security::add_entry(int, unsigned int, char const*, unsigned char const*, int, unsigned short) (3rd_party/cert_rehash/c_rehash.hpp:66)not called0.0%
evse_security::base64_encode(unsigned char const*, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:848)called 2 times16.0%
evse_security::bit_isset(unsigned char*, unsigned int) (3rd_party/cert_rehash/c_rehash.hpp:62)not called0.0%
evse_security::bit_set(unsigned char*, unsigned int) (3rd_party/cert_rehash/c_rehash.hpp:58)not called0.0%
evse_security::conversions::ca_certificate_type_to_string[abi:cxx11](evse_security::CaCertificateType) (lib/evse_security/evse_types.cpp:21)called 80 times54.0%
evse_security::conversions::certificate_type_to_string[abi:cxx11](evse_security::CertificateType) (lib/evse_security/evse_types.cpp:66)not called0.0%
evse_security::conversions::delete_certificate_result_to_string[abi:cxx11](evse_security::DeleteCertificateResult) (lib/evse_security/evse_types.cpp:132)not called0.0%
evse_security::conversions::encoding_format_to_string[abi:cxx11](evse_security::EncodingFormat) (lib/evse_security/evse_types.cpp:10)not called0.0%
evse_security::conversions::get_certificate_sign_request_result_to_string[abi:cxx11](evse_security::CertificateSignRequestResult) (lib/evse_security/crypto/interface/crypto_types.cpp:10)not called0.0%
evse_security::conversions::get_installed_certificates_status_to_string[abi:cxx11](evse_security::GetInstalledCertificatesStatus) (lib/evse_security/evse_types.cpp:145)not called0.0%
evse_security::conversions::get_key_pair_status_to_string[abi:cxx11](evse_security::GetCertificateInfoStatus) (lib/evse_security/evse_types.cpp:156)not called0.0%
evse_security::conversions::hash_algorithm_to_string[abi:cxx11](evse_security::HashAlgorithm) (lib/evse_security/evse_types.cpp:83)called 16 times25.0%
evse_security::conversions::install_certificate_result_to_string[abi:cxx11](evse_security::InstallCertificateResult) (lib/evse_security/evse_types.cpp:107)not called0.0%
evse_security::conversions::leaf_certificate_type_to_filename[abi:cxx11](evse_security::LeafCertificateType) (lib/evse_security/evse_types.cpp:51)called 10 times31.0%
evse_security::conversions::leaf_certificate_type_to_string[abi:cxx11](evse_security::LeafCertificateType) (lib/evse_security/evse_types.cpp:36)called 54 times31.0%
evse_security::conversions::string_to_hash_algorithm(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/evse_types.cpp:96)called 74 times33.0%
evse_security::export_key_internal(evse_security::KeyGenerationInfo const&, std::unique_ptr<evp_pkey_st, std::default_delete<evp_pkey_st> > const&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:73)called 26 times26.0%
evse_security::filesystem_utils::create_file_if_nonexistent(std::filesystem::__cxx11::path const&) (lib/evse_security/utils/evse_filesystem.cpp:51)called 28 times8.0%
evse_security::filesystem_utils::create_file_or_dir_if_nonexistent(std::filesystem::__cxx11::path const&) (lib/evse_security/utils/evse_filesystem.cpp:72)called 496 times18.0%
evse_security::filesystem_utils::delete_file(std::filesystem::__cxx11::path const&) (lib/evse_security/utils/evse_filesystem.cpp:20)called 112 times8.0%
evse_security::filesystem_utils::get_random_file_name(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/utils/evse_filesystem.cpp:149)called 26 times73.0%
evse_security::filesystem_utils::is_subdirectory(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&) (lib/evse_security/utils/evse_filesystem.cpp:15)not called0.0%
evse_security::filesystem_utils::process_file(std::filesystem::__cxx11::path const&, unsigned long, std::function<bool (unsigned char const*, unsigned long, bool)>&&) (lib/evse_security/utils/evse_filesystem.cpp:120)not called0.0%
evse_security::filesystem_utils::read_from_file(std::filesystem::__cxx11::path const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (lib/evse_security/utils/evse_filesystem.cpp:33)called 1022 times18.0%
evse_security::filesystem_utils::read_hash_from_file(std::filesystem::__cxx11::path const&, evse_security::CertificateHashData&) (lib/evse_security/utils/evse_filesystem.cpp:166)called 142 times28.0%
evse_security::filesystem_utils::write_hash_to_file(std::filesystem::__cxx11::path const&, evse_security::CertificateHashData const&) (lib/evse_security/utils/evse_filesystem.cpp:190)called 16 times35.0%
evse_security::filesystem_utils::write_to_file(std::filesystem::__cxx11::path const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::_Ios_Openmode) (lib/evse_security/utils/evse_filesystem.cpp:98)called 42 times12.0%
evse_security::get(evse_security::KeyHandle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:36)called 14 times71.0%
evse_security::get(evse_security::X509Handle*) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:28)called 22704 times71.0%
evse_security::get_ca_certificate_types(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> >) (lib/evse_security/evse_security.cpp:49)called 24 times79.0%
evse_security::get_certificate_path_of_key(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >) (lib/evse_security/evse_security.cpp:156)called 34 times32.0%
evse_security::get_certificate_path_of_key(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}::operator()(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) const (lib/evse_security/evse_security.cpp:174)called 26 times74.0%
evse_security::get_certificate_path_of_key(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}::operator()(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&) const (lib/evse_security/evse_security.cpp:198)called 8 times70.0%
evse_security::get_certificate_type(evse_security::CaCertificateType) (lib/evse_security/evse_security.cpp:68)called 50 times55.0%
evse_security::get_ocsp_request_data_internal(std::filesystem::__cxx11::path&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >&) (lib/evse_security/evse_security.cpp:764)called 4 times28.0%
evse_security::get_ocsp_request_data_internal(std::filesystem::__cxx11::path&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> >&)::{lambda(evse_security::OCSPRequestData const&)#1}::operator()(evse_security::OCSPRequestData const&) const (lib/evse_security/evse_security.cpp:791)called 16 times100.0%
evse_security::get_private_key_path_of_certificate(evse_security::X509Wrapper const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >) (lib/evse_security/evse_security.cpp:97)called 92 times40.0%
evse_security::handle_certificate(char const*, char const*) (3rd_party/cert_rehash/c_rehash.hpp:161)not called0.0%
evse_security::handle_symlink(char const*, char const*) (3rd_party/cert_rehash/c_rehash.hpp:122)not called0.0%
evse_security::hash_dir(char const*) (3rd_party/cert_rehash/c_rehash.hpp:210)not called0.0%
evse_security::is_custom_private_key_file(std::filesystem::__cxx11::path const&) (lib/evse_security/crypto/openssl/openssl_provider.cpp:34)not called0.0%
evse_security::is_custom_private_key_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (lib/evse_security/crypto/openssl/openssl_provider.cpp:29)called 202 times100.0%
evse_security::is_keyfile(std::filesystem::__cxx11::path const&) (lib/evse_security/evse_security.cpp:83)called 293 times100.0%
evse_security::s_generate_key(evse_security::KeyGenerationInfo const&, std::unique_ptr<evse_security::KeyHandle, std::default_delete<evse_security::KeyHandle> >&, std::unique_ptr<evp_pkey_ctx_st, std::default_delete<evp_pkey_ctx_st> >&) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:117)called 26 times45.0%
evse_security::str_cast_insensitive_cmp(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (include/evse_security/evse_types.hpp:102)called 246 times100.0%
evse_security::str_cast_insensitive_cmp(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char, char)#1}::operator()(char, char) const (include/evse_security/evse_types.hpp:107)called 3100 times100.0%
evse_security::to_certificate_error(int) (lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp:44)called 2 times58.0%
evse_security::to_install_certificate_result(evse_security::CertificateValidationResult) (lib/evse_security/evse_security.cpp:24)called 4 times12.0%
evse_security::x509_duplicate_unique(evse_security::X509Handle*) (lib/evse_security/crypto/interface/crypto_supplier.cpp:74)not called0.0%
std::default_delete<X509_req_st>::operator()(X509_req_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:33)called 14 times100.0%
std::default_delete<bio_st>::operator()(bio_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:61)called 1360 times100.0%
std::default_delete<evp_Encode_Ctx_st>::operator()(evp_Encode_Ctx_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:75)called 4 times100.0%
std::default_delete<evp_md_ctx_st>::operator()(evp_md_ctx_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:68)not called0.0%
std::default_delete<evp_pkey_ctx_st>::operator()(evp_pkey_ctx_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:54)called 26 times100.0%
std::default_delete<evp_pkey_st>::operator()(evp_pkey_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:47)called 228 times100.0%
std::default_delete<stack_st_X509>::operator()(stack_st_X509*) const (include/evse_security/crypto/openssl/openssl_types.hpp:40)not called0.0%
std::default_delete<x509_st>::operator()(x509_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:12)called 7132 times100.0%
std::default_delete<x509_store_ctx_st>::operator()(x509_store_ctx_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:26)called 3082 times100.0%
std::default_delete<x509_store_st>::operator()(x509_store_st*) const (include/evse_security/crypto/openssl/openssl_types.hpp:19)called 3082 times100.0%
void evse_security::X509CertificateBundle::for_each_chain<evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#3}>(evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#3}) (include/evse_security/certificate/x509_bundle.hpp:83)called 22 times100.0%
void evse_security::X509CertificateBundle::for_each_chain<evse_security::EvseSecurityTestsExpired_verify_expired_leaf_deletion_Test::TestBody()::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}>(evse_security::EvseSecurityTestsExpired_verify_expired_leaf_deletion_Test::TestBody()::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}) (include/evse_security/certificate/x509_bundle.hpp:83)called 2 times91.0%
void evse_security::X509CertificateBundle::for_each_chain<evse_security::get_certificate_path_of_key(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}>(evse_security::get_certificate_path_of_key(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}) (include/evse_security/certificate/x509_bundle.hpp:83)called 26 times91.0%
void evse_security::X509CertificateBundle::for_each_chain<evse_security::get_certificate_path_of_key(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}>(evse_security::get_certificate_path_of_key(std::filesystem::__cxx11::path const&, std::filesystem::__cxx11::path const&, std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}) (include/evse_security/certificate/x509_bundle.hpp:83)called 8 times91.0%
void evse_security::X509CertificateBundle::for_each_chain_ordered<evse_security::EvseSecurity::garbage_collect()::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}, evse_security::EvseSecurity::garbage_collect()::{lambda(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}>(evse_security::EvseSecurity::garbage_collect()::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}, evse_security::EvseSecurity::garbage_collect()::{lambda(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}) (include/evse_security/certificate/x509_bundle.hpp:91)called 2 times80.0%
void evse_security::X509CertificateBundle::for_each_chain_ordered<evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}, evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}>(evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::filesystem::__cxx11::path const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#1}, evse_security::EvseSecurity::get_full_leaf_certificate_info_internal(evse_security::LeafCertificateType, evse_security::EncodingFormat, bool, bool, bool)::{lambda(std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&, std::vector<evse_security::X509Wrapper, std::allocator<evse_security::X509Wrapper> > const&)#2}) (include/evse_security/certificate/x509_bundle.hpp:91)called 20 times83.0%
void evse_security::X509CertificateHierarchy::for_each<evse_security::X509CertificateHierarchy::collect_descendants(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&)#1}>(evse_security::X509CertificateHierarchy::collect_descendants(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&)#1}) (include/evse_security/certificate/x509_hierarchy.hpp:76)called 16 times80.0%
void evse_security::X509CertificateHierarchy::for_each<evse_security::X509CertificateHierarchy::contains_certificate_hash(evse_security::CertificateHashData const&)::{lambda(evse_security::X509Node const&)#1}>(evse_security::X509CertificateHierarchy::contains_certificate_hash(evse_security::CertificateHashData const&)::{lambda(evse_security::X509Node const&)#1}) (include/evse_security/certificate/x509_hierarchy.hpp:76)called 24 times84.0%
void evse_security::X509CertificateHierarchy::for_each<evse_security::X509CertificateHierarchy::find_certificate(evse_security::CertificateHashData const&, bool)::{lambda(evse_security::X509Node&)#1}>(evse_security::X509CertificateHierarchy::find_certificate(evse_security::CertificateHashData const&, bool)::{lambda(evse_security::X509Node&)#1}) (include/evse_security/certificate/x509_hierarchy.hpp:76)called 80 times86.0%
void evse_security::X509CertificateHierarchy::for_each<evse_security::X509CertificateHierarchy::find_certificates_multi(evse_security::CertificateHashData const&)::{lambda(evse_security::X509Node&)#1}>(evse_security::X509CertificateHierarchy::find_certificates_multi(evse_security::CertificateHashData const&)::{lambda(evse_security::X509Node&)#1}) (include/evse_security/certificate/x509_hierarchy.hpp:76)called 12 times80.0%
void evse_security::X509CertificateHierarchy::for_each<evse_security::X509CertificateHierarchy::get_certificate_hash(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&)#1}>(evse_security::X509CertificateHierarchy::get_certificate_hash(evse_security::X509Wrapper const&)::{lambda(evse_security::X509Node const&)#1}) (include/evse_security/certificate/x509_hierarchy.hpp:76)called 24 times78.0%
void evse_security::X509CertificateHierarchy::for_each<evse_security::X509CertificateHierarchy::insert(evse_security::X509Wrapper&&)::{lambda(evse_security::X509Node&)#1}>(evse_security::X509CertificateHierarchy::insert(evse_security::X509Wrapper&&)::{lambda(evse_security::X509Node&)#1}) (include/evse_security/certificate/x509_hierarchy.hpp:76)called 738 times86.0%
void evse_security::X509CertificateHierarchy::for_each<evse_security::X509CertificateHierarchy::prune()::{lambda(evse_security::X509Node&)#1}>(evse_security::X509CertificateHierarchy::prune()::{lambda(evse_security::X509Node&)#1}) (include/evse_security/certificate/x509_hierarchy.hpp:76)called 12 times86.0%
void evse_security::X509CertificateHierarchy::for_each_descendant<evse_security::EvseSecurity::get_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&)::{lambda(evse_security::X509Node const&, int)#1}>(evse_security::EvseSecurity::get_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&)::{lambda(evse_security::X509Node const&, int)#1}, evse_security::X509Node const&, int) (include/evse_security/certificate/x509_hierarchy.hpp:104)called 84 times100.0%
void evse_security::X509CertificateHierarchy::for_each_descendant<evse_security::EvseSecurity::get_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&)::{lambda(evse_security::X509Node const&, int)#2}>(evse_security::EvseSecurity::get_installed_certificates(std::vector<evse_security::CertificateType, std::allocator<evse_security::CertificateType> > const&)::{lambda(evse_security::X509Node const&, int)#2}, evse_security::X509Node const&, int) (include/evse_security/certificate/x509_hierarchy.hpp:104)called 28 times100.0%
+ +
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.html new file mode 100644 index 00000000..48726889 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.html @@ -0,0 +1,381 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
Date:2024-12-18 15:37:36
Coverage: + low: ≥ 0% + medium: ≥ 75.0% + high: ≥ 90.0% +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:1758288860.9%
Functions:19427670.3%
Branches:2195915824.0%
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileLinesFunctionsBranches
+ 3rd_party/cert_rehash/c_rehash.hpp + + 0.0 + 0.0%0 / 1730.0%0 / 60.0%0 / 482
+ include/evse_security/certificate/x509_bundle.hpp + + 100.0 + 100.0%26 / 26100.0%11 / 1171.1%27 / 38
+ include/evse_security/certificate/x509_hierarchy.hpp + + 96.8 + 96.8%30 / 31100.0%13 / 1380.0%32 / 40
+ include/evse_security/certificate/x509_wrapper.hpp + + 57.1 + 57.1%4 / 775.0%3 / 40.0%0 / 24
+ include/evse_security/crypto/interface/crypto_types.hpp + + 100.0 + 100.0%2 / 2100.0%1 / 1-%0 / 0
+ include/evse_security/crypto/openssl/openssl_provider.hpp + + 100.0 + 100.0%3 / 3100.0%1 / 1-%0 / 0
+ include/evse_security/crypto/openssl/openssl_types.hpp + + 84.2 + 84.2%32 / 3885.7%12 / 14-%0 / 0
+ include/evse_security/evse_types.hpp + + 89.5 + 89.5%17 / 19100.0%4 / 483.3%15 / 18
+ lib/evse_security/certificate/x509_bundle.cpp + + 65.4 + 65.4%140 / 21469.2%18 / 2636.0%128 / 356
+ lib/evse_security/certificate/x509_hierarchy.cpp + + 90.9 + 90.9%150 / 16590.9%20 / 2256.5%113 / 200
+ lib/evse_security/certificate/x509_wrapper.cpp + + 71.3 + 71.3%77 / 10888.5%23 / 2628.0%33 / 118
+ lib/evse_security/crypto/interface/crypto_supplier.cpp + + 0.0 + 0.0%0 / 580.0%0 / 250.0%0 / 640
+ lib/evse_security/crypto/interface/crypto_types.cpp + + 0.0 + 0.0%0 / 150.0%0 / 10.0%0 / 21
+ lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp + + 66.1 + 66.1%349 / 52876.5%26 / 3423.9%395 / 1655
+ lib/evse_security/crypto/openssl/openssl_provider.cpp + + 27.6 + 27.6%8 / 2936.4%4 / 110.0%0 / 8
+ lib/evse_security/evse_security.cpp + + 67.7 + 67.7%822 / 121580.4%45 / 5628.0%1356 / 4841
+ lib/evse_security/evse_types.cpp + + 22.3 + 22.3%29 / 13045.5%5 / 1111.8%19 / 161
+ lib/evse_security/utils/evse_filesystem.cpp + + 54.3 + 54.3%69 / 12780.0%8 / 1013.8%77 / 556
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_crypto_supplier.cpp.37614a6ef01f5066779263697ffa21dd.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_crypto_supplier.cpp.37614a6ef01f5066779263697ffa21dd.html new file mode 100644 index 00000000..af1dc829 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_crypto_supplier.cpp.37614a6ef01f5066779263697ffa21dd.html @@ -0,0 +1,8246 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/crypto/openssl/openssl_crypto_supplier.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:34952866.1%
Functions:263476.5%
Branches:395165523.9%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #include <evse_security/crypto/openssl/openssl_crypto_supplier.hpp>
4 +
5 + #include <everest/logging.hpp>
6 +
7 + #include <algorithm>
8 + #include <chrono>
9 + #include <cstring>
10 + #include <numeric>
11 + #include <string>
12 + #include <vector>
13 +
14 + #include <openssl/bio.h>
15 + #include <openssl/err.h>
16 + #include <openssl/evp.h>
17 + #include <openssl/opensslv.h>
18 + #include <openssl/pem.h>
19 + #include <openssl/sha.h>
20 + #include <openssl/x509v3.h>
21 +
22 + #include <evse_security/crypto/openssl/openssl_provider.hpp>
23 + #include <evse_security/crypto/openssl/openssl_types.hpp>
24 + #include <evse_security/utils/evse_filesystem.hpp>
25 +
26 + namespace evse_security {
27 +
28 + 22704static X509* get(X509Handle* handle) {
29 +
+ 2/4 +
+
✓ Branch 0 taken 22704 times.
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 22704 times.
+
✗ Branch 3 not taken.
+
+
+
22704 if (X509HandleOpenSSL* ssl_handle = dynamic_cast<X509HandleOpenSSL*>(handle)) {
30 + 22704 return ssl_handle->get();
31 + }
32 +
33 + return nullptr;
34 + }
35 +
36 + 14static EVP_PKEY* get(KeyHandle* handle) {
37 +
+ 2/4 +
+
✓ Branch 0 taken 14 times.
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
+
+
14 if (KeyHandleOpenSSL* ssl_handle = dynamic_cast<KeyHandleOpenSSL*>(handle)) {
38 + 14 return ssl_handle->get();
39 + }
40 +
41 + return nullptr;
42 + }
43 +
44 + 2static CertificateValidationResult to_certificate_error(const int ec) {
45 +
+ 1/6 +
+
✗ Branch 0 not taken.
+
✗ Branch 1 not taken.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 2 times.
+
+
+
2 switch (ec) {
46 + case X509_V_ERR_CERT_HAS_EXPIRED:
47 + return CertificateValidationResult::Expired;
48 + case X509_V_ERR_CERT_SIGNATURE_FAILURE:
49 + return CertificateValidationResult::InvalidSignature;
50 + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
51 + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
52 + return CertificateValidationResult::IssuerNotFound;
53 + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
54 + return CertificateValidationResult::InvalidLeafSignature;
55 + case X509_V_ERR_CERT_CHAIN_TOO_LONG:
56 + case X509_V_ERR_CERT_UNTRUSTED:
57 + return CertificateValidationResult::InvalidChain;
58 + 2 default:
59 +
+ 13/24 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 2 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 2 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 2 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 2 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 2 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 2 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 2 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 2 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 2 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 2 times.
+
✓ Branch 36 taken 2 times.
+
+
+
4 EVLOG_warning << X509_verify_cert_error_string(ec);
60 + 2 return CertificateValidationResult::Unknown;
61 + }
62 + }
63 +
64 + const char* OpenSSLSupplier::get_supplier_name() {
65 + return OPENSSL_VERSION_TEXT;
66 + }
67 +
68 + bool OpenSSLSupplier::supports_tpm_key_creation() {
69 + OpenSSLProvider provider;
70 + return provider.supports_provider_tpm();
71 + }
72 +
73 + 26static bool export_key_internal(const KeyGenerationInfo& key_info, const EVP_PKEY_ptr& evp_key) {
74 + // write private key to file
75 +
+ 2/2 +
+
✓ Branch 1 taken 18 times.
+
✓ Branch 2 taken 8 times.
+
+
+
26 if (key_info.private_key_file.has_value()) {
76 +
+ 2/4 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 18 times.
+
✗ Branch 6 not taken.
+
+
+
18 BIO_ptr key_bio(BIO_new_file(key_info.private_key_file.value().c_str(), "w"));
77 +
78 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 18 times.
+
+
+
18 if (!key_bio) {
79 + EVLOG_error << "Failed to create private key file!";
80 + return false;
81 + }
82 +
83 + int success;
84 +
+ 2/2 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 14 times.
+
+
+
18 if (key_info.private_key_pass.has_value()) {
85 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 6 taken 4 times.
+
✗ Branch 7 not taken.
+
+
+
4 success = PEM_write_bio_PrivateKey(key_bio.get(), evp_key.get(), EVP_aes_128_cbc(), NULL, 0, NULL,
86 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 (void*)key_info.private_key_pass.value().c_str());
87 + } else {
88 +
+ 1/2 +
+
✓ Branch 3 taken 14 times.
+
✗ Branch 4 not taken.
+
+
+
14 success = PEM_write_bio_PrivateKey(key_bio.get(), evp_key.get(), NULL, NULL, 0, NULL, NULL);
89 + }
90 +
91 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 18 times.
+
+
+
18 if (false == success) {
92 + EVLOG_error << "Failed to write private key!";
93 + return false;
94 + }
95 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 }
96 +
97 +
+ 2/2 +
+
✓ Branch 1 taken 6 times.
+
✓ Branch 2 taken 20 times.
+
+
+
26 if (key_info.public_key_file.has_value()) {
98 +
+ 2/4 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 6 times.
+
✗ Branch 6 not taken.
+
+
+
6 BIO_ptr key_bio(BIO_new_file(key_info.public_key_file.value().c_str(), "w"));
99 +
100 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 6 times.
+
+
+
6 if (!key_bio) {
101 + EVLOG_error << "Failed to create private key file!";
102 + return false;
103 + }
104 +
105 +
+ 2/4 +
+
✓ Branch 3 taken 6 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 6 times.
+
+
+
6 if (false == PEM_write_bio_PUBKEY(key_bio.get(), evp_key.get())) {
106 + EVLOG_error << "Failed to write pubkey!";
107 + return false;
108 + }
109 +
+ 1/2 +
+
✓ Branch 1 taken 6 times.
+
✗ Branch 2 not taken.
+
+
+
6 }
110 +
111 + 26 return true;
112 + }
113 +
114 + constexpr const char* kt_rsa = "RSA";
115 + constexpr const char* kt_ec = "EC";
116 +
117 + 26static bool s_generate_key(const KeyGenerationInfo& key_info, KeyHandle_ptr& out_key, EVP_PKEY_CTX_ptr& ctx) {
118 + 26 unsigned int bits = 0;
119 + 26 char group_256[] = "P-256";
120 + 26 char group_384[] = "P-384";
121 + 26 char* group = nullptr;
122 + 26 std::size_t group_sz = 0;
123 + 26 int nid = NID_undef;
124 +
125 + 26 bool bResult = true;
126 + 26 bool bEC = true;
127 +
128 + // note when using tpm2 some key_types may not be supported.
129 +
130 +
+ 9/16 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 26 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 26 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 26 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 26 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 26 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 26 times.
+
✓ Branch 24 taken 26 times.
+
+
+
52 EVLOG_info << "Key parameters";
131 +
+ 4/5 +
+
✓ Branch 0 taken 4 times.
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 16 times.
+
✓ Branch 4 taken 2 times.
+
+
+
26 switch (key_info.key_type) {
132 + 4 case CryptoKeyType::RSA_TPM20:
133 + 4 bits = 2048;
134 + 4 bEC = false;
135 + 4 break;
136 + 4 case CryptoKeyType::RSA_3072:
137 + 4 bits = 3072;
138 + 4 bEC = false;
139 + 4 break;
140 + case CryptoKeyType::RSA_7680:
141 + bits = 7680;
142 + bEC = false;
143 + break;
144 + 16 case CryptoKeyType::EC_prime256v1:
145 + 16 group = group_256;
146 + 16 group_sz = sizeof(group_256);
147 + 16 nid = NID_X9_62_prime256v1;
148 + 16 break;
149 + 2 case CryptoKeyType::EC_secp384r1:
150 + default:
151 + 2 group = group_384;
152 + 2 group_sz = sizeof(group_384);
153 + 2 nid = NID_secp384r1;
154 + 2 break;
155 + }
156 +
157 + OSSL_PARAM params[2];
158 + 26 std::memset(&params[0], 0, sizeof(params));
159 +
160 +
+ 2/2 +
+
✓ Branch 0 taken 18 times.
+
✓ Branch 1 taken 8 times.
+
+
+
26 if (bEC) {
161 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 params[0] = OSSL_PARAM_construct_utf8_string("group", group, group_sz);
162 +
+ 9/16 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 18 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 18 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 18 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 18 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 18 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 18 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 18 times.
+
✓ Branch 24 taken 18 times.
+
+
+
36 EVLOG_info << "Key parameters: EC";
163 +
+ 1/2 +
+
✓ Branch 1 taken 18 times.
+
✗ Branch 2 not taken.
+
+
+
18 ctx = EVP_PKEY_CTX_ptr(EVP_PKEY_CTX_new_from_name(nullptr, kt_ec, nullptr));
164 + } else {
165 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 params[0] = OSSL_PARAM_construct_uint("bits", &bits);
166 +
+ 9/16 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 8 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 8 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 8 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 8 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 8 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 8 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 8 times.
+
✓ Branch 24 taken 8 times.
+
+
+
16 EVLOG_info << "Key parameters: RSA";
167 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 ctx = EVP_PKEY_CTX_ptr(EVP_PKEY_CTX_new_from_name(nullptr, kt_rsa, nullptr));
168 + }
169 +
170 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 params[1] = OSSL_PARAM_construct_end();
171 +
172 +
+ 1/2 +
+
✓ Branch 0 taken 26 times.
+
✗ Branch 1 not taken.
+
+
+
26 if (bResult) {
173 +
+ 9/16 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 26 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 26 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 26 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 26 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 26 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 26 times.
+
✓ Branch 24 taken 26 times.
+
+
+
52 EVLOG_info << "Key parameters done";
174 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 26 times.
+
+
+
26 if (nullptr == ctx.get()) {
175 + EVLOG_error << "create key context failed!";
176 + ERR_print_errors_fp(stderr);
177 + bResult = false;
178 + }
179 + }
180 +
181 +
+ 1/2 +
+
✓ Branch 0 taken 26 times.
+
✗ Branch 1 not taken.
+
+
+
26 if (bResult) {
182 +
+ 9/16 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 26 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 26 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 26 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 26 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 26 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 26 times.
+
✓ Branch 24 taken 26 times.
+
+
+
52 EVLOG_info << "Keygen init";
183 +
+ 5/10 +
+
✓ Branch 2 taken 26 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 26 times.
+
✗ Branch 9 not taken.
+
✗ Branch 10 not taken.
+
✓ Branch 11 taken 26 times.
+
✗ Branch 12 not taken.
+
✓ Branch 13 taken 26 times.
+
+
+
26 if (EVP_PKEY_keygen_init(ctx.get()) <= 0 || EVP_PKEY_CTX_set_params(ctx.get(), params) <= 0) {
184 + EVLOG_error << "Keygen init failed";
185 + ERR_print_errors_fp(stderr);
186 + bResult = false;
187 + }
188 + }
189 +
190 + 26 EVP_PKEY* pkey = nullptr;
191 +
192 +
+ 1/2 +
+
✓ Branch 0 taken 26 times.
+
✗ Branch 1 not taken.
+
+
+
26 if (bResult) {
193 +
+ 9/16 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 26 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 26 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 26 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 26 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 26 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 26 times.
+
✓ Branch 24 taken 26 times.
+
+
+
52 EVLOG_info << "Key generate";
194 +
+ 2/4 +
+
✓ Branch 2 taken 26 times.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 26 times.
+
+
+
26 if (EVP_PKEY_generate(ctx.get(), &pkey) <= 0) {
195 + EVLOG_error << "Failed to generate tpm2 key!";
196 + ERR_print_errors_fp(stderr);
197 + bResult = false;
198 + }
199 + }
200 +
201 + 26 auto evp_key = EVP_PKEY_ptr(pkey);
202 +
203 +
+ 1/2 +
+
✓ Branch 0 taken 26 times.
+
✗ Branch 1 not taken.
+
+
+
26 if (bResult) {
204 +
+ 9/16 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 26 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 26 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 26 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 26 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 26 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 26 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 26 times.
+
✓ Branch 24 taken 26 times.
+
+
+
52 EVLOG_info << "Key export";
205 + // Export keys too
206 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 bResult = export_key_internal(key_info, evp_key);
207 + 26 EVP_PKEY* raw_key_handle = evp_key.release();
208 +
+ 1/2 +
+
✓ Branch 1 taken 26 times.
+
✗ Branch 2 not taken.
+
+
+
26 out_key = std::make_unique<KeyHandleOpenSSL>(raw_key_handle);
209 + }
210 +
211 + 26 return bResult;
212 + 26}
213 +
214 + 12bool OpenSSLSupplier::generate_key(const KeyGenerationInfo& key_info, KeyHandle_ptr& out_key) {
215 + 12 KeyHandle_ptr gen_key;
216 + 12 EVP_PKEY_CTX_ptr ctx;
217 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 OpenSSLProvider provider;
218 + 12 bool bResult = true;
219 +
220 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 12 times.
+
+
+
12 if (key_info.generate_on_custom) {
221 + provider.set_global_mode(OpenSSLProvider::mode_t::custom_provider);
222 +
223 + } else {
224 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 provider.set_global_mode(OpenSSLProvider::mode_t::default_provider);
225 + }
226 +
227 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 bResult = s_generate_key(key_info, gen_key, ctx);
228 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 12 times.
+
+
+
12 if (!bResult) {
229 + EVLOG_error << "Failed to generate csr pub/priv key!";
230 + }
231 + 12 return bResult;
232 + 12}
233 +
234 + 950std::vector<X509Handle_ptr> OpenSSLSupplier::load_certificates(const std::string& data, const EncodingFormat encoding) {
235 + 950 std::vector<X509Handle_ptr> certificates;
236 +
237 +
+ 1/2 +
+
✓ Branch 3 taken 950 times.
+
✗ Branch 4 not taken.
+
+
+
950 BIO_ptr bio(BIO_new_mem_buf(data.data(), static_cast<int>(data.size())));
238 +
239 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 950 times.
+
+
+
950 if (!bio) {
240 + throw CertificateLoadException("Failed to create BIO from data");
241 + }
242 +
243 +
+ 1/2 +
+
✓ Branch 0 taken 950 times.
+
✗ Branch 1 not taken.
+
+
+
950 if (encoding == EncodingFormat::PEM) {
244 +
+ 1/2 +
+
✓ Branch 2 taken 950 times.
+
✗ Branch 3 not taken.
+
+
+
950 STACK_OF(X509_INFO)* allcerts = PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr);
245 +
246 +
+ 2/2 +
+
✓ Branch 0 taken 948 times.
+
✓ Branch 1 taken 2 times.
+
+
+
950 if (allcerts) {
247 +
+ 3/4 +
+
✓ Branch 2 taken 2592 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 1644 times.
+
✓ Branch 5 taken 948 times.
+
+
+
2592 for (int i = 0; i < sk_X509_INFO_num(allcerts); i++) {
248 +
+ 1/2 +
+
✓ Branch 2 taken 1644 times.
+
✗ Branch 3 not taken.
+
+
+
1644 X509_INFO* xi = sk_X509_INFO_value(allcerts, i);
249 +
250 +
+ 2/4 +
+
✓ Branch 0 taken 1644 times.
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 1644 times.
+
✗ Branch 3 not taken.
+
+
+
1644 if (xi && xi->x509) {
251 + // Transfer ownership, safely, push_back since emplace_back can cause a memory leak
252 +
+ 2/4 +
+
✓ Branch 1 taken 1644 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 1644 times.
+
✗ Branch 6 not taken.
+
+
+
1644 certificates.push_back(std::make_unique<X509HandleOpenSSL>(xi->x509));
253 + 1644 xi->x509 = nullptr;
254 + }
255 + }
256 +
257 +
+ 1/2 +
+
✓ Branch 3 taken 948 times.
+
✗ Branch 4 not taken.
+
+
+
948 sk_X509_INFO_pop_free(allcerts, X509_INFO_free);
258 + } else {
259 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 throw CertificateLoadException("Certificate (PEM) parsing error");
260 + }
261 + } else if (encoding == EncodingFormat::DER) {
262 + X509* x509 = d2i_X509_bio(bio.get(), nullptr);
263 +
264 + if (x509) {
265 + certificates.push_back(std::make_unique<X509HandleOpenSSL>(x509));
266 + } else {
267 + throw CertificateLoadException("Certificate (DER) parsing error");
268 + }
269 + } else {
270 + throw CertificateLoadException("Unsupported encoding format");
271 + }
272 +
273 + 1896 return certificates;
274 + 952}
275 +
276 + 170std::string OpenSSLSupplier::x509_to_string(X509Handle* handle) {
277 +
+ 1/2 +
+
✓ Branch 1 taken 170 times.
+
✗ Branch 2 not taken.
+
+
+
170 if (X509* x509 = get(handle)) {
278 +
+ 2/4 +
+
✓ Branch 1 taken 170 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 170 times.
+
✗ Branch 5 not taken.
+
+
+
170 BIO_ptr bio_write(BIO_new(BIO_s_mem()));
279 +
280 +
+ 1/2 +
+
✓ Branch 2 taken 170 times.
+
✗ Branch 3 not taken.
+
+
+
170 int rc = PEM_write_bio_X509(bio_write.get(), x509);
281 +
282 +
+ 1/2 +
+
✓ Branch 0 taken 170 times.
+
✗ Branch 1 not taken.
+
+
+
170 if (rc == 1) {
283 + 170 BUF_MEM* mem = NULL;
284 +
+ 1/2 +
+
✓ Branch 2 taken 170 times.
+
✗ Branch 3 not taken.
+
+
+
170 BIO_get_mem_ptr(bio_write.get(), &mem);
285 +
286 +
+ 1/2 +
+
✓ Branch 2 taken 170 times.
+
✗ Branch 3 not taken.
+
+
+
170 return std::string(mem->data, mem->length);
287 + }
288 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 170 times.
+
+
+
170 }
289 +
290 + return {};
291 + }
292 +
293 + 324std::string OpenSSLSupplier::x509_get_common_name(X509Handle* handle) {
294 + 324 X509* x509 = get(handle);
295 +
296 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 324 times.
+
+
+
324 if (x509 == nullptr)
297 + return {};
298 +
299 +
+ 1/2 +
+
✓ Branch 1 taken 324 times.
+
✗ Branch 2 not taken.
+
+
+
324 X509_NAME* subject = X509_get_subject_name(x509);
300 +
+ 1/2 +
+
✓ Branch 1 taken 324 times.
+
✗ Branch 2 not taken.
+
+
+
324 int nid = OBJ_txt2nid("CN");
301 +
+ 1/2 +
+
✓ Branch 1 taken 324 times.
+
✗ Branch 2 not taken.
+
+
+
324 int index = X509_NAME_get_index_by_NID(subject, nid, -1);
302 +
303 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 324 times.
+
+
+
324 if (index == -1) {
304 + return {};
305 + }
306 +
307 +
+ 1/2 +
+
✓ Branch 1 taken 324 times.
+
✗ Branch 2 not taken.
+
+
+
324 X509_NAME_ENTRY* entry = X509_NAME_get_entry(subject, index);
308 +
+ 1/2 +
+
✓ Branch 1 taken 324 times.
+
✗ Branch 2 not taken.
+
+
+
324 ASN1_STRING* ca_asn1 = X509_NAME_ENTRY_get_data(entry);
309 +
310 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 324 times.
+
+
+
324 if (ca_asn1 == nullptr) {
311 + return {};
312 + }
313 +
314 +
+ 1/2 +
+
✓ Branch 1 taken 324 times.
+
✗ Branch 2 not taken.
+
+
+
324 const unsigned char* cn_str = ASN1_STRING_get0_data(ca_asn1);
315 +
+ 2/4 +
+
✓ Branch 2 taken 324 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 324 times.
+
✗ Branch 6 not taken.
+
+
+
324 std::string common_name(reinterpret_cast<const char*>(cn_str), ASN1_STRING_length(ca_asn1));
316 + 324 return common_name;
317 + 324}
318 +
319 + 1106std::string OpenSSLSupplier::x509_get_issuer_name_hash(X509Handle* handle) {
320 + 1106 X509* x509 = get(handle);
321 +
322 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 1106 times.
+
+
+
1106 if (x509 == nullptr)
323 + return {};
324 +
325 + unsigned char md[SHA256_DIGEST_LENGTH];
326 +
+ 1/2 +
+
✓ Branch 1 taken 1106 times.
+
✗ Branch 2 not taken.
+
+
+
1106 X509_NAME* name = X509_get_issuer_name(x509);
327 +
+ 2/4 +
+
✓ Branch 1 taken 1106 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 1106 times.
+
✗ Branch 5 not taken.
+
+
+
1106 X509_NAME_digest(name, EVP_sha256(), md, NULL);
328 +
329 +
+ 1/2 +
+
✓ Branch 1 taken 1106 times.
+
✗ Branch 2 not taken.
+
+
+
1106 std::stringstream ss;
330 +
+ 2/2 +
+
✓ Branch 0 taken 35392 times.
+
✓ Branch 1 taken 1106 times.
+
+
+
36498 for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
331 +
+ 3/6 +
+
✓ Branch 4 taken 35392 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 35392 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 35392 times.
+
✗ Branch 11 not taken.
+
+
+
35392 ss << std::setw(2) << std::setfill('0') << std::hex << (int)md[i];
332 + }
333 +
+ 1/2 +
+
✓ Branch 1 taken 1106 times.
+
✗ Branch 2 not taken.
+
+
+
1106 return ss.str();
334 + 1106}
335 +
336 + 1104std::string OpenSSLSupplier::x509_get_serial_number(X509Handle* handle) {
337 + 1104 X509* x509 = get(handle);
338 +
339 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 1104 times.
+
+
+
1104 if (x509 == nullptr)
340 + return {};
341 +
342 +
+ 1/2 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
+
+
1104 ASN1_INTEGER* serial_asn1 = X509_get_serialNumber(x509);
343 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 1104 times.
+
+
+
1104 if (serial_asn1 == nullptr) {
344 + ERR_print_errors_fp(stderr);
345 + return {};
346 + }
347 +
348 +
+ 1/2 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
+
+
1104 BIGNUM* bn_serial = ASN1_INTEGER_to_BN(serial_asn1, NULL);
349 +
350 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 1104 times.
+
+
+
1104 if (bn_serial == nullptr) {
351 + ERR_print_errors_fp(stderr);
352 + return {};
353 + }
354 +
355 +
+ 1/2 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
+
+
1104 char* hex_serial = BN_bn2hex(bn_serial);
356 +
357 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 1104 times.
+
+
+
1104 if (hex_serial == nullptr) {
358 + ERR_print_errors_fp(stderr);
359 + return {};
360 + }
361 +
362 +
+ 1/2 +
+
✓ Branch 2 taken 1104 times.
+
✗ Branch 3 not taken.
+
+
+
1104 std::string serial(hex_serial);
363 +
+ 2/2 +
+
✓ Branch 4 taken 4416 times.
+
✓ Branch 5 taken 1104 times.
+
+
+
5520 for (char& i : serial) {
364 + 4416 i = std::tolower(i);
365 + }
366 +
367 +
+ 1/2 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
+
+
1104 BN_free(bn_serial);
368 +
+ 1/2 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
+
+
1104 OPENSSL_free(hex_serial);
369 +
370 +
+ 1/2 +
+
✓ Branch 4 taken 1104 times.
+
✗ Branch 5 not taken.
+
+
+
1104 serial.erase(0, std::min(serial.find_first_not_of('0'), serial.size() - 1));
371 + 1104 return serial;
372 + 1104}
373 +
374 + 1104std::string OpenSSLSupplier::x509_get_key_hash(X509Handle* handle) {
375 + 1104 X509* x509 = get(handle);
376 +
377 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 1104 times.
+
+
+
1104 if (x509 == nullptr)
378 + return {};
379 +
380 + unsigned char tmphash[SHA256_DIGEST_LENGTH];
381 +
+ 2/4 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 1104 times.
+
✗ Branch 5 not taken.
+
+
+
1104 X509_pubkey_digest(x509, EVP_sha256(), tmphash, NULL);
382 +
+ 1/2 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
+
+
1104 std::stringstream ss;
383 +
+ 2/2 +
+
✓ Branch 0 taken 35328 times.
+
✓ Branch 1 taken 1104 times.
+
+
+
36432 for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
384 +
+ 3/6 +
+
✓ Branch 4 taken 35328 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 35328 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 35328 times.
+
✗ Branch 11 not taken.
+
+
+
35328 ss << std::setw(2) << std::setfill('0') << std::hex << (int)tmphash[i];
385 + }
386 +
387 +
+ 1/2 +
+
✓ Branch 1 taken 1104 times.
+
✗ Branch 2 not taken.
+
+
+
1104 return ss.str();
388 + 1104}
389 +
390 + 20std::string OpenSSLSupplier::x509_get_responder_url(X509Handle* handle) {
391 + 20 X509* x509 = get(handle);
392 +
393 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 20 times.
+
+
+
20 if (x509 == nullptr)
394 + return {};
395 +
396 +
+ 1/2 +
+
✓ Branch 1 taken 20 times.
+
✗ Branch 2 not taken.
+
+
+
20 const auto ocsp = X509_get1_ocsp(x509);
397 + 20 std::string responder_url;
398 +
+ 3/4 +
+
✓ Branch 2 taken 36 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 16 times.
+
✓ Branch 5 taken 20 times.
+
+
+
36 for (int i = 0; i < sk_OPENSSL_STRING_num(ocsp); i++) {
399 +
+ 2/4 +
+
✓ Branch 2 taken 16 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 16 times.
+
✗ Branch 6 not taken.
+
+
+
16 responder_url.append(sk_OPENSSL_STRING_value(ocsp, i));
400 + }
401 +
402 +
+ 2/2 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 16 times.
+
+
+
20 if (responder_url.empty()) {
403 +
+ 12/22 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 4 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 4 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 4 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 4 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 4 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 4 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 4 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 4 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 4 times.
+
✓ Branch 33 taken 4 times.
+
+
+
8 EVLOG_warning << "Could not retrieve OCSP Responder URL from certificate";
404 + }
405 +
406 + 20 return responder_url;
407 +
+ 0/2 +
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
+
+
20}
408 +
409 + 1632bool OpenSSLSupplier::x509_get_validity(X509Handle* handle, std::int64_t& out_valid_in, std::int64_t& out_valid_to) {
410 + 1632 X509* x509 = get(handle);
411 +
412 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 1632 times.
+
+
+
1632 if (x509 == nullptr) {
413 + return false;
414 + }
415 +
416 + // For valid_in and valid_to
417 +
+ 1/2 +
+
✓ Branch 1 taken 1632 times.
+
✗ Branch 2 not taken.
+
+
+
1632 ASN1_TIME* notBefore = X509_get_notBefore(x509);
418 +
+ 1/2 +
+
✓ Branch 1 taken 1632 times.
+
✗ Branch 2 not taken.
+
+
+
1632 ASN1_TIME* notAfter = X509_get_notAfter(x509);
419 +
420 + int day, sec;
421 +
+ 1/2 +
+
✓ Branch 1 taken 1632 times.
+
✗ Branch 2 not taken.
+
+
+
1632 ASN1_TIME_diff(&day, &sec, nullptr, notBefore);
422 + 1632 out_valid_in =
423 +
+ 1/2 +
+
✓ Branch 2 taken 1632 times.
+
✗ Branch 3 not taken.
+
+
+
1632 std::chrono::duration_cast<std::chrono::seconds>(days_to_seconds(day)).count() + sec; // Convert days to seconds
424 +
+ 1/2 +
+
✓ Branch 1 taken 1632 times.
+
✗ Branch 2 not taken.
+
+
+
1632 ASN1_TIME_diff(&day, &sec, nullptr, notAfter);
425 + 1632 out_valid_to =
426 +
+ 1/2 +
+
✓ Branch 2 taken 1632 times.
+
✗ Branch 3 not taken.
+
+
+
1632 std::chrono::duration_cast<std::chrono::seconds>(days_to_seconds(day)).count() + sec; // Convert days to seconds
427 +
428 + 1632 return true;
429 + }
430 +
431 + 3066bool OpenSSLSupplier::x509_is_child(X509Handle* child, X509Handle* parent) {
432 + // A certif can't be it's own parent, use is_selfsigned if that is intended
433 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 3066 times.
+
+
+
3066 if (child == parent)
434 + return false;
435 +
436 + 3066 X509* x509_parent = get(parent);
437 + 3066 X509* x509_child = get(child);
438 +
439 +
+ 2/4 +
+
✓ Branch 0 taken 3066 times.
+
✗ Branch 1 not taken.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 3066 times.
+
+
+
3066 if (x509_parent == nullptr || x509_child == nullptr)
440 + return false;
441 +
442 +
+ 1/2 +
+
✓ Branch 1 taken 3066 times.
+
✗ Branch 2 not taken.
+
+
+
3066 X509_STORE_ptr store(X509_STORE_new());
443 +
+ 1/2 +
+
✓ Branch 2 taken 3066 times.
+
✗ Branch 3 not taken.
+
+
+
3066 X509_STORE_add_cert(store.get(), x509_parent);
444 +
445 +
+ 1/2 +
+
✓ Branch 1 taken 3066 times.
+
✗ Branch 2 not taken.
+
+
+
3066 X509_STORE_CTX_ptr ctx(X509_STORE_CTX_new());
446 +
+ 1/2 +
+
✓ Branch 3 taken 3066 times.
+
✗ Branch 4 not taken.
+
+
+
3066 X509_STORE_CTX_init(ctx.get(), store.get(), x509_child, NULL);
447 +
448 + // If the parent is not a self-signed certificate, assume we have a partial chain
449 +
+ 3/4 +
+
✓ Branch 1 taken 3066 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 2088 times.
+
✓ Branch 4 taken 978 times.
+
+
+
3066 if (x509_is_selfsigned(parent) == false) {
450 + // TODO(ioan): see if this strict flag is required, caused many problems
451 + // X509_STORE_CTX_set_flags(ctx.get(), X509_V_FLAG_X509_STRICT);
452 +
453 +
+ 1/2 +
+
✓ Branch 2 taken 2088 times.
+
✗ Branch 3 not taken.
+
+
+
2088 X509_STORE_CTX_set_flags(ctx.get(), X509_V_FLAG_PARTIAL_CHAIN);
454 + }
455 +
456 +
+ 3/4 +
+
✓ Branch 2 taken 3066 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 1630 times.
+
✓ Branch 5 taken 1436 times.
+
+
+
3066 if (X509_verify_cert(ctx.get()) != 1) {
457 +
+ 1/2 +
+
✓ Branch 2 taken 1630 times.
+
✗ Branch 3 not taken.
+
+
+
1630 int ec = X509_STORE_CTX_get_error(ctx.get());
458 +
+ 1/2 +
+
✓ Branch 1 taken 1630 times.
+
✗ Branch 2 not taken.
+
+
+
1630 const char* error = X509_verify_cert_error_string(ec);
459 +
460 +
+ 14/26 +
+
✓ Branch 1 taken 1630 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 1630 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 1630 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 1630 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 1630 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 1630 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 1630 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 1630 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 1630 times.
+
✗ Branch 27 not taken.
+
✓ Branch 28 taken 1630 times.
+
✗ Branch 29 not taken.
+
✓ Branch 31 taken 1630 times.
+
✗ Branch 32 not taken.
+
✓ Branch 34 taken 1630 times.
+
✗ Branch 35 not taken.
+
✓ Branch 37 taken 1630 times.
+
✓ Branch 38 taken 1630 times.
+
+
+
3260 EVLOG_debug << "Certificate issued by error: " << ((error != nullptr) ? error : "UNKNOWN");
461 + 1630 return false;
462 + }
463 +
464 + 1436 return true;
465 + 3066}
466 +
467 + 4592bool OpenSSLSupplier::x509_is_selfsigned(X509Handle* handle) {
468 + 4592 X509* x509 = get(handle);
469 +
470 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 4592 times.
+
+
+
4592 if (x509 == nullptr)
471 + return false;
472 +
473 + 4592 return (X509_self_signed(x509, 0) == 1);
474 + }
475 +
476 + 392bool OpenSSLSupplier::x509_is_equal(X509Handle* a, X509Handle* b) {
477 + 392 return (X509_cmp(get(a), get(b)) == 0);
478 + }
479 +
480 + 5488X509Handle_ptr OpenSSLSupplier::x509_duplicate_unique(X509Handle* handle) {
481 +
+ 2/4 +
+
✓ Branch 2 taken 5488 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 5488 times.
+
✗ Branch 6 not taken.
+
+
+
5488 return std::make_unique<X509HandleOpenSSL>(X509_dup(get(handle)));
482 + }
483 +
484 + 16CertificateValidationResult OpenSSLSupplier::x509_verify_certificate_chain(
485 + X509Handle* target, const std::vector<X509Handle*>& parents, const std::vector<X509Handle*>& untrusted_subcas,
486 + bool allow_future_certificates, const std::optional<fs::path> dir_path, const std::optional<fs::path> file_path) {
487 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 OpenSSLProvider provider;
488 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 provider.set_global_mode(OpenSSLProvider::mode_t::default_provider);
489 +
490 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 X509_STORE_ptr store_ptr(X509_STORE_new());
491 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 X509_STORE_CTX_ptr store_ctx_ptr(X509_STORE_CTX_new());
492 +
493 +
+ 2/2 +
+
✓ Branch 1 taken 14 times.
+
✓ Branch 2 taken 16 times.
+
+
+
30 for (size_t i = 0; i < parents.size(); i++) {
494 +
+ 1/2 +
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
+
+
14 X509_STORE_add_cert(store_ptr.get(), get(parents[i]));
495 + }
496 +
497 +
+ 5/6 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✓ Branch 5 taken 2 times.
+
✓ Branch 6 taken 14 times.
+
✓ Branch 7 taken 2 times.
+
+
+
16 if (dir_path.has_value() || file_path.has_value()) {
498 +
+ 1/4 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 14 times.
+
✗ Branch 4 not taken.
+
✗ Branch 5 not taken.
+
+
+
14 const char* c_dir_path = dir_path.has_value() ? dir_path.value().c_str() : nullptr;
499 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
+
+
14 const char* c_file_path = file_path.has_value() ? file_path.value().c_str() : nullptr;
500 +
501 +
+ 2/4 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 14 times.
+
+
+
14 if (1 != X509_STORE_load_locations(store_ptr.get(), c_file_path, c_dir_path)) {
502 + EVLOG_warning << "X509 could not load store locations!";
503 + return CertificateValidationResult::Unknown;
504 + }
505 +
506 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 14 times.
+
+
+
14 if (dir_path.has_value()) {
507 + if (X509_STORE_add_lookup(store_ptr.get(), X509_LOOKUP_file()) == nullptr) {
508 + EVLOG_warning << "X509 could not add store lookup!";
509 + return CertificateValidationResult::Unknown;
510 + }
511 + }
512 + }
513 +
514 + 16 X509_STACK_UNSAFE_ptr untrusted = nullptr;
515 +
516 + // Build potentially untrusted intermediary (subca) certificates
517 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 16 times.
+
+
+
16 if (false == untrusted_subcas.empty()) {
518 + untrusted = X509_STACK_UNSAFE_ptr(sk_X509_new_null());
519 + int flags = X509_ADD_FLAG_NO_DUP | X509_ADD_FLAG_NO_SS;
520 +
521 + for (auto& untrusted_cert : untrusted_subcas) {
522 + if (1 != X509_add_cert(untrusted.get(), get(untrusted_cert), flags)) {
523 + EVLOG_error << "X509 could not create untrusted store stack!";
524 + return CertificateValidationResult::Unknown;
525 + }
526 + }
527 + }
528 +
529 +
+ 2/4 +
+
✓ Branch 5 taken 16 times.
+
✗ Branch 6 not taken.
+
✗ Branch 7 not taken.
+
✓ Branch 8 taken 16 times.
+
+
+
16 if (1 != X509_STORE_CTX_init(store_ctx_ptr.get(), store_ptr.get(), get(target), untrusted.get())) {
530 + EVLOG_error << "X509 could not init x509 store ctx!";
531 + return CertificateValidationResult::Unknown;
532 + }
533 +
534 +
+ 1/2 +
+
✓ Branch 0 taken 16 times.
+
✗ Branch 1 not taken.
+
+
+
16 if (allow_future_certificates) {
535 + // Manually check if cert is expired
536 + int day, sec;
537 +
+ 2/4 +
+
✓ Branch 2 taken 16 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 16 times.
+
✗ Branch 6 not taken.
+
+
+
16 ASN1_TIME_diff(&day, &sec, nullptr, X509_get_notAfter(get(target)));
538 +
+ 3/4 +
+
✓ Branch 0 taken 14 times.
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 14 times.
+
+
+
16 if (day < 0 || sec < 0) {
539 + // certificate is expired
540 + 2 return CertificateValidationResult::Expired;
541 + }
542 + // certificate is not expired, but may not be valid yet. Since we allow future certs, disable time checks.
543 +
+ 1/2 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
+
+
14 X509_STORE_CTX_set_flags(store_ctx_ptr.get(), X509_V_FLAG_NO_CHECK_TIME);
544 + }
545 +
546 + // verifies the certificate chain based on ctx
547 + // verifies the certificate has not expired and is already valid
548 +
+ 3/4 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 2 times.
+
✓ Branch 5 taken 12 times.
+
+
+
14 if (X509_verify_cert(store_ctx_ptr.get()) != 1) {
549 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 int ec = X509_STORE_CTX_get_error(store_ctx_ptr.get());
550 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 return to_certificate_error(ec);
551 + }
552 +
553 + 12 return CertificateValidationResult::Valid;
554 + 16}
555 +
556 + 202KeyValidationResult OpenSSLSupplier::x509_check_private_key(X509Handle* handle, std::string private_key,
557 + std::optional<std::string> password) {
558 + 202 X509* x509 = get(handle);
559 +
560 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 202 times.
+
+
+
202 if (x509 == nullptr) {
561 + return KeyValidationResult::Unknown;
562 + }
563 +
564 +
+ 1/2 +
+
✓ Branch 1 taken 202 times.
+
✗ Branch 2 not taken.
+
+
+
202 OpenSSLProvider provider;
565 +
566 +
+ 1/2 +
+
✓ Branch 1 taken 202 times.
+
✗ Branch 2 not taken.
+
+
+
202 const bool custom_key = is_custom_private_key_string(private_key);
567 +
+ 2/2 +
+
✓ Branch 0 taken 72 times.
+
✓ Branch 1 taken 130 times.
+
+
+
202 if (custom_key) {
568 +
+ 1/2 +
+
✓ Branch 1 taken 72 times.
+
✗ Branch 2 not taken.
+
+
+
72 provider.set_global_mode(OpenSSLProvider::mode_t::custom_provider);
569 + } else {
570 +
+ 1/2 +
+
✓ Branch 1 taken 130 times.
+
✗ Branch 2 not taken.
+
+
+
130 provider.set_global_mode(OpenSSLProvider::mode_t::default_provider);
571 + }
572 +
+ 13/24 +
+
✓ Branch 1 taken 202 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 202 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 202 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 202 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 202 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 202 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 202 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 202 times.
+
✗ Branch 24 not taken.
+
✓ Branch 26 taken 202 times.
+
✗ Branch 27 not taken.
+
✓ Branch 29 taken 202 times.
+
✗ Branch 30 not taken.
+
✓ Branch 32 taken 202 times.
+
✗ Branch 33 not taken.
+
✓ Branch 35 taken 202 times.
+
✓ Branch 36 taken 202 times.
+
+
+
404 EVLOG_debug << "Is Custom Key: " << custom_key;
573 +
574 +
+ 1/2 +
+
✓ Branch 2 taken 202 times.
+
✗ Branch 3 not taken.
+
+
+
202 BIO_ptr bio(BIO_new_mem_buf(private_key.c_str(), -1));
575 + // Passing password string since if NULL is provided, the password CB will be called
576 +
+ 2/4 +
+
✓ Branch 1 taken 202 times.
+
✗ Branch 2 not taken.
+
✓ Branch 6 taken 202 times.
+
✗ Branch 7 not taken.
+
+
+
202 EVP_PKEY_ptr evp_pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, (void*)password.value_or("").c_str()));
577 +
578 + 202 bool bResult = true;
579 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 202 times.
+
+
+
202 if (!evp_pkey) {
580 + EVLOG_warning << "Invalid evp_pkey: " << private_key << " error: " << ERR_error_string(ERR_get_error(), NULL)
581 + << " Password configured correctly?";
582 + ERR_print_errors_fp(stderr);
583 +
584 + return KeyValidationResult::KeyLoadFailure;
585 + }
586 +
587 +
+ 3/4 +
+
✓ Branch 2 taken 202 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 140 times.
+
✓ Branch 5 taken 62 times.
+
+
+
202 if (X509_check_private_key(x509, evp_pkey.get()) == 1) {
588 + 140 return KeyValidationResult::Valid;
589 + } else {
590 + 62 return KeyValidationResult::Invalid;
591 + }
592 + 202}
593 +
594 + bool OpenSSLSupplier::x509_verify_signature(X509Handle* handle, const std::vector<std::uint8_t>& signature,
595 + const std::vector<std::uint8_t>& data) {
596 + OpenSSLProvider provider;
597 + provider.set_global_mode(OpenSSLProvider::mode_t::default_provider);
598 + // extract public key
599 + X509* x509 = get(handle);
600 +
601 + if (x509 == nullptr)
602 + return false;
603 +
604 + EVP_PKEY_ptr public_key_ptr(X509_get_pubkey(x509));
605 +
606 + if (!public_key_ptr.get()) {
607 + EVLOG_error << "Error during X509_get_pubkey";
608 + return false;
609 + }
610 +
611 + // verify file signature
612 + EVP_PKEY_CTX_ptr public_key_context_ptr(EVP_PKEY_CTX_new(public_key_ptr.get(), nullptr));
613 +
614 + if (!public_key_context_ptr.get()) {
615 + EVLOG_error << "Error setting up public key context";
616 + return false;
617 + }
618 +
619 + if (EVP_PKEY_verify_init(public_key_context_ptr.get()) <= 0) {
620 + EVLOG_error << "Error during EVP_PKEY_verify_init";
621 + return false;
622 + }
623 +
624 + if (EVP_PKEY_CTX_set_signature_md(public_key_context_ptr.get(), EVP_sha256()) <= 0) {
625 + EVLOG_error << "Error during EVP_PKEY_CTX_set_signature_md";
626 + return false;
627 + };
628 +
629 + int result = EVP_PKEY_verify(public_key_context_ptr.get(), reinterpret_cast<const unsigned char*>(signature.data()),
630 + signature.size(), reinterpret_cast<const unsigned char*>(data.data()), data.size());
631 +
632 + EVP_cleanup();
633 +
634 + if (result != 1) {
635 + EVLOG_error << "Failure to verify: " << result;
636 + return false;
637 + } else {
638 + EVLOG_debug << "Successful verification";
639 + return true;
640 + }
641 + }
642 +
643 + 14CertificateSignRequestResult OpenSSLSupplier::x509_generate_csr(const CertificateSigningRequestInfo& csr_info,
644 + std::string& out_csr) {
645 +
646 + 14 KeyHandle_ptr gen_key;
647 + 14 EVP_PKEY_CTX_ptr ctx;
648 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 OpenSSLProvider provider;
649 +
650 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 14 times.
+
+
+
14 if (csr_info.key_info.generate_on_custom) {
651 + provider.set_global_mode(OpenSSLProvider::mode_t::custom_provider);
652 + } else {
653 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 provider.set_global_mode(OpenSSLProvider::mode_t::default_provider);
654 + }
655 +
656 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 14 times.
+
+
+
14 if (false == s_generate_key(csr_info.key_info, gen_key, ctx)) {
657 + return CertificateSignRequestResult::KeyGenerationError;
658 + }
659 +
660 + 14 EVP_PKEY* key = get(gen_key.get());
661 +
662 + // X509 CSR request
663 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_REQ_ptr x509_req_ptr(X509_REQ_new());
664 +
665 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 14 times.
+
+
+
14 if (nullptr == x509_req_ptr.get()) {
666 + EVLOG_error << "Failed to create CSR request!";
667 + ERR_print_errors_fp(stderr);
668 +
669 + return CertificateSignRequestResult::Unknown;
670 + }
671 +
672 + // set version of x509 req
673 + 14 int n_version = csr_info.n_version;
674 +
675 +
+ 2/4 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 14 times.
+
+
+
14 if (false == X509_REQ_set_version(x509_req_ptr.get(), n_version)) {
676 + EVLOG_error << "Failed to set csr version!";
677 + ERR_print_errors_fp(stderr);
678 +
679 + return CertificateSignRequestResult::VersioningError;
680 + }
681 +
682 + // set public key of x509 req
683 +
+ 2/4 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
✗ Branch 4 not taken.
+
✓ Branch 5 taken 14 times.
+
+
+
14 if (false == X509_REQ_set_pubkey(x509_req_ptr.get(), key)) {
684 + EVLOG_error << "Failed to set csr pubkey!";
685 + ERR_print_errors_fp(stderr);
686 +
687 + return CertificateSignRequestResult::PubkeyError;
688 + }
689 +
690 +
+ 1/2 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
+
+
14 X509_NAME* x509Name = X509_REQ_get_subject_name(x509_req_ptr.get());
691 +
692 + // set subject of x509 req
693 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_NAME_add_entry_by_txt(x509Name, "C", MBSTRING_ASC,
694 + 14 reinterpret_cast<const unsigned char*>(csr_info.country.c_str()), -1, -1, 0);
695 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_NAME_add_entry_by_txt(x509Name, "O", MBSTRING_ASC,
696 + 14 reinterpret_cast<const unsigned char*>(csr_info.organization.c_str()), -1, -1, 0);
697 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_NAME_add_entry_by_txt(x509Name, "CN", MBSTRING_ASC,
698 + 14 reinterpret_cast<const unsigned char*>(csr_info.commonName.c_str()), -1, -1, 0);
699 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_NAME_add_entry_by_txt(x509Name, "DC", MBSTRING_ASC, reinterpret_cast<const unsigned char*>("CPO"), -1, -1, 0);
700 +
701 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 STACK_OF(X509_EXTENSION)* extensions = sk_X509_EXTENSION_new_null();
702 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_EXTENSION* ext_key_usage = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, "digitalSignature, keyAgreement");
703 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_EXTENSION* ext_basic_constraints = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, "critical,CA:false");
704 +
+ 1/2 +
+
✓ Branch 3 taken 14 times.
+
✗ Branch 4 not taken.
+
+
+
14 sk_X509_EXTENSION_push(extensions, ext_key_usage);
705 +
+ 1/2 +
+
✓ Branch 3 taken 14 times.
+
✗ Branch 4 not taken.
+
+
+
14 sk_X509_EXTENSION_push(extensions, ext_basic_constraints);
706 +
707 + 14 std::vector<std::string> names;
708 +
+ 2/2 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 10 times.
+
+
+
14 if (csr_info.dns_name.has_value()) {
709 +
+ 4/8 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 4 times.
+
✗ Branch 6 not taken.
+
✓ Branch 8 taken 4 times.
+
✗ Branch 9 not taken.
+
✓ Branch 11 taken 4 times.
+
✗ Branch 12 not taken.
+
+
+
4 names.push_back({std::string("DNS:") + csr_info.dns_name.value()});
710 + }
711 +
+ 2/2 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 10 times.
+
+
+
14 if (csr_info.ip_address.has_value()) {
712 +
+ 4/8 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 4 times.
+
✗ Branch 6 not taken.
+
✓ Branch 8 taken 4 times.
+
✗ Branch 9 not taken.
+
✓ Branch 11 taken 4 times.
+
✗ Branch 12 not taken.
+
+
+
4 names.push_back({std::string("IP:") + csr_info.ip_address.value()});
713 + }
714 +
715 + 14 X509_EXTENSION* ext_san = nullptr;
716 +
+ 2/2 +
+
✓ Branch 1 taken 6 times.
+
✓ Branch 2 taken 8 times.
+
+
+
14 if (!names.empty()) {
717 +
+ 2/4 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 2 times.
+
✗ Branch 6 not taken.
+
+
+
4 auto comma_fold = [](std::string a, const std::string& b) { return std::move(a) + ',' + b; };
718 +
+ 3/6 +
+
✓ Branch 2 taken 6 times.
+
✗ Branch 3 not taken.
+
✓ Branch 7 taken 6 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 6 times.
+
✗ Branch 11 not taken.
+
+
+
6 std::string value = std::accumulate(std::next(names.begin()), names.end(), std::string(names[0]), comma_fold);
719 +
+ 1/2 +
+
✓ Branch 2 taken 6 times.
+
✗ Branch 3 not taken.
+
+
+
6 ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, value.c_str());
720 +
+ 1/2 +
+
✓ Branch 3 taken 6 times.
+
✗ Branch 4 not taken.
+
+
+
6 sk_X509_EXTENSION_push(extensions, ext_san);
721 + 6 }
722 +
723 +
+ 1/2 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
+
+
14 const bool result = X509_REQ_add_extensions(x509_req_ptr.get(), extensions);
724 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_EXTENSION_free(ext_key_usage);
725 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_EXTENSION_free(ext_basic_constraints);
726 +
+ 1/2 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
+
+
14 X509_EXTENSION_free(ext_san);
727 +
+ 1/2 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
+
+
14 sk_X509_EXTENSION_free(extensions);
728 +
729 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 14 times.
+
+
+
14 if (!result) {
730 + EVLOG_error << "Failed to add csr extensions!";
731 + ERR_print_errors_fp(stderr);
732 +
733 + return CertificateSignRequestResult::ExtensionsError;
734 + }
735 +
736 + // sign the certificate with the private key
737 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 14 times.
+
✗ Branch 6 not taken.
+
+
+
14 bool x509_signed = X509_REQ_sign(x509_req_ptr.get(), key, EVP_sha256());
738 +
739 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 14 times.
+
+
+
14 if (x509_signed == false) {
740 + EVLOG_error << "Failed to sign csr with error!";
741 + ERR_print_errors_fp(stderr);
742 +
743 + return CertificateSignRequestResult::SigningError;
744 + }
745 +
746 + // write csr
747 +
+ 2/4 +
+
✓ Branch 1 taken 14 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 14 times.
+
✗ Branch 5 not taken.
+
+
+
14 BIO_ptr bio(BIO_new(BIO_s_mem()));
748 +
+ 1/2 +
+
✓ Branch 3 taken 14 times.
+
✗ Branch 4 not taken.
+
+
+
14 PEM_write_bio_X509_REQ(bio.get(), x509_req_ptr.get());
749 +
750 + 14 BUF_MEM* mem_csr = NULL;
751 +
+ 1/2 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
+
+
14 BIO_get_mem_ptr(bio.get(), &mem_csr);
752 +
753 +
+ 1/2 +
+
✓ Branch 2 taken 14 times.
+
✗ Branch 3 not taken.
+
+
+
14 out_csr = std::string(mem_csr->data, mem_csr->length);
754 +
755 + 14 return CertificateSignRequestResult::Valid;
756 + 14}
757 +
758 + bool OpenSSLSupplier::digest_file_sha256(const fs::path& path, std::vector<std::uint8_t>& out_digest) {
759 + EVP_MD_CTX_ptr md_context_ptr(EVP_MD_CTX_create());
760 + if (!md_context_ptr.get()) {
761 + EVLOG_error << "Could not create EVP_MD_CTX";
762 + return false;
763 + }
764 +
765 + const EVP_MD* md = EVP_get_digestbyname("SHA256");
766 + if (EVP_DigestInit_ex(md_context_ptr.get(), md, nullptr) == 0) {
767 + EVLOG_error << "Error during EVP_DigestInit_ex";
768 + return false;
769 + }
770 +
771 + bool digest_error = false;
772 +
773 + unsigned int sha256_out_length = 0;
774 + std::uint8_t sha256_out[EVP_MAX_MD_SIZE];
775 +
776 + // calculate sha256 of file
777 + bool processed_file = filesystem_utils::process_file(
778 + path, BUFSIZ, [&](const std::uint8_t* bytes, std::size_t read, bool last_chunk) -> bool {
779 + if (read > 0) {
780 + if (EVP_DigestUpdate(md_context_ptr.get(), bytes, read) == 0) {
781 + EVLOG_error << "Error during EVP_DigestUpdate";
782 + digest_error = true;
783 + return true;
784 + }
785 + }
786 +
787 + if (last_chunk) {
788 + if (EVP_DigestFinal_ex(md_context_ptr.get(), reinterpret_cast<unsigned char*>(sha256_out),
789 + &sha256_out_length) == 0) {
790 + EVLOG_error << "Error during EVP_DigestFinal_ex";
791 + digest_error = true;
792 + return true;
793 + }
794 + }
795 +
796 + return false;
797 + });
798 +
799 + if ((processed_file == false) || (digest_error == true)) {
800 + EVLOG_error << "Could not digest file at: " << path.string();
801 + return false;
802 + }
803 +
804 + out_digest.clear();
805 + out_digest.insert(std::end(out_digest), sha256_out, sha256_out + sha256_out_length);
806 +
807 + return true;
808 + }
809 +
810 + 4template <typename T> static bool base64_decode(const std::string& base64_string, T& out_decoded) {
811 + 4 EVP_ENCODE_CTX_ptr base64_decode_context_ptr(EVP_ENCODE_CTX_new());
812 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 2 times.
+
+
+
4 if (!base64_decode_context_ptr.get()) {
813 + EVLOG_error << "Error during EVP_ENCODE_CTX_new";
814 + return false;
815 + }
816 +
817 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
4 EVP_DecodeInit(base64_decode_context_ptr.get());
818 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 2 times.
+
+
+
4 if (!base64_decode_context_ptr.get()) {
819 + EVLOG_error << "Error during EVP_DecodeInit";
820 + return false;
821 + }
822 +
823 + 4 const unsigned char* encoded_str = reinterpret_cast<const unsigned char*>(base64_string.data());
824 + 4 int base64_length = base64_string.size();
825 +
826 + 4 std::uint8_t decoded_out[base64_length];
827 +
828 + int decoded_out_length;
829 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
4 if (EVP_DecodeUpdate(base64_decode_context_ptr.get(), reinterpret_cast<unsigned char*>(decoded_out),
830 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 2 times.
+
+
+
4 &decoded_out_length, encoded_str, base64_length) < 0) {
831 + EVLOG_error << "Error during DecodeUpdate";
832 + return false;
833 + }
834 +
835 + int decode_final_out;
836 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
4 if (EVP_DecodeFinal(base64_decode_context_ptr.get(), reinterpret_cast<unsigned char*>(decoded_out),
837 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 2 times.
+
+
+
4 &decode_final_out) < 0) {
838 + EVLOG_error << "Error during EVP_DecodeFinal";
839 + return false;
840 + }
841 +
842 + 4 out_decoded.clear();
843 +
+ 1/2 +
+
✓ Branch 3 taken 2 times.
+
✗ Branch 4 not taken.
+
+
+
4 out_decoded.insert(std::end(out_decoded), decoded_out, decoded_out + decoded_out_length);
844 +
845 + 4 return true;
846 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
8}
847 +
848 + 2static bool base64_encode(const unsigned char* bytes_str, int bytes_size, std::string& out_encoded) {
849 + 2 EVP_ENCODE_CTX_ptr base64_encode_context_ptr(EVP_ENCODE_CTX_new());
850 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 2 times.
+
+
+
2 if (!base64_encode_context_ptr.get()) {
851 + EVLOG_error << "Error during EVP_ENCODE_CTX_new";
852 + return false;
853 + }
854 +
855 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 EVP_EncodeInit(base64_encode_context_ptr.get());
856 + // evp_encode_ctx_set_flags(base64_encode_context_ptr.get(), EVP_ENCODE_CTX_NO_NEWLINES); // Of course it's not
857 + // public
858 +
859 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 2 times.
+
+
+
2 if (!base64_encode_context_ptr.get()) {
860 + EVLOG_error << "Error during EVP_EncodeInit";
861 + return false;
862 + }
863 +
864 + 2 int base64_length = ((bytes_size / 3) * 4) + 2;
865 + // If it causes issues, replace with 'alloca' on different platform
866 + 2 char base64_out[base64_length + 66]; // + 66 bytes for final block
867 + 2 int full_len = 0;
868 +
869 + int base64_out_length;
870 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 if (EVP_EncodeUpdate(base64_encode_context_ptr.get(), reinterpret_cast<unsigned char*>(base64_out),
871 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 2 times.
+
+
+
2 &base64_out_length, bytes_str, bytes_size) < 0) {
872 + EVLOG_error << "Error during EVP_EncodeUpdate";
873 + return false;
874 + }
875 + 2 full_len += base64_out_length;
876 +
877 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 EVP_EncodeFinal(base64_encode_context_ptr.get(), reinterpret_cast<unsigned char*>(base64_out) + base64_out_length,
878 + &base64_out_length);
879 + 2 full_len += base64_out_length;
880 +
881 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 out_encoded.assign(base64_out, full_len);
882 +
883 + 2 return true;
884 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
4}
885 +
886 + bool OpenSSLSupplier::base64_decode_to_bytes(const std::string& base64_string, std::vector<std::uint8_t>& out_decoded) {
887 + return base64_decode<std::vector<std::uint8_t>>(base64_string, out_decoded);
888 + }
889 +
890 + 2bool OpenSSLSupplier::base64_decode_to_string(const std::string& base64_string, std::string& out_decoded) {
891 + 2 return base64_decode<std::string>(base64_string, out_decoded);
892 + }
893 +
894 + bool OpenSSLSupplier::base64_encode_from_bytes(const std::vector<std::uint8_t>& bytes, std::string& out_encoded) {
895 + return base64_encode(reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size(), out_encoded);
896 + }
897 +
898 + 2bool OpenSSLSupplier::base64_encode_from_string(const std::string& string, std::string& out_encoded) {
899 + 2 return base64_encode(reinterpret_cast<const unsigned char*>(string.data()), string.size(), out_encoded);
900 + }
901 +
902 + } // namespace evse_security
903 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_provider.cpp.70788c4a83fb92199bed35fa968c2b42.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_provider.cpp.70788c4a83fb92199bed35fa968c2b42.html new file mode 100644 index 00000000..f407104a --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_provider.cpp.70788c4a83fb92199bed35fa968c2b42.html @@ -0,0 +1,2135 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/crypto/openssl/openssl_provider.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:82927.6%
Functions:41136.4%
Branches:080.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 +
4 + #include <evse_security/crypto/openssl/openssl_provider.hpp>
5 + #include <evse_security/evse_types.hpp>
6 +
7 + #include <openssl/opensslv.h>
8 +
9 + #if USING_CUSTOM_PROVIDER
10 + // OpenSSL3 without TPM will use the default provider anyway
11 + #include <openssl/err.h>
12 + #include <openssl/evp.h>
13 + #include <openssl/provider.h>
14 +
15 + #include <everest/logging.hpp>
16 + #else
17 + // dummy structures for non-OpenSSL 3
18 + struct ossl_provider_st {};
19 + typedef struct ossl_provider_st OSSL_PROVIDER;
20 + struct ossl_lib_ctx_st;
21 + typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
22 + #endif
23 +
24 + namespace evse_security {
25 +
26 + static auto KEY_HEADER_DEFAULT = "-----BEGIN PRIVATE KEY-----";
27 + static auto KEY_HEADER_TPM2 = "-----BEGIN TSS2 PRIVATE KEY-----";
28 +
29 + 202bool is_custom_private_key_string(const std::string& private_key_pem) {
30 + // If we can't find the standard header it means it's a custom key
31 + 202 return private_key_pem.find(KEY_HEADER_DEFAULT) == std::string::npos;
32 + }
33 +
34 + bool is_custom_private_key_file(const fs::path& private_key_file_pem) {
35 + if (fs::is_regular_file(private_key_file_pem)) {
36 + std::ifstream key_file(private_key_file_pem);
37 + std::string line;
38 + std::getline(key_file, line);
39 + key_file.close();
40 +
41 + // Search for the standard header
42 + return is_custom_private_key_string(line);
43 + }
44 +
45 + return false;
46 + }
47 +
48 + #ifdef USING_CUSTOM_PROVIDER
49 +
50 + constexpr bool is_custom_provider_tpm() {
51 + // custom provider string (see CMakeLists.txt)
52 + constexpr const std::string_view custom_provider(CUSTOM_PROVIDER_NAME);
53 + return (custom_provider == "tpm2");
54 + }
55 +
56 + // ----------------------------------------------------------------------------
57 + // class OpenSSLProvider OpenSSL 3
58 +
59 + static const char* mode_t_str[2] = {
60 + "default provider", // mode_t::default_provider
61 + "custom provider" // mode_t::custom_provider
62 + };
63 +
64 + static_assert(static_cast<int>(OpenSSLProvider::mode_t::default_provider) == 0);
65 + static_assert(static_cast<int>(OpenSSLProvider::mode_t::custom_provider) == 1);
66 +
67 + std::ostream& operator<<(std::ostream& out, OpenSSLProvider::mode_t mode) {
68 + const unsigned int idx = static_cast<unsigned int>(mode);
69 + if (idx <= static_cast<unsigned int>(OpenSSLProvider::mode_t::custom_provider)) {
70 + out << mode_t_str[idx];
71 + }
72 + return out;
73 + }
74 +
75 + static bool s_load_and_test_provider(OSSL_PROVIDER*& provider, OSSL_LIB_CTX* libctx, const char* provider_name) {
76 + bool result = true;
77 + #ifdef DEBUG
78 + const char* modestr = (libctx == nullptr) ? "global" : "TLS";
79 + EVLOG_info << "Loading " << modestr << " provider: " << provider_name;
80 + #endif
81 + if ((provider = OSSL_PROVIDER_load(libctx, provider_name)) == nullptr) {
82 + EVLOG_error << "Unable to load OSSL_PROVIDER: " << provider_name;
83 + ERR_print_errors_fp(stderr);
84 + result = false;
85 + } else {
86 + #ifdef DEBUG
87 + EVLOG_info << "Testing " << modestr << " provider: " << provider_name;
88 + #endif
89 + if (OSSL_PROVIDER_self_test(provider) == 0) {
90 + EVLOG_error << "Self-test failed: OSSL_PROVIDER: " << provider_name;
91 + ERR_print_errors_fp(stderr);
92 + OSSL_PROVIDER_unload(provider);
93 + provider = nullptr;
94 + result = false;
95 + }
96 + }
97 + return result;
98 + }
99 +
100 + std::mutex OpenSSLProvider::s_mux;
101 + OpenSSLProvider::flags_underlying_t OpenSSLProvider::s_flags = 0;
102 +
103 + OSSL_PROVIDER* OpenSSLProvider::s_global_prov_default_p = nullptr;
104 + OSSL_PROVIDER* OpenSSLProvider::s_global_prov_custom_p = nullptr;
105 + OSSL_PROVIDER* OpenSSLProvider::s_tls_prov_default_p = nullptr;
106 + OSSL_PROVIDER* OpenSSLProvider::s_tls_prov_custom_p = nullptr;
107 + OSSL_LIB_CTX* OpenSSLProvider::s_tls_libctx_p = nullptr;
108 +
109 + // propquery strings (see CMakeLists.txt)
110 + static const char* s_default_provider = PROPQUERY_PROVIDER_DEFAULT;
111 + static const char* s_custom_provider = PROPQUERY_PROVIDER_CUSTOM;
112 +
113 + OpenSSLProvider::OpenSSLProvider() {
114 + s_mux.lock();
115 +
116 + if (is_reset(flags_t::initialised)) {
117 + set(flags_t::initialised);
118 + OPENSSL_atexit(&OpenSSLProvider::cleanup);
119 +
120 + if (s_tls_libctx_p == nullptr) {
121 + s_tls_libctx_p = OSSL_LIB_CTX_new();
122 + if (s_tls_libctx_p == nullptr) {
123 + EVLOG_error << "Unable to create OpenSSL library context";
124 + ERR_print_errors_fp(stderr);
125 + }
126 + }
127 +
128 + // load providers for global context
129 + (void)load_global(mode_t::default_provider);
130 + (void)load_global(mode_t::custom_provider);
131 + (void)set_propstr(nullptr, mode_t::default_provider);
132 +
133 + // load providers for tls context
134 + (void)load_tls(mode_t::default_provider);
135 + (void)load_tls(mode_t::custom_provider);
136 + (void)set_propstr(s_tls_libctx_p, mode_t::default_provider);
137 + }
138 + }
139 +
140 + OpenSSLProvider::~OpenSSLProvider() {
141 + s_mux.unlock();
142 + }
143 +
144 + bool OpenSSLProvider::load(OSSL_PROVIDER*& default_p, OSSL_PROVIDER*& custom_p, OSSL_LIB_CTX* libctx_p, mode_t mode) {
145 + bool result = true;
146 + switch (mode) {
147 + case mode_t::custom_provider:
148 + if (custom_p == nullptr) {
149 + // custom provider string (see CMakeLists.txt)
150 + result = s_load_and_test_provider(custom_p, libctx_p, CUSTOM_PROVIDER_NAME);
151 + update(flags_t::custom_provider_available, result);
152 + }
153 + break;
154 + case mode_t::default_provider:
155 + default:
156 + if (default_p == nullptr) {
157 + result = s_load_and_test_provider(default_p, libctx_p, "default");
158 + }
159 + break;
160 + }
161 + return result;
162 + }
163 +
164 + bool OpenSSLProvider::set_propstr(OSSL_LIB_CTX* libctx, mode_t mode) {
165 + const char* propstr = propquery(mode);
166 + #ifdef DEBUG
167 + EVLOG_info << "Setting " << ((libctx == nullptr) ? "global" : "tls") << " propquery: " << propstr;
168 + #endif
169 + const bool result = EVP_set_default_properties(libctx, propstr) == 1;
170 + if (!result) {
171 + EVLOG_error << "Unable to set OpenSSL provider: " << mode;
172 + ERR_print_errors_fp(stderr);
173 + }
174 + return result;
175 + }
176 +
177 + bool OpenSSLProvider::set_mode(OSSL_LIB_CTX* libctx, mode_t mode) {
178 + bool result;
179 + const flags_t f = (libctx == nullptr) ? flags_t::global_custom_provider : flags_t::tls_custom_provider;
180 +
181 + const bool apply = update(f, mode == mode_t::custom_provider);
182 + if (apply) {
183 + result = set_propstr(libctx, mode);
184 + }
185 +
186 + return result;
187 + }
188 +
189 + const char* OpenSSLProvider::propquery(mode_t mode) const {
190 + const char* propquery_str = nullptr;
191 +
192 + switch (mode) {
193 + case mode_t::default_provider:
194 + propquery_str = s_default_provider;
195 + break;
196 + case mode_t::custom_provider:
197 + propquery_str = s_custom_provider;
198 + break;
199 + default:
200 + break;
201 + }
202 +
203 + return propquery_str;
204 + }
205 +
206 + void OpenSSLProvider::cleanup() {
207 + // at the point this is called logging may not be available
208 + // relying on OpenSSL errors
209 + std::lock_guard guard(s_mux);
210 + if (OSSL_PROVIDER_unload(s_tls_prov_custom_p) == 0) {
211 + ERR_print_errors_fp(stderr);
212 + }
213 + if (OSSL_PROVIDER_unload(s_tls_prov_default_p) == 0) {
214 + ERR_print_errors_fp(stderr);
215 + }
216 + if (OSSL_PROVIDER_unload(s_global_prov_custom_p) == 0) {
217 + ERR_print_errors_fp(stderr);
218 + }
219 + if (OSSL_PROVIDER_unload(s_global_prov_default_p) == 0) {
220 + ERR_print_errors_fp(stderr);
221 + }
222 +
223 + s_tls_prov_custom_p = nullptr;
224 + s_tls_prov_default_p = nullptr;
225 + s_global_prov_custom_p = nullptr;
226 + s_global_prov_default_p = nullptr;
227 +
228 + OSSL_LIB_CTX_free(s_tls_libctx_p);
229 +
230 + s_tls_libctx_p = nullptr;
231 + s_flags = 0;
232 + }
233 +
234 + bool OpenSSLProvider::supports_provider_tpm() {
235 + return is_set(flags_t::custom_provider_available) && is_custom_provider_tpm();
236 + }
237 +
238 + bool OpenSSLProvider::supports_provider_custom() {
239 + return is_set(flags_t::custom_provider_available);
240 + }
241 +
242 + #else // USING_OPENSSL_3_TPM
243 + // ----------------------------------------------------------------------------
244 + // class OpenSSLProvider dummy where OpenSSL 3 is not available
245 +
246 + OpenSSLProvider::flags_underlying_t OpenSSLProvider::s_flags = 0;
247 +
248 + OSSL_PROVIDER* OpenSSLProvider::s_global_prov_default_p = nullptr;
249 + OSSL_PROVIDER* OpenSSLProvider::s_global_prov_custom_p = nullptr;
250 + OSSL_PROVIDER* OpenSSLProvider::s_tls_prov_default_p = nullptr;
251 + OSSL_PROVIDER* OpenSSLProvider::s_tls_prov_custom_p = nullptr;
252 + OSSL_LIB_CTX* OpenSSLProvider::s_tls_libctx_p = nullptr;
253 +
254 + 244OpenSSLProvider::OpenSSLProvider() {
255 + 244}
256 +
257 + 244OpenSSLProvider::~OpenSSLProvider() {
258 + 244}
259 +
260 + bool OpenSSLProvider::load(OSSL_PROVIDER*&, OSSL_PROVIDER*&, OSSL_LIB_CTX*, mode_t) {
261 + return false;
262 + }
263 +
264 + bool OpenSSLProvider::set_propstr(OSSL_LIB_CTX*, mode_t) {
265 + return false;
266 + }
267 +
268 + 244bool OpenSSLProvider::set_mode(OSSL_LIB_CTX*, mode_t) {
269 + 244 return false;
270 + }
271 +
272 + const char* OpenSSLProvider::propquery(mode_t mode) const {
273 + return nullptr;
274 + }
275 +
276 + void OpenSSLProvider::cleanup() {
277 + }
278 +
279 + bool OpenSSLProvider::supports_provider_tpm() {
280 + return false;
281 + }
282 +
283 + bool OpenSSLProvider::supports_provider_custom() {
284 + return false;
285 + }
286 +
287 + #endif // USING_OPENSSL_3_TPM
288 +
289 + } // namespace evse_security
290 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_provider.hpp.4388443b4d40efdf369d95a6f763fdc6.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_provider.hpp.4388443b4d40efdf369d95a6f763fdc6.html new file mode 100644 index 00000000..3d3fd5bb --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_provider.hpp.4388443b4d40efdf369d95a6f763fdc6.html @@ -0,0 +1,1257 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:include/evse_security/crypto/openssl/openssl_provider.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:33100.0%
Functions:11100.0%
Branches:00-%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 +
4 + #ifndef OPENSSL_TPM_HPP
5 + #define OPENSSL_TPM_HPP
6 +
7 + #include <cstdint>
8 + #include <fstream>
9 + #include <mutex>
10 + #include <string>
11 +
12 + #include <evse_security/utils/evse_filesystem_types.hpp>
13 +
14 + // opaque types (from OpenSSL)
15 + struct ossl_lib_ctx_st; // OpenSSL OSSL_LIB_CTX;
16 + struct ossl_provider_st; // OpenSSL OSSL_PROVIDER
17 +
18 + namespace evse_security {
19 +
20 + /// @brief determine if the PEM string is a custom private key. Will
21 + /// only work for private keys, public keys will always return true
22 + /// @param private_key_pem string containing the PEM encoded key
23 + /// @return true when file does not start "-----BEGIN PRIVATE KEY-----"
24 + /// @note works irrespective of OpenSSL version
25 + bool is_custom_private_key_string(const std::string& private_key_pem);
26 +
27 + /// @brief determine if the PEM file contains a custom private key. Will
28 + /// only work for private keys, public keys will always return true
29 + /// @param private_key_file_pem filename of the PEM file
30 + /// @return true when file does not start "-----BEGIN PRIVATE KEY-----"
31 + /// @note works irrespective of OpenSSL version
32 + bool is_custom_private_key_file(const fs::path& private_key_file_pem);
33 +
34 + /// @brief Manage the loading and configuring of OpenSSL providers
35 + ///
36 + /// There are two providers considered:
37 + /// - 'default'
38 + /// - 'tpm2' for working with TSS2 keys (protected by a TPM)
39 + /// The 'tpm2' can be replaced with a custom provider (see CMakeLists.txt)
40 + ///
41 + /// There are two contexts:
42 + /// - 'global' for general use
43 + /// - 'tls' for TLS connections
44 + ///
45 + /// The class also acts as a scoped mutex to prevent changes in the
46 + /// provider configuration during crypto operations
47 + ///
48 + /// @note OpenSSL SSL_CTX caches the propquery so updates via
49 + /// this class may not be effective. See SSL_CTX_new_ex()
50 + ///
51 + /// This code provides a null implementation when OpenSSL 3 or later isn't
52 + /// used. The null implementation is also used when -DUSING_TPM2=OFF is
53 + /// set with cmake.
54 + ///
55 + /// @note the tpm2-abrmd daemon is needed to support openssl-tpm2 for TLS
56 + class OpenSSLProvider {
57 + public:
58 + /// @brief supported propquery strings
59 + enum class mode_t {
60 + default_provider,
61 + custom_provider,
62 + };
63 +
64 + private:
65 + typedef std::uint8_t flags_underlying_t;
66 + enum class flags_t : flags_underlying_t {
67 + initialised,
68 + custom_provider_available,
69 + global_custom_provider,
70 + tls_custom_provider,
71 + };
72 +
73 + static std::mutex s_mux;
74 + static flags_underlying_t s_flags;
75 +
76 + static struct ossl_provider_st* s_global_prov_default_p;
77 + static struct ossl_provider_st* s_global_prov_custom_p;
78 + static struct ossl_provider_st* s_tls_prov_default_p;
79 + static struct ossl_provider_st* s_tls_prov_custom_p;
80 + static struct ossl_lib_ctx_st* s_tls_libctx_p;
81 +
82 + static inline void reset(flags_t f) {
83 + s_flags &= ~(1 << static_cast<flags_underlying_t>(f));
84 + }
85 +
86 + static inline void set(flags_t f) {
87 + s_flags |= 1 << static_cast<flags_underlying_t>(f);
88 + }
89 +
90 + static inline bool is_set(flags_t f) {
91 + return (s_flags & (1 << static_cast<flags_underlying_t>(f))) != 0;
92 + }
93 +
94 + static inline bool is_reset(flags_t f) {
95 + return !is_set(f);
96 + }
97 +
98 + /// @brief uodate the flag
99 + /// @param f - flag to update
100 + /// @param val - whether to set or reset the flag
101 + /// @return true when the flag was changed
102 + static inline bool update(flags_t f, bool val) {
103 + bool result = (val != is_set(f));
104 + if (val) {
105 + set(f);
106 + } else {
107 + reset(f);
108 + }
109 + return result;
110 + }
111 +
112 + bool load(struct ossl_provider_st*& default_p, struct ossl_provider_st*& custom_p, struct ossl_lib_ctx_st* libctx_p,
113 + mode_t mode);
114 + inline bool load_global(mode_t mode) {
115 + return load(s_global_prov_default_p, s_global_prov_custom_p, nullptr, mode);
116 + }
117 + inline bool load_tls(mode_t mode) {
118 + return load(s_tls_prov_default_p, s_tls_prov_custom_p, s_tls_libctx_p, mode);
119 + }
120 +
121 + bool set_propstr(struct ossl_lib_ctx_st* libctx, mode_t mode);
122 + bool set_mode(struct ossl_lib_ctx_st* libctx, mode_t mode);
123 +
124 + public:
125 + OpenSSLProvider();
126 + ~OpenSSLProvider();
127 +
128 + 244 inline void set_global_mode(mode_t mode) {
129 + 244 set_mode(nullptr, mode);
130 + 244 }
131 +
132 + inline void set_tls_mode(mode_t mode) {
133 + set_mode(s_tls_libctx_p, mode);
134 + }
135 +
136 + const char* propquery(mode_t mode) const;
137 +
138 + inline mode_t propquery_global() const {
139 + return (is_set(flags_t::global_custom_provider)) ? mode_t::custom_provider : mode_t::default_provider;
140 + }
141 + inline mode_t propquery_tls() const {
142 + return (is_set(flags_t::tls_custom_provider)) ? mode_t::custom_provider : mode_t::default_provider;
143 + }
144 +
145 + inline const char* propquery_global_str() const {
146 + return propquery(propquery_global());
147 + }
148 + inline const char* propquery_tls_str() const {
149 + return propquery(propquery_tls());
150 + }
151 +
152 + /// @brief return the TLS OSSL library context
153 + inline operator struct ossl_lib_ctx_st *() {
154 + return s_tls_libctx_p;
155 + }
156 +
157 + static bool supports_provider_tpm();
158 + static bool supports_provider_custom();
159 +
160 + static void cleanup();
161 + };
162 +
163 + } // namespace evse_security
164 +
165 + #endif // OPENSSL_TPM_HPP
166 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_types.hpp.6d83d0a61ec1152885e9ecb9d2dbe99d.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_types.hpp.6d83d0a61ec1152885e9ecb9d2dbe99d.html new file mode 100644 index 00000000..c0f77a4c --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.openssl_types.hpp.6d83d0a61ec1152885e9ecb9d2dbe99d.html @@ -0,0 +1,983 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:include/evse_security/crypto/openssl/openssl_types.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:323884.2%
Functions:121485.7%
Branches:00-%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #pragma once
4 +
5 + #ifdef LIBEVSE_CRYPTO_SUPPLIER_OPENSSL
6 +
7 + #include <memory>
8 + #include <openssl/x509v3.h>
9 +
10 + template <> class std::default_delete<X509> {
11 + public:
12 + 7132 void operator()(X509* ptr) const {
13 + 7132 ::X509_free(ptr);
14 + 7132 }
15 + };
16 +
17 + template <> class std::default_delete<X509_STORE> {
18 + public:
19 + 3082 void operator()(X509_STORE* ptr) const {
20 + 3082 ::X509_STORE_free(ptr);
21 + 3082 }
22 + };
23 +
24 + template <> class std::default_delete<X509_STORE_CTX> {
25 + public:
26 + 3082 void operator()(X509_STORE_CTX* ptr) const {
27 + 3082 ::X509_STORE_CTX_free(ptr);
28 + 3082 }
29 + };
30 +
31 + template <> class std::default_delete<X509_REQ> {
32 + public:
33 + 14 void operator()(X509_REQ* ptr) const {
34 + 14 ::X509_REQ_free(ptr);
35 + 14 }
36 + };
37 +
38 + template <> class std::default_delete<STACK_OF(X509)> {
39 + public:
40 + void operator()(STACK_OF(X509) * ptr) const {
41 + sk_X509_free(ptr);
42 + }
43 + };
44 +
45 + template <> class std::default_delete<EVP_PKEY> {
46 + public:
47 + 228 void operator()(EVP_PKEY* ptr) const {
48 + 228 ::EVP_PKEY_free(ptr);
49 + 228 }
50 + };
51 +
52 + template <> class std::default_delete<EVP_PKEY_CTX> {
53 + public:
54 + 26 void operator()(EVP_PKEY_CTX* ptr) const {
55 + 26 ::EVP_PKEY_CTX_free(ptr);
56 + 26 }
57 + };
58 +
59 + template <> class std::default_delete<BIO> {
60 + public:
61 + 1360 void operator()(BIO* ptr) const {
62 + 1360 ::BIO_free(ptr);
63 + 1360 }
64 + };
65 +
66 + template <> class std::default_delete<EVP_MD_CTX> {
67 + public:
68 + void operator()(EVP_MD_CTX* ptr) const {
69 + ::EVP_MD_CTX_destroy(ptr);
70 + }
71 + };
72 +
73 + template <> class std::default_delete<EVP_ENCODE_CTX> {
74 + public:
75 + 4 void operator()(EVP_ENCODE_CTX* ptr) const {
76 + 4 ::EVP_ENCODE_CTX_free(ptr);
77 + 4 }
78 + };
79 +
80 + namespace evse_security {
81 +
82 + using X509_ptr = std::unique_ptr<X509>;
83 + using X509_STORE_ptr = std::unique_ptr<X509_STORE>;
84 + using X509_STORE_CTX_ptr = std::unique_ptr<X509_STORE_CTX>;
85 + // Unsafe since it does not free contained elements, only the stack, the element
86 + // cleanup has to be done manually
87 + using X509_STACK_UNSAFE_ptr = std::unique_ptr<STACK_OF(X509)>;
88 + using X509_REQ_ptr = std::unique_ptr<X509_REQ>;
89 + using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY>;
90 + using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX>;
91 + using BIO_ptr = std::unique_ptr<BIO>;
92 + using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX>;
93 + using EVP_ENCODE_CTX_ptr = std::unique_ptr<EVP_ENCODE_CTX>;
94 +
95 + struct X509Handle;
96 + struct KeyHandle;
97 +
98 + struct X509HandleOpenSSL : public X509Handle {
99 + 7132 X509HandleOpenSSL(X509* certificate) : x509(certificate) {
100 + 7132 }
101 +
102 + 22704 X509* get() {
103 + 22704 return x509.get();
104 + }
105 +
106 + private:
107 + X509_ptr x509;
108 + };
109 +
110 + struct KeyHandleOpenSSL : public KeyHandle {
111 + 26 KeyHandleOpenSSL(EVP_PKEY* key) : key(key) {
112 + 26 }
113 +
114 + 14 EVP_PKEY* get() {
115 + 14 return key.get();
116 + }
117 +
118 + private:
119 + EVP_PKEY_ptr key;
120 + };
121 +
122 + } // namespace evse_security
123 +
124 + #endif
125 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_bundle.cpp.219aaa49d2b73a419b71890d81c4a48c.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_bundle.cpp.219aaa49d2b73a419b71890d81c4a48c.html new file mode 100644 index 00000000..d4cb2dc0 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_bundle.cpp.219aaa49d2b73a419b71890d81c4a48c.html @@ -0,0 +1,3452 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/certificate/x509_bundle.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:14021465.4%
Functions:182669.2%
Branches:12835636.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #include <evse_security/certificate/x509_bundle.hpp>
4 +
5 + #include <algorithm>
6 + #include <fstream>
7 +
8 + #include <everest/logging.hpp>
9 + #include <evse_security/crypto/evse_crypto.hpp>
10 + #include <evse_security/utils/evse_filesystem.hpp>
11 +
12 + #include <openssl/err.h>
13 + #include <openssl/x509v3.h>
14 +
15 + namespace evse_security {
16 +
17 + X509Wrapper X509CertificateBundle::get_latest_valid_certificate(const std::vector<X509Wrapper>& certificates) {
18 + // Filter certificates with valid_in > 0
19 + std::vector<X509Wrapper> valid_certificates;
20 + for (const auto& cert : certificates) {
21 + if (cert.is_valid()) {
22 + valid_certificates.push_back(cert);
23 + }
24 + }
25 +
26 + if (valid_certificates.empty()) {
27 + // No valid certificates found
28 + throw NoCertificateValidException("No valid certificates available.");
29 + }
30 +
31 + // Find the certificate with the latest valid_in
32 + auto latest_certificate = std::max_element(
33 + valid_certificates.begin(), valid_certificates.end(),
34 + [](const X509Wrapper& cert1, const X509Wrapper& cert2) { return cert1.get_valid_in() < cert2.get_valid_in(); });
35 +
36 + return *latest_certificate;
37 + }
38 +
39 + 28X509CertificateBundle::X509CertificateBundle(const std::string& certificate, const EncodingFormat encoding) :
40 + 28 hierarchy_invalidated(true), source(X509CertificateSource::STRING) {
41 +
+ 1/2 +
+
✓ Branch 2 taken 28 times.
+
✗ Branch 3 not taken.
+
+
+
28 add_certificates(certificate, encoding, std::nullopt);
42 + 28}
43 +
44 + 488X509CertificateBundle::X509CertificateBundle(const fs::path& path, const EncodingFormat encoding) :
45 + 488 hierarchy_invalidated(true) {
46 +
+ 1/2 +
+
✓ Branch 1 taken 488 times.
+
✗ Branch 2 not taken.
+
+
+
488 this->path = path;
47 +
48 + // Attempt creation
49 +
+ 1/2 +
+
✓ Branch 1 taken 488 times.
+
✗ Branch 2 not taken.
+
+
+
488 filesystem_utils::create_file_or_dir_if_nonexistent(path);
50 +
51 +
+ 3/4 +
+
✓ Branch 1 taken 488 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 172 times.
+
✓ Branch 4 taken 316 times.
+
+
+
488 if (fs::is_directory(path)) {
52 + 172 source = X509CertificateSource::DIRECTORY;
53 +
54 + // Iterate directory
55 +
+ 4/6 +
+
✓ Branch 1 taken 172 times.
+
✗ Branch 2 not taken.
+
✓ Branch 11 taken 998 times.
+
✗ Branch 12 not taken.
+
✓ Branch 14 taken 998 times.
+
✓ Branch 15 taken 172 times.
+
+
+
1170 for (const auto& entry : fs::recursive_directory_iterator(path)) {
56 +
+ 3/4 +
+
✓ Branch 2 taken 998 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 526 times.
+
✓ Branch 5 taken 472 times.
+
+
+
998 if (is_certificate_file(entry)) {
57 + 526 std::string certificate;
58 +
+ 2/4 +
+
✓ Branch 2 taken 526 times.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 526 times.
+
✗ Branch 5 not taken.
+
+
+
526 if (filesystem_utils::read_from_file(entry.path(), certificate))
59 +
+ 2/4 +
+
✓ Branch 2 taken 526 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 526 times.
+
✗ Branch 6 not taken.
+
+
+
526 add_certificates(certificate, encoding, entry.path());
60 + 526 }
61 + 172 }
62 +
+ 2/4 +
+
✓ Branch 1 taken 316 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 316 times.
+
✗ Branch 4 not taken.
+
+
+
316 } else if (is_certificate_file(path)) {
63 + 316 source = X509CertificateSource::FILE;
64 +
65 + 316 std::string certificate;
66 +
+ 2/4 +
+
✓ Branch 1 taken 316 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 316 times.
+
✗ Branch 4 not taken.
+
+
+
316 if (filesystem_utils::read_from_file(path, certificate))
67 +
+ 2/4 +
+
✓ Branch 1 taken 316 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 316 times.
+
✗ Branch 5 not taken.
+
+
+
316 add_certificates(certificate, encoding, path);
68 + 316 } else {
69 + throw CertificateLoadException("Failed to create certificate info from path: " + path.string());
70 + }
71 + 488}
72 +
73 + 406std::vector<X509Wrapper> X509CertificateBundle::split() {
74 + 406 std::vector<X509Wrapper> full_certificates;
75 +
76 + // Append all chains
77 +
+ 2/2 +
+
✓ Branch 5 taken 554 times.
+
✓ Branch 6 taken 406 times.
+
+
+
960 for (const auto& chains : certificates) {
78 +
+ 2/2 +
+
✓ Branch 5 taken 1108 times.
+
✓ Branch 6 taken 554 times.
+
+
+
1662 for (const auto& cert : chains.second)
79 +
+ 1/2 +
+
✓ Branch 1 taken 1108 times.
+
✗ Branch 2 not taken.
+
+
+
1108 full_certificates.push_back(cert);
80 + }
81 +
82 + 406 return full_certificates;
83 + }
84 +
85 + 8int X509CertificateBundle::get_certificate_count() const {
86 + 8 int count = 0;
87 +
+ 2/2 +
+
✓ Branch 4 taken 10 times.
+
✓ Branch 5 taken 8 times.
+
+
+
18 for (const auto& chain : certificates) {
88 + 10 count += chain.second.size();
89 + }
90 +
91 + 8 return count;
92 + }
93 +
94 + 32int X509CertificateBundle::get_certificate_chains_count() const {
95 + 32 return certificates.size();
96 + }
97 +
98 + 870void X509CertificateBundle::add_certificates(const std::string& data, const EncodingFormat encoding,
99 + const std::optional<fs::path>& path) {
100 +
+ 1/2 +
+
✓ Branch 1 taken 870 times.
+
✗ Branch 2 not taken.
+
+
+
870 auto loaded = CryptoSupplier::load_certificates(data, encoding);
101 +
+ 2/4 +
+
✓ Branch 2 taken 870 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 870 times.
+
✗ Branch 6 not taken.
+
+
+
870 auto& list = certificates[path.value_or(std::filesystem::path())];
102 +
103 +
+ 2/2 +
+
✓ Branch 5 taken 1562 times.
+
✓ Branch 6 taken 870 times.
+
+
+
2432 for (auto& x509 : loaded) {
104 +
+ 2/2 +
+
✓ Branch 1 taken 1530 times.
+
✓ Branch 2 taken 32 times.
+
+
+
1562 if (path.has_value())
105 +
+ 2/4 +
+
✓ Branch 1 taken 1530 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 1530 times.
+
✗ Branch 6 not taken.
+
+
+
1530 list.emplace_back(std::move(x509), path.value());
106 + else
107 +
+ 1/2 +
+
✓ Branch 2 taken 32 times.
+
✗ Branch 3 not taken.
+
+
+
32 list.emplace_back(std::move(x509));
108 + }
109 + 870}
110 +
111 + 54bool X509CertificateBundle::contains_certificate(const X509Wrapper& certificate) {
112 + // Search through all the chains
113 +
+ 2/2 +
+
✓ Branch 5 taken 54 times.
+
✓ Branch 6 taken 36 times.
+
+
+
90 for (const auto& chain : certificates) {
114 +
+ 2/2 +
+
✓ Branch 5 taken 166 times.
+
✓ Branch 6 taken 36 times.
+
+
+
202 for (const auto& certif : chain.second) {
115 +
+ 3/4 +
+
✓ Branch 1 taken 166 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 18 times.
+
✓ Branch 4 taken 148 times.
+
+
+
166 if (certif == certificate)
116 + 18 return true;
117 + }
118 + }
119 +
120 + 36 return false;
121 + }
122 +
123 + bool X509CertificateBundle::contains_certificate(const CertificateHashData& certificate_hash) {
124 + // Try an initial search for root certificates, else a hierarchy build will be required
125 + for (const auto& chain : certificates) {
126 + bool found = std::find_if(std::begin(chain.second), std::end(chain.second), [&](const X509Wrapper& cert) {
127 + return cert.is_selfsigned() && cert == certificate_hash;
128 + }) != std::end(chain.second);
129 +
130 + if (found)
131 + return true;
132 + }
133 +
134 + // Nothing found, build the hierarchy and search by the issued hash
135 + X509CertificateHierarchy& hierarchy = get_certificate_hierarchy();
136 + return hierarchy.contains_certificate_hash(certificate_hash);
137 + }
138 +
139 + X509Wrapper X509CertificateBundle::find_certificate(const CertificateHashData& certificate_hash,
140 + bool case_insensitive_comparison) {
141 + // Try an initial search for root certificates, else a hierarchy build will be required
142 + for (const auto& chain : certificates) {
143 + for (const auto& certif : chain.second) {
144 + if (certif.is_selfsigned()) {
145 + bool matches = false;
146 +
147 + if (case_insensitive_comparison) {
148 + CertificateHashData certif_hash = certif.get_certificate_hash_data();
149 + matches = certif_hash.case_insensitive_comparison(certificate_hash);
150 + } else {
151 + matches = (certif == certificate_hash);
152 + }
153 +
154 + if (matches) {
155 + return certif;
156 + }
157 + }
158 + }
159 + }
160 +
161 + // Nothing found, build the hierarchy and search by the issued hash
162 + X509CertificateHierarchy& hierarchy = get_certificate_hierarchy();
163 + return hierarchy.find_certificate(certificate_hash, case_insensitive_comparison);
164 + }
165 +
166 + 12int X509CertificateBundle::delete_certificate(const X509Wrapper& certificate, bool include_issued) {
167 + 12 std::vector<X509Wrapper> to_delete;
168 +
169 +
+ 1/2 +
+
✓ Branch 0 taken 12 times.
+
✗ Branch 1 not taken.
+
+
+
12 if (include_issued) {
170 + // Include all descendants in the delete list
171 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 auto& hierarchy = get_certificate_hierarchy();
172 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 to_delete = hierarchy.collect_descendants(certificate);
173 + }
174 +
175 + // Include default delete
176 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 to_delete.push_back(certificate);
177 + 12 int deleted = 0;
178 +
179 +
+ 2/2 +
+
✓ Branch 4 taken 16 times.
+
✓ Branch 5 taken 12 times.
+
+
+
28 for (auto& chains : certificates) {
180 + 16 auto& certifs = chains.second;
181 +
182 +
+ 2/4 +
+
✓ Branch 4 taken 16 times.
+
✗ Branch 5 not taken.
+
✓ Branch 8 taken 16 times.
+
✗ Branch 9 not taken.
+
+
+
16 certifs.erase(std::remove_if(certifs.begin(), certifs.end(),
183 + 44 [&](const auto& certif) {
184 + bool found =
185 +
+ 1/2 +
+
✓ Branch 4 taken 44 times.
+
✗ Branch 5 not taken.
+
+
+
44 std::find(to_delete.begin(), to_delete.end(), certif) != to_delete.end();
186 +
187 +
+ 2/2 +
+
✓ Branch 0 taken 20 times.
+
✓ Branch 1 taken 24 times.
+
+
+
44 if (found)
188 + 20 deleted++;
189 +
190 + 44 return found;
191 + }),
192 + 32 certifs.end());
193 + }
194 +
195 + // If we deleted any, invalidate the built hierarchy
196 +
+ 1/2 +
+
✓ Branch 0 taken 12 times.
+
✗ Branch 1 not taken.
+
+
+
12 if (deleted) {
197 + 12 invalidate_hierarchy();
198 + }
199 +
200 + 12 return deleted;
201 + 12}
202 +
203 + 42int X509CertificateBundle::delete_certificate(const CertificateHashData& data, bool include_issued) {
204 + 42 auto& hierarchy = get_certificate_hierarchy();
205 +
206 + try {
207 + // Try to find the certificate by correct hierarchy hash
208 +
+ 2/2 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 34 times.
+
+
+
42 X509Wrapper to_delete = hierarchy.find_certificate(data, true /* = Case insensitive search */);
209 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 return delete_certificate(to_delete, include_issued);
210 +
+ 1/2 +
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 34 times.
+
+
+
42 } catch (NoCertificateFound& e) {
211 + 34 }
212 +
213 + 34 return 0;
214 + }
215 +
216 + void X509CertificateBundle::delete_all_certificates() {
217 + certificates.clear();
218 + }
219 +
220 + 36void X509CertificateBundle::add_certificate(X509Wrapper&& certificate) {
221 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 36 times.
+
+
+
36 if (source == X509CertificateSource::DIRECTORY) {
222 + // If it is in directory mode only allow sub-directories of that directory
223 + std::filesystem::path certif_path = certificate.get_file().value_or(std::filesystem::path());
224 +
225 + if (filesystem_utils::is_subdirectory(path, certif_path)) {
226 + certificates[certif_path].push_back(std::move(certificate));
227 + invalidate_hierarchy();
228 + } else {
229 + throw InvalidOperationException(
230 + "Added certificate with directory bundle, must be subdir of the main directory: " + path.string());
231 + }
232 + } else {
233 + // The bundle came from a file, so there is only one file we could add the certificate to
234 +
+ 1/2 +
+
✓ Branch 3 taken 36 times.
+
✗ Branch 4 not taken.
+
+
+
36 certificates.begin()->second.push_back(certificate);
235 + 36 invalidate_hierarchy();
236 + }
237 + 36}
238 +
239 + 24void X509CertificateBundle::add_certificate_unique(X509Wrapper&& certificate) {
240 +
+ 2/2 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 16 times.
+
+
+
24 if (!contains_certificate(certificate)) {
241 + 8 return add_certificate(std::move(certificate));
242 + invalidate_hierarchy();
243 + }
244 + }
245 +
246 + bool X509CertificateBundle::update_certificate(X509Wrapper&& certificate) {
247 + for (auto& chain : certificates) {
248 + for (auto& certif : chain.second) {
249 + if (certif == certificate) {
250 + certif = std::move(certificate);
251 + invalidate_hierarchy();
252 +
253 + return true;
254 + }
255 + }
256 + }
257 +
258 + return false;
259 + }
260 +
261 + 36bool X509CertificateBundle::export_certificates() {
262 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 36 times.
+
+
+
36 if (source == X509CertificateSource::STRING) {
263 + EVLOG_error << "Export for string is invalid!";
264 + return false;
265 + }
266 +
267 + // Add/delete certifs
268 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 36 times.
+
+
+
36 if (!sync_to_certificate_store()) {
269 + EVLOG_error << "Sync to certificate store failed!";
270 + return false;
271 + }
272 +
273 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 34 times.
+
+
+
36 if (source == X509CertificateSource::DIRECTORY) {
274 + 2 bool exported_all = true;
275 +
276 + // Write updated certificates
277 +
+ 2/2 +
+
✓ Branch 5 taken 2 times.
+
✓ Branch 6 taken 2 times.
+
+
+
4 for (auto& chains : certificates) {
278 + // Ignore empty chains (the file was deleted)
279 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 2 times.
+
+
+
2 if (chains.second.empty())
280 + continue;
281 +
282 + // Each chain is a single file
283 +
+ 3/6 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 2 times.
+
✗ Branch 5 not taken.
+
✗ Branch 7 not taken.
+
✓ Branch 8 taken 2 times.
+
+
+
2 if (!filesystem_utils::write_to_file(chains.first, to_export_string(chains.first), std::ios::trunc)) {
284 + exported_all = false;
285 + }
286 + }
287 +
288 + 2 return exported_all;
289 +
+ 1/2 +
+
✓ Branch 0 taken 34 times.
+
✗ Branch 1 not taken.
+
+
+
34 } else if (source == X509CertificateSource::FILE) {
290 + // We're using a single file, no need to check for deleted certificates
291 +
+ 2/4 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 34 times.
+
✗ Branch 5 not taken.
+
+
+
34 return filesystem_utils::write_to_file(path, to_export_string(), std::ios::trunc);
292 + }
293 +
294 + return false;
295 + }
296 +
297 + 38bool X509CertificateBundle::sync_to_certificate_store() {
298 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 38 times.
+
+
+
38 if (source == X509CertificateSource::STRING) {
299 + EVLOG_error << "Sync for string is invalid!";
300 + return false;
301 + }
302 +
303 +
+ 2/2 +
+
✓ Branch 0 taken 4 times.
+
✓ Branch 1 taken 34 times.
+
+
+
38 if (source == X509CertificateSource::DIRECTORY) {
304 + // Get existing certificates from filesystem
305 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 X509CertificateBundle fs_certificates(path, EncodingFormat::PEM);
306 + 4 bool success = true;
307 +
308 + // Delete filesystem certificate chains missing from our map
309 +
+ 2/2 +
+
✓ Branch 5 taken 8 times.
+
✓ Branch 6 taken 4 times.
+
+
+
12 for (const auto& fs_chain : fs_certificates.certificates) {
310 +
+ 2/4 +
+
✓ Branch 2 taken 8 times.
+
✗ Branch 3 not taken.
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 8 times.
+
+
+
8 if (certificates.find(fs_chain.first) == certificates.end()) {
311 + // fs certif chain not existing in our certificate list, delete
312 + if (!filesystem_utils::delete_file(fs_chain.first))
313 + success = false;
314 + }
315 + }
316 +
317 + // Add the certificates that are not existing in the filesystem. Each chain represents a single file
318 +
+ 2/2 +
+
✓ Branch 5 taken 8 times.
+
✓ Branch 6 taken 4 times.
+
+
+
12 for (const auto& chain : certificates) {
319 +
+ 2/2 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 4 times.
+
+
+
8 if (chain.second.empty()) {
320 + // If it's an empty chain, delete
321 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 4 times.
+
+
+
4 if (!filesystem_utils::delete_file(chain.first))
322 + success = false;
323 +
+ 2/4 +
+
✓ Branch 2 taken 4 times.
+
✗ Branch 3 not taken.
+
✗ Branch 5 not taken.
+
✓ Branch 6 taken 4 times.
+
+
+
4 } else if (fs_certificates.certificates.find(chain.first) == fs_certificates.certificates.end()) {
324 + // Certif not existing in fs certificates write it out
325 + if (!filesystem_utils::write_to_file(chain.first, to_export_string(chain.first), std::ios::trunc))
326 + success = false;
327 + }
328 + }
329 +
330 + // After fs deletion erase all empty files from our certificate list, so that we don't write them out
331 +
+ 2/2 +
+
✓ Branch 3 taken 8 times.
+
✓ Branch 4 taken 4 times.
+
+
+
12 for (auto first = certificates.begin(); first != certificates.end();) {
332 +
+ 2/2 +
+
✓ Branch 2 taken 4 times.
+
✓ Branch 3 taken 4 times.
+
+
+
8 if (first->second.empty())
333 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
4 first = certificates.erase(first);
334 + else
335 + 4 ++first;
336 + }
337 +
338 + 4 return success;
339 +
+ 1/2 +
+
✓ Branch 1 taken 34 times.
+
✗ Branch 2 not taken.
+
+
+
38 } else if (source == X509CertificateSource::FILE) {
340 + // Delete source file if we're empty
341 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 34 times.
+
+
+
34 if (certificates.empty()) {
342 + return filesystem_utils::delete_file(path);
343 + }
344 +
345 + 34 return true;
346 + }
347 +
348 + return false;
349 + }
350 +
351 + X509Wrapper X509CertificateBundle::get_latest_valid_certificate() {
352 + return get_latest_valid_certificate(split());
353 + }
354 +
355 + 48void X509CertificateBundle::invalidate_hierarchy() {
356 + 48 hierarchy_invalidated = true;
357 + 48}
358 +
359 + 140X509CertificateHierarchy& X509CertificateBundle::get_certificate_hierarchy() {
360 +
+ 2/2 +
+
✓ Branch 0 taken 126 times.
+
✓ Branch 1 taken 14 times.
+
+
+
140 if (hierarchy_invalidated) {
361 +
+ 9/16 +
+
✓ Branch 1 taken 126 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 126 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 126 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 126 times.
+
✗ Branch 11 not taken.
+
✓ Branch 13 taken 126 times.
+
✗ Branch 14 not taken.
+
✓ Branch 17 taken 126 times.
+
✗ Branch 18 not taken.
+
✓ Branch 20 taken 126 times.
+
✗ Branch 21 not taken.
+
✓ Branch 23 taken 126 times.
+
✓ Branch 24 taken 126 times.
+
+
+
252 EVLOG_info << "Building new certificate hierarchy!";
362 + 126 hierarchy_invalidated = false;
363 +
364 +
+ 1/2 +
+
✓ Branch 1 taken 126 times.
+
✗ Branch 2 not taken.
+
+
+
126 auto certificates = split();
365 +
+ 1/2 +
+
✓ Branch 1 taken 126 times.
+
✗ Branch 2 not taken.
+
+
+
126 hierarchy = X509CertificateHierarchy::build_hierarchy(certificates);
366 + 126 }
367 +
368 + 140 return hierarchy;
369 + }
370 +
371 + 34std::string X509CertificateBundle::to_export_string() const {
372 + 34 std::string export_string;
373 +
374 +
+ 2/2 +
+
✓ Branch 5 taken 34 times.
+
✓ Branch 6 taken 34 times.
+
+
+
68 for (auto& chain : certificates) {
375 +
+ 2/2 +
+
✓ Branch 4 taken 150 times.
+
✓ Branch 5 taken 34 times.
+
+
+
184 for (auto& certificate : chain.second) {
376 +
+ 2/4 +
+
✓ Branch 1 taken 150 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 150 times.
+
✗ Branch 5 not taken.
+
+
+
150 export_string += certificate.get_export_string();
377 + }
378 + }
379 +
380 + 34 return export_string;
381 + }
382 +
383 + 2std::string X509CertificateBundle::to_export_string(const std::filesystem::path& chain) const {
384 + 2 std::string export_string;
385 +
386 +
+ 1/2 +
+
✓ Branch 1 taken 2 times.
+
✗ Branch 2 not taken.
+
+
+
2 auto found = certificates.find(chain);
387 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 if (found != certificates.end()) {
388 +
+ 2/2 +
+
✓ Branch 5 taken 4 times.
+
✓ Branch 6 taken 2 times.
+
+
+
6 for (auto& certificate : found->second)
389 +
+ 2/4 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 4 times.
+
✗ Branch 5 not taken.
+
+
+
4 export_string += certificate.get_export_string();
390 + }
391 +
392 + 4 return export_string;
393 + }
394 +
395 + } // namespace evse_security
396 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_bundle.hpp.8ba7eb14d3ae8f3936b125660004f332.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_bundle.hpp.8ba7eb14d3ae8f3936b125660004f332.html new file mode 100644 index 00000000..b2d4b029 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_bundle.hpp.8ba7eb14d3ae8f3936b125660004f332.html @@ -0,0 +1,1651 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:include/evse_security/certificate/x509_bundle.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:2626100.0%
Functions:1111100.0%
Branches:273871.1%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #pragma once
4 +
5 + #include <algorithm>
6 + #include <map>
7 +
8 + #include <evse_security/certificate/x509_hierarchy.hpp>
9 + #include <evse_security/certificate/x509_wrapper.hpp>
10 +
11 + namespace evse_security {
12 +
13 + /// @brief Custom exception that is thrown when no private key could be found for a selected certificate
14 + class NoPrivateKeyException : public std::runtime_error {
15 + public:
16 + using std::runtime_error::runtime_error;
17 + };
18 +
19 + /// @brief Custom exception that is thrown when no valid certificate could be found for the specified filesystem
20 + /// locations
21 + class NoCertificateValidException : public std::runtime_error {
22 + public:
23 + using std::runtime_error::runtime_error;
24 + };
25 +
26 + /// @brief Custom exception that is thrown when a invalid operation is with the current state
27 + /// on the certificate bundle
28 + class InvalidOperationException : public std::runtime_error {
29 + public:
30 + using std::runtime_error::runtime_error;
31 + };
32 +
33 + /// @brief X509 certificate bundle, used for holding multiple X509Wrappers. Supports
34 + /// operations like add/delete importing/exporting certificates. Can use either a
35 + /// directory with multiple certificates (or bundles of certificates) or a single
36 + /// file with one or more certificates in it.
37 + class X509CertificateBundle {
38 + public:
39 + X509CertificateBundle(const fs::path& path, const EncodingFormat encoding);
40 + X509CertificateBundle(const std::string& certificate, const EncodingFormat encoding);
41 +
42 + X509CertificateBundle(X509CertificateBundle&& other) = default;
43 + X509CertificateBundle(const X509CertificateBundle& other) = delete;
44 +
45 + public:
46 + /// @brief Gets if this certificate bundle comes from a single certificate bundle file
47 + /// @return
48 + 58 bool is_using_bundle_file() const {
49 + 58 return (source == X509CertificateSource::FILE);
50 + }
51 +
52 + /// @brief Gets if this certificate bundle comes from an entire directory
53 + /// @return
54 + 34 bool is_using_directory() const {
55 + 34 return (source == X509CertificateSource::DIRECTORY);
56 + }
57 +
58 + /// @return True if multiple certificates are contained within
59 + bool is_bundle() const {
60 + return (get_certificate_count() > 1);
61 + }
62 +
63 + 22 bool empty() const {
64 + 22 return certificates.empty();
65 + }
66 +
67 + /// @return Contained certificate count
68 + int get_certificate_count() const;
69 +
70 + /// @return Contained certificate chains count
71 + int get_certificate_chains_count() const;
72 +
73 + 70 fs::path get_path() const {
74 + 70 return path;
75 + }
76 +
77 + X509CertificateSource get_source() const {
78 + return source;
79 + }
80 +
81 + /// @brief Iterates through all the contained certificate chains (file, certificates)
82 + /// while the provided function returns true
83 + 114 template <typename function> void for_each_chain(function func) {
84 +
+ 2/2 +
+
✓ Branch 5 taken 142 times.
+
✓ Branch 6 taken 40 times.
+
+
+
298 for (const auto& chain : certificates) {
85 +
+ 3/4 +
+
✓ Branch 1 taken 142 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 18 times.
+
✓ Branch 4 taken 124 times.
+
+
+
220 if (!func(chain.first, chain.second))
86 + 36 break;
87 + }
88 + 114 }
89 +
90 + /// @brief Same as 'for_each_chain' but it also uses a predicate for ordering
91 + 44 template <typename function, typename ordering> void for_each_chain_ordered(function func, ordering order) {
92 + struct Chain {
93 + const fs::path* path;
94 + const std::vector<X509Wrapper>* certificates;
95 + };
96 +
97 + 44 std::vector<Chain> ordered;
98 +
99 +
+ 2/2 +
+
✓ Branch 6 taken 110 times.
+
✓ Branch 7 taken 22 times.
+
+
+
264 for (auto& [path, certs] : certificates) {
100 +
+ 1/2 +
+
✓ Branch 1 taken 110 times.
+
✗ Branch 2 not taken.
+
+
+
220 ordered.push_back(Chain{&path, &certs});
101 + }
102 +
103 +
+ 1/2 +
+
✓ Branch 3 taken 22 times.
+
✗ Branch 4 not taken.
+
+
+
44 std::sort(std::begin(ordered), std::end(ordered),
104 + 372 [&order](Chain& a, Chain& b) { return order(*a.certificates, *b.certificates); });
105 +
106 +
+ 2/2 +
+
✓ Branch 5 taken 88 times.
+
✓ Branch 6 taken 4 times.
+
+
+
184 for (const auto& chain : ordered) {
107 +
+ 3/4 +
+
✓ Branch 1 taken 88 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 18 times.
+
✓ Branch 4 taken 70 times.
+
+
+
176 if (!func(*chain.path, *chain.certificates))
108 + 36 break;
109 + }
110 + 44 }
111 +
112 + public:
113 + /// @brief Splits the certificate (chain) into single certificates
114 + /// @return vector containing single certificates
115 + std::vector<X509Wrapper> split();
116 +
117 + /// @brief If we already have the certificate
118 + bool contains_certificate(const X509Wrapper& certificate);
119 + /// @brief If we already have the certificate hash
120 + bool contains_certificate(const CertificateHashData& certificate_hash);
121 +
122 + /// @brief Finds a certificate based on its hash
123 + X509Wrapper find_certificate(const CertificateHashData& certificate_hash, bool case_insensitive_comparison = false);
124 +
125 + /// @brief Adds a single certificate in the bundle. Only in memory, use @ref export_certificates to filesystem
126 + void add_certificate(X509Wrapper&& certificate);
127 +
128 + /// @brief Adds a single certificate in the bundle, only if it is not contained
129 + /// already. Only in memory, use @ref export_certificates to filesystem
130 + void add_certificate_unique(X509Wrapper&& certificate);
131 +
132 + /// @brief Updates an already existing certificate if it is found
133 + bool update_certificate(X509Wrapper&& certificate);
134 +
135 + /// @brief Deletes all instances of the provided certificate. Only in memory, use @ref export_certificates
136 + /// to filesystem export
137 + /// @param include_issued If true the child certificates will also be deleted, if any are found
138 + /// @return count of certificates deleted
139 + int delete_certificate(const X509Wrapper& certificate, bool include_issued);
140 +
141 + /// @brief Deletes all certificates with the provided certificate hash. Only in memory,
142 + /// use @ref export_certificates to filesystem export
143 + /// @param include_issued If true the child certificates will also be deleted, if any are found
144 + /// @return count of certificates deleted
145 + int delete_certificate(const CertificateHashData& data, bool include_issued);
146 +
147 + /// @brief Deletes all certificates. Only in memory, use @ref export_certificates to filesystem export
148 + void delete_all_certificates();
149 +
150 + /// @brief Returns a full exportable representation of a certificate bundle file in PEM format
151 + std::string to_export_string() const;
152 +
153 + /// @brief Returns a full exportable representation of a certificate sub-chain, if found
154 + std::string to_export_string(const std::filesystem::path& chain) const;
155 +
156 + /// @brief Exports the full certificate chain either as individual files if it is using a directory
157 + /// or as a bundle if it uses a bundle file, at the initially provided path. Also deletes/adds the updated
158 + /// certificates
159 + /// @return true on success, false otherwise
160 + bool export_certificates();
161 +
162 + /// @brief Syncs the file structure with the certificate store adding certificates that are not found on the
163 + /// storage and deleting the certificates that are not contained in this bundle
164 + bool sync_to_certificate_store();
165 +
166 + public:
167 + /// @brief returns the latest valid certificate within this bundle
168 + X509Wrapper get_latest_valid_certificate();
169 +
170 + /// @brief Utility that returns current the certificate hierarchy of this bundle
171 + /// Invalidated on any add/delete operation
172 + X509CertificateHierarchy& get_certificate_hierarchy();
173 +
174 + public:
175 + X509CertificateBundle& operator=(X509CertificateBundle&& other) = default;
176 +
177 + public:
178 + /// @brief Returns the latest valid certif that we might contain
179 + static X509Wrapper get_latest_valid_certificate(const std::vector<X509Wrapper>& certificates);
180 +
181 + 1320 static bool is_certificate_file(const fs::path& file) {
182 +
+ 5/6 +
+
✓ Branch 1 taken 1320 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 1282 times.
+
✓ Branch 4 taken 38 times.
+
✓ Branch 5 taken 510 times.
+
✓ Branch 6 taken 772 times.
+
+
+
3112 return fs::is_regular_file(file) &&
183 +
+ 8/14 +
+
✓ Branch 1 taken 1282 times.
+
✗ Branch 2 not taken.
+
✓ Branch 5 taken 510 times.
+
✗ Branch 6 not taken.
+
✓ Branch 8 taken 72 times.
+
✓ Branch 9 taken 438 times.
+
✓ Branch 10 taken 510 times.
+
✓ Branch 11 taken 810 times.
+
✓ Branch 13 taken 1282 times.
+
✓ Branch 14 taken 38 times.
+
✗ Branch 16 not taken.
+
✗ Branch 17 not taken.
+
✗ Branch 19 not taken.
+
✗ Branch 20 not taken.
+
+
+
3112 ((file.extension() == PEM_EXTENSION) || (file.extension() == DER_EXTENSION));
184 + }
185 +
186 + private:
187 + /// @brief Adds to our certificate list the certificates found in the file
188 + /// @return number of added certificates
189 + void add_certificates(const std::string& data, const EncodingFormat encoding, const std::optional<fs::path>& path);
190 +
191 + /// @brief operation to be executed after each add/delete to this bundle
192 + void invalidate_hierarchy();
193 +
194 + private:
195 + // Structure of the bundle - maps files to the certificates stored in them
196 + // For certificates coming from a string, uses a default empty path
197 + std::map<fs::path, std::vector<X509Wrapper>> certificates;
198 + // Relevant bundle file or directory for the certificates
199 + fs::path path;
200 + // Source from where we created the certificates. If 'string' the 'export' functions will not work
201 + X509CertificateSource source;
202 +
203 + // Cached certificate hierarchy, invalidated on any operation
204 + X509CertificateHierarchy hierarchy;
205 + bool hierarchy_invalidated;
206 + };
207 +
208 + } // namespace evse_security
209 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_hierarchy.cpp.4aa6731438aa6ac9f1c10ceadfdd5908.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_hierarchy.cpp.4aa6731438aa6ac9f1c10ceadfdd5908.html new file mode 100644 index 00000000..a1fa40cb --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_hierarchy.cpp.4aa6731438aa6ac9f1c10ceadfdd5908.html @@ -0,0 +1,2968 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/certificate/x509_hierarchy.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:15016590.9%
Functions:202290.9%
Branches:11320056.5%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #include <evse_security/certificate/x509_hierarchy.hpp>
4 +
5 + #include <algorithm>
6 +
7 + namespace evse_security {
8 +
9 + bool X509CertificateHierarchy::is_internal_root(const X509Wrapper& certificate) const {
10 + if (certificate.is_selfsigned()) {
11 + return (std::find_if(hierarchy.begin(), hierarchy.end(), [&certificate](const X509Node& node) {
12 + return node.certificate == certificate;
13 + }) != hierarchy.end());
14 + }
15 +
16 + return false;
17 + }
18 +
19 + 16std::vector<X509Wrapper> X509CertificateHierarchy::collect_descendants(const X509Wrapper& top) {
20 + 16 std::vector<X509Wrapper> descendants;
21 +
22 +
+ 1/2 +
+
✓ Branch 1 taken 16 times.
+
✗ Branch 2 not taken.
+
+
+
16 for_each([&](const X509Node& node) {
23 + // If we found the certificate
24 +
+ 2/2 +
+
✓ Branch 1 taken 16 times.
+
✓ Branch 2 taken 18 times.
+
+
+
34 if (node.certificate == top) {
25 + // Collect all descendants
26 +
+ 2/2 +
+
✓ Branch 1 taken 8 times.
+
✓ Branch 2 taken 8 times.
+
+
+
16 if (node.children.size()) {
27 + 8 for_each_descendant(
28 + 34 [&](const X509Node& descendant, int depth) { descendants.push_back(descendant.certificate); },
29 + node);
30 + }
31 +
32 + 16 return false;
33 + }
34 +
35 + 18 return true;
36 + });
37 +
38 + 16 return descendants;
39 + }
40 +
41 + 68CertificateHashData X509CertificateHierarchy::get_certificate_hash(const X509Wrapper& certificate) {
42 +
+ 3/4 +
+
✓ Branch 1 taken 68 times.
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 44 times.
+
✓ Branch 4 taken 24 times.
+
+
+
68 if (certificate.is_selfsigned()) {
43 +
+ 1/2 +
+
✓ Branch 1 taken 44 times.
+
✗ Branch 2 not taken.
+
+
+
44 return certificate.get_certificate_hash_data();
44 + }
45 +
46 + // Search for certificate in the hierarchy and return the hash
47 + 24 CertificateHashData hash;
48 + bool found;
49 +
50 +
+ 1/2 +
+
✓ Branch 1 taken 24 times.
+
✗ Branch 2 not taken.
+
+
+
24 for_each([&](const X509Node& node) {
51 +
+ 2/2 +
+
✓ Branch 1 taken 24 times.
+
✓ Branch 2 taken 52 times.
+
+
+
76 if (node.certificate == certificate) {
52 + 24 hash = node.hash;
53 + 24 found = true;
54 +
55 + 24 return false;
56 + }
57 +
58 + 52 return true;
59 + });
60 +
61 +
+ 1/2 +
+
✓ Branch 0 taken 24 times.
+
✗ Branch 1 not taken.
+
+
+
24 if (found)
62 + 24 return hash;
63 +
64 + throw NoCertificateFound("Could not find owner for certificate: " + certificate.get_common_name());
65 + 24}
66 +
67 + 24bool X509CertificateHierarchy::contains_certificate_hash(const CertificateHashData& hash) {
68 + 24 bool contains = false;
69 +
70 +
+ 1/2 +
+
✓ Branch 1 taken 24 times.
+
✗ Branch 2 not taken.
+
+
+
24 for_each([&](const X509Node& node) {
71 +
+ 2/2 +
+
✓ Branch 1 taken 12 times.
+
✓ Branch 2 taken 34 times.
+
+
+
46 if (node.hash == hash) {
72 + 12 contains = true;
73 + 12 return false;
74 + }
75 +
76 + 34 return true;
77 + });
78 +
79 + 24 return contains;
80 + }
81 +
82 + 4X509Wrapper X509CertificateHierarchy::find_certificate_root(const X509Wrapper& leaf) {
83 + 4 const X509Wrapper* root_ptr = nullptr;
84 +
85 +
+ 2/2 +
+
✓ Branch 5 taken 8 times.
+
✓ Branch 6 taken 4 times.
+
+
+
12 for (const auto& root : hierarchy) {
86 +
+ 1/2 +
+
✓ Branch 0 taken 8 times.
+
✗ Branch 1 not taken.
+
+
+
8 if (root.state.is_selfsigned) {
87 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 for_each_descendant(
88 + 16 [&](const X509Node& node, int depth) {
89 + // If we found our matching certificate, we also found the root
90 +
+ 2/2 +
+
✓ Branch 1 taken 4 times.
+
✓ Branch 2 taken 12 times.
+
+
+
16 if (node.certificate == leaf) {
91 + 4 root_ptr = &root.certificate;
92 + }
93 + 16 },
94 + root, 1);
95 + }
96 + }
97 +
98 +
+ 1/2 +
+
✓ Branch 0 taken 4 times.
+
✗ Branch 1 not taken.
+
+
+
4 if (root_ptr)
99 +
+ 1/2 +
+
✓ Branch 1 taken 4 times.
+
✗ Branch 2 not taken.
+
+
+
8 return *root_ptr;
100 +
101 + throw NoCertificateFound("Could not find a certificate root for leaf: " + leaf.get_common_name());
102 + }
103 +
104 + 80X509Wrapper X509CertificateHierarchy::find_certificate(const CertificateHashData& hash,
105 + bool case_insensitive_comparison) {
106 + 80 X509Wrapper* certificate = nullptr;
107 +
108 +
+ 1/2 +
+
✓ Branch 1 taken 80 times.
+
✗ Branch 2 not taken.
+
+
+
80 for_each([&](X509Node& node) {
109 + 262 bool matches = false;
110 +
111 +
+ 2/2 +
+
✓ Branch 0 taken 202 times.
+
✓ Branch 1 taken 60 times.
+
+
+
262 if (case_insensitive_comparison) {
112 + 202 matches = (node.hash.case_insensitive_comparison(hash));
113 + } else {
114 + 60 matches = (node.hash == hash);
115 + }
116 +
117 +
+ 2/2 +
+
✓ Branch 0 taken 30 times.
+
✓ Branch 1 taken 232 times.
+
+
+
262 if (matches) {
118 + 30 certificate = &node.certificate;
119 + 30 return false;
120 + }
121 +
122 + 232 return true;
123 + });
124 +
125 +
+ 2/2 +
+
✓ Branch 0 taken 30 times.
+
✓ Branch 1 taken 50 times.
+
+
+
80 if (certificate)
126 +
+ 1/2 +
+
✓ Branch 1 taken 30 times.
+
✗ Branch 2 not taken.
+
+
+
60 return *certificate;
127 +
128 +
+ 2/4 +
+
✓ Branch 2 taken 50 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 50 times.
+
✗ Branch 6 not taken.
+
+
+
50 throw NoCertificateFound("Could not find a certificate for hash: " + hash.issuer_name_hash);
129 + }
130 +
131 + 12std::vector<X509Wrapper> X509CertificateHierarchy::find_certificates_multi(const CertificateHashData& hash) {
132 + 12 std::vector<X509Wrapper> certificates;
133 +
134 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 for_each([&](X509Node& node) {
135 +
+ 2/2 +
+
✓ Branch 1 taken 24 times.
+
✓ Branch 2 taken 60 times.
+
+
+
84 if (node.hash == hash) {
136 + 24 certificates.push_back(node.certificate);
137 + }
138 +
139 + 84 return true;
140 + });
141 +
142 + 12 return certificates;
143 + }
144 +
145 + 80std::string X509CertificateHierarchy::to_debug_string() {
146 +
+ 1/2 +
+
✓ Branch 1 taken 80 times.
+
✗ Branch 2 not taken.
+
+
+
80 std::stringstream str;
147 +
148 +
+ 2/2 +
+
✓ Branch 5 taken 106 times.
+
✓ Branch 6 taken 80 times.
+
+
+
186 for (const auto& root : hierarchy) {
149 +
+ 2/2 +
+
✓ Branch 0 taken 98 times.
+
✓ Branch 1 taken 8 times.
+
+
+
106 if (root.state.is_selfsigned)
150 +
+ 1/2 +
+
✓ Branch 1 taken 98 times.
+
✗ Branch 2 not taken.
+
+
+
98 str << "* [ROOT]";
151 + else
152 +
+ 1/2 +
+
✓ Branch 1 taken 8 times.
+
✗ Branch 2 not taken.
+
+
+
8 str << "+ [ORPH]";
153 +
154 +
+ 4/8 +
+
✓ Branch 1 taken 106 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 106 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 106 times.
+
✗ Branch 8 not taken.
+
✓ Branch 10 taken 106 times.
+
✗ Branch 11 not taken.
+
+
+
106 str << ' ' << root.certificate.get_common_name() << std::endl;
155 +
156 +
+ 1/2 +
+
✓ Branch 1 taken 106 times.
+
✗ Branch 2 not taken.
+
+
+
106 for_each_descendant(
157 + 216 [&](const X509Node& node, int depth) {
158 +
+ 2/2 +
+
✓ Branch 0 taken 376 times.
+
✓ Branch 1 taken 216 times.
+
+
+
592 while (depth-- > 0)
159 + 376 str << "---";
160 +
161 +
+ 3/6 +
+
✓ Branch 2 taken 216 times.
+
✗ Branch 3 not taken.
+
✓ Branch 5 taken 216 times.
+
✗ Branch 6 not taken.
+
✓ Branch 8 taken 216 times.
+
✗ Branch 9 not taken.
+
+
+
216 str << ' ' << node.certificate.get_common_name() << std::endl;
162 + 216 },
163 + root, 1);
164 + }
165 +
166 +
+ 1/2 +
+
✓ Branch 1 taken 80 times.
+
✗ Branch 2 not taken.
+
+
+
160 return str.str();
167 + 80}
168 +
169 + 1060void X509CertificateHierarchy::insert(X509Wrapper&& inserted_certificate) {
170 + // Invalid hash
171 +
+ 3/4 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 1058 times.
+
✓ Branch 3 taken 2 times.
+
✗ Branch 4 not taken.
+
+
+
1060 static CertificateHashData invalid_hash{HashAlgorithm::SHA256, {}, {}, {}};
172 +
173 +
+ 2/2 +
+
✓ Branch 1 taken 738 times.
+
✓ Branch 2 taken 322 times.
+
+
+
1060 if (false == inserted_certificate.is_selfsigned()) {
174 + // If this certif has any link to any of the existing certificates
175 + 738 bool hierarchy_found = false;
176 +
177 + // Create a new node, is not self-signed and is not a permanent orphan
178 +
+ 3/10 +
+
✓ Branch 1 taken 738 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 738 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 738 times.
+
✗ Branch 8 not taken.
+
✗ Branch 10 not taken.
+
✗ Branch 11 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 14 not taken.
+
+
+
738 X509Node new_node = {{0, 0, 0}, inserted_certificate, invalid_hash, inserted_certificate, {}};
179 +
180 + // Search through all the list for a link
181 +
+ 1/2 +
+
✓ Branch 1 taken 738 times.
+
✗ Branch 2 not taken.
+
+
+
738 for_each([&](X509Node& top) {
182 +
+ 2/2 +
+
✓ Branch 1 taken 146 times.
+
✓ Branch 2 taken 1000 times.
+
+
+
1146 if (top.certificate.is_child(inserted_certificate)) {
183 + // Some sanity checks
184 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 146 times.
+
+
+
146 if (top.state.is_selfsigned) {
185 + throw InvalidStateException(
186 + "Newly added certificate can't be parent of a self-signed certificate!");
187 + }
188 +
189 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 146 times.
+
+
+
146 if (top.state.is_hash_computed) {
190 + throw InvalidStateException("Existing non-root top certificate can't have a valid hash!");
191 + }
192 +
193 + // If the top certificate is a descendant of the certificate we're adding
194 +
195 + // Cache top node
196 + 146 auto temp_top = std::move(top);
197 +
198 + // Set the new state of the top node
199 + 146 temp_top.state = {0, 0, 1};
200 +
+ 1/2 +
+
✓ Branch 1 taken 146 times.
+
✗ Branch 2 not taken.
+
+
+
146 temp_top.hash = temp_top.certificate.get_certificate_hash_data(new_node.certificate);
201 +
+ 1/2 +
+
✓ Branch 1 taken 146 times.
+
✗ Branch 2 not taken.
+
+
+
146 temp_top.issuer = X509Wrapper(new_node.certificate);
202 +
203 + // Set the top as a child of the new_node
204 +
+ 1/2 +
+
✓ Branch 2 taken 146 times.
+
✗ Branch 3 not taken.
+
+
+
146 new_node.children.push_back(std::move(temp_top));
205 +
206 + // Set the new top
207 + 146 top = std::move(new_node);
208 + 1704 hierarchy_found = true; // Found a link
209 +
+ 2/2 +
+
✓ Branch 2 taken 412 times.
+
✓ Branch 3 taken 588 times.
+
+
+
1146 } else if (inserted_certificate.is_child(top.certificate)) {
210 + // If the certificate is the descendant of top certificate
211 +
212 + // Calculate hash and set issuer
213 + 412 new_node.state = {0, 0, 1};
214 +
+ 1/2 +
+
✓ Branch 1 taken 412 times.
+
✗ Branch 2 not taken.
+
+
+
412 new_node.hash = inserted_certificate.get_certificate_hash_data(top.certificate);
215 +
+ 1/2 +
+
✓ Branch 1 taken 412 times.
+
✗ Branch 2 not taken.
+
+
+
412 new_node.issuer = X509Wrapper(top.certificate); // Set the new issuer
216 +
217 + // Add it to the top's descendant list
218 + 412 top.children.push_back(new_node);
219 + 412 hierarchy_found = true; // Found a link
220 + }
221 +
222 + // Keep iterating while we did not find a link
223 + 1146 return (false == hierarchy_found);
224 + });
225 +
226 + // Else insert it in the roots as a potentially orphan certificate
227 +
+ 2/2 +
+
✓ Branch 0 taken 180 times.
+
✓ Branch 1 taken 558 times.
+
+
+
738 if (hierarchy_found == false) {
228 +
+ 1/2 +
+
✓ Branch 1 taken 180 times.
+
✗ Branch 2 not taken.
+
+
+
180 hierarchy.push_back(new_node);
229 + }
230 + 738 } else {
231 + // If it is self-signed insert it in the roots, with the state set as a self-signed and a properly computed hash
232 + 322 hierarchy.push_back({{1, 0, 1},
233 + inserted_certificate,
234 + inserted_certificate.get_certificate_hash_data(),
235 + inserted_certificate,
236 + {}});
237 +
238 + // Attempt a partial prune, by searching through all the contained temporary orphan certificates
239 + // and trying to add them to the newly inserted root certificate, if that is possible
240 +
241 + // Only iterate until last (not including) since the last is the new node
242 +
+ 2/2 +
+
✓ Branch 1 taken 328 times.
+
✓ Branch 2 taken 322 times.
+
+
+
650 for (int i = 0; i < (hierarchy.size() - 1); ++i) {
243 + 328 auto& node = hierarchy[i];
244 + 328 auto& state = node.state;
245 +
246 + // If we have a temporary orphan, that is if we are in the roots
247 + // and we are not self-signed then it means that we are an orphan
248 +
+ 2/2 +
+
✓ Branch 0 taken 154 times.
+
✓ Branch 1 taken 174 times.
+
+
+
328 if (state.is_selfsigned == 0) {
249 + // Some sanity checks
250 +
+ 1/2 +
+
✗ Branch 0 not taken.
+
✓ Branch 1 taken 154 times.
+
+
+
154 if (state.is_hash_computed)
251 + throw InvalidStateException("Orphan certificate can't have a proper hash!");
252 +
253 + // If it is a child of the new root certificate insert it to it's list and break
254 +
+ 2/2 +
+
✓ Branch 1 taken 142 times.
+
✓ Branch 2 taken 12 times.
+
+
+
154 if (node.certificate.is_child(inserted_certificate)) {
255 + 142 auto& new_root = hierarchy.back();
256 +
257 + // Hash is properly computed now
258 +
+ 1/2 +
+
✓ Branch 1 taken 142 times.
+
✗ Branch 2 not taken.
+
+
+
142 node.hash = node.certificate.get_certificate_hash_data(new_root.certificate);
259 + 142 node.state.is_hash_computed = 1;
260 + 142 node.state.is_orphan = 0; // Not an orphan any more
261 +
+ 1/2 +
+
✓ Branch 1 taken 142 times.
+
✗ Branch 2 not taken.
+
+
+
142 node.issuer = X509Wrapper(inserted_certificate); // Set the new valid issuer
262 +
263 + // Add to the newly inserted root child list
264 + 142 new_root.children.push_back(std::move(node));
265 +
266 + // Erase element since it was added to the root's descendants
267 +
+ 1/2 +
+
✓ Branch 4 taken 142 times.
+
✗ Branch 5 not taken.
+
+
+
142 hierarchy.erase(hierarchy.begin() + i);
268 + // Decrement i again since it was erased
269 + 142 i--;
270 + }
271 + }
272 + }
273 + }
274 +
+ 4/14 +
+
✓ Branch 1 taken 322 times.
+
✗ Branch 2 not taken.
+
✓ Branch 4 taken 322 times.
+
✗ Branch 5 not taken.
+
✓ Branch 7 taken 322 times.
+
✗ Branch 8 not taken.
+
✓ Branch 11 taken 322 times.
+
✗ Branch 12 not taken.
+
✗ Branch 13 not taken.
+
✗ Branch 14 not taken.
+
✗ Branch 16 not taken.
+
✗ Branch 17 not taken.
+
✗ Branch 19 not taken.
+
✗ Branch 20 not taken.
+
+
+
1382} // End insert
275 +
276 + 258void X509CertificateHierarchy::prune() {
277 +
+ 2/2 +
+
✓ Branch 1 taken 156 times.
+
✓ Branch 2 taken 102 times.
+
+
+
258 if (hierarchy.size() <= 1)
278 + 156 return;
279 +
280 +
+ 2/2 +
+
✓ Branch 1 taken 226 times.
+
✓ Branch 2 taken 102 times.
+
+
+
328 for (int i = 0; i < hierarchy.size(); ++i) {
281 + // Possible orphan
282 + 226 auto& orphan = hierarchy[i];
283 +
284 +
+ 3/4 +
+
✓ Branch 0 taken 12 times.
+
✓ Branch 1 taken 214 times.
+
✓ Branch 2 taken 12 times.
+
✗ Branch 3 not taken.
+
+
+
226 bool is_orphan = (orphan.state.is_selfsigned) == 0 && (orphan.state.is_orphan == 0);
285 +
+ 2/2 +
+
✓ Branch 0 taken 214 times.
+
✓ Branch 1 taken 12 times.
+
+
+
226 if (is_orphan == false)
286 + 214 continue;
287 +
288 + // Found a non-permanent orphan, search for a issuer
289 + 12 bool found_issuer = false;
290 +
291 +
+ 1/2 +
+
✓ Branch 1 taken 12 times.
+
✗ Branch 2 not taken.
+
+
+
12 for_each([&](X509Node& top) {
292 +
+ 2/2 +
+
✓ Branch 1 taken 10 times.
+
✓ Branch 2 taken 40 times.
+
+
+
50 if (orphan.certificate.is_child(top.certificate)) {
293 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 orphan.hash = orphan.certificate.get_certificate_hash_data(top.certificate);
294 + 10 orphan.state.is_hash_computed = 1;
295 + 10 orphan.state.is_orphan = 0; // Not an orphan any more
296 +
+ 1/2 +
+
✓ Branch 1 taken 10 times.
+
✗ Branch 2 not taken.
+
+
+
10 orphan.issuer = X509Wrapper(top.certificate); // Set the new valid issuer
297 +
298 + 10 top.children.push_back(std::move(orphan));
299 + 10 found_issuer = true;
300 + }
301 +
302 + 50 return (false == found_issuer);
303 + });
304 +
305 +
+ 2/2 +
+
✓ Branch 0 taken 2 times.
+
✓ Branch 1 taken 10 times.
+
+
+
12 if (false == found_issuer) {
306 + // Mark as permanent orphan
307 + 2 orphan.state.is_orphan = 1; // Permanent orphan
308 + 2 orphan.state.is_hash_computed = 0;
309 + } else {
310 + // Erase from hierarchy list and decrement iterator
311 +
+ 1/2 +
+
✓ Branch 4 taken 10 times.
+
✗ Branch 5 not taken.
+
+
+
10 hierarchy.erase(std::begin(hierarchy) + i);
312 + 10 i--;
313 + }
314 + }
315 + }
316 +
317 + 126X509CertificateHierarchy X509CertificateHierarchy::build_hierarchy(std::vector<X509Wrapper>& certificates) {
318 + 126 X509CertificateHierarchy ordered;
319 +
320 +
+ 2/2 +
+
✓ Branch 1 taken 350 times.
+
✓ Branch 2 taken 126 times.
+
+
+
476 while (certificates.size()) {
321 +
+ 1/2 +
+
✓ Branch 3 taken 350 times.
+
✗ Branch 4 not taken.
+
+
+
350 ordered.insert(std::move(certificates.back()));
322 + 350 certificates.pop_back();
323 + }
324 +
325 + // Prune the tree
326 +
+ 1/2 +
+
✓ Branch 1 taken 126 times.
+
✗ Branch 2 not taken.
+
+
+
126 ordered.prune();
327 +
328 + 126 return ordered;
329 + }
330 +
331 + } // namespace evse_security
332 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_hierarchy.hpp.74d824b022d679ccf3e0efb14b6fa13c.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_hierarchy.hpp.74d824b022d679ccf3e0efb14b6fa13c.html new file mode 100644 index 00000000..47f71d31 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_hierarchy.hpp.74d824b022d679ccf3e0efb14b6fa13c.html @@ -0,0 +1,1282 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:include/evse_security/certificate/x509_hierarchy.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:303196.8%
Functions:1313100.0%
Branches:324080.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #pragma once
4 +
5 + #include <algorithm>
6 + #include <queue>
7 +
8 + #include <evse_security/certificate/x509_wrapper.hpp>
9 +
10 + namespace evse_security {
11 +
12 + class NoCertificateFound : public std::runtime_error {
13 + public:
14 + using std::runtime_error::runtime_error;
15 + };
16 +
17 + class InvalidStateException : public std::runtime_error {
18 + public:
19 + using std::runtime_error::runtime_error;
20 + };
21 +
22 + struct NodeState {
23 + std::uint32_t is_selfsigned : 1;
24 + std::uint32_t is_orphan : 1; // 0 means temporary orphan, 1 is permanent orphan, no relevance if it is self-signed
25 + std::uint32_t is_hash_computed : 1; // if the hash was correctly computed or if a placeholder hash is set
26 + };
27 +
28 + struct X509Node {
29 + NodeState state;
30 +
31 + X509Wrapper certificate;
32 + CertificateHashData hash;
33 +
34 + X509Wrapper issuer;
35 + std::vector<X509Node> children;
36 + };
37 +
38 + /// @brief Utility class that is able to build a immutable certificate hierarchy
39 + /// with a list of self-signed root certificates and their respective sub-certificates
40 + /// Note: non self-signed roots and cross-signed certificates are not supported now
41 + class X509CertificateHierarchy {
42 + public:
43 + 68 const std::vector<X509Node>& get_hierarchy() const {
44 + 68 return hierarchy;
45 + }
46 +
47 + /// @brief Checks if the provided certificate is a self-signed root CA certificate
48 + /// contained in our hierarchy
49 + bool is_internal_root(const X509Wrapper& certificate) const;
50 +
51 + /// @brief Collects all the descendants of the provided certificate
52 + /// @param top Certificate that issued the descendants
53 + std::vector<X509Wrapper> collect_descendants(const X509Wrapper& top);
54 +
55 + /// @brief obtains the hash data of the certificate, finding its issuer if needed
56 + CertificateHashData get_certificate_hash(const X509Wrapper& certificate);
57 +
58 + /// @brief returns true if we contain a certificate with the following hash
59 + bool contains_certificate_hash(const CertificateHashData& hash);
60 +
61 + /// @brief Searches for the root of the provided leaf, throwing a NoCertificateFound if not found
62 + X509Wrapper find_certificate_root(const X509Wrapper& leaf);
63 +
64 + /// @brief Searches for the provided hash, throwing a NoCertificateFound if not found
65 + X509Wrapper find_certificate(const CertificateHashData& hash, bool case_insensitive_comparison = false);
66 +
67 + /// @brief Searches for all the certificates with the provided hash, throwing a NoCertificateFound
68 + // if none were found. Can be useful when we have SUB-CAs in multiple bundles
69 + std::vector<X509Wrapper> find_certificates_multi(const CertificateHashData& hash);
70 +
71 + public:
72 + std::string to_debug_string();
73 +
74 + /// @brief Breadth-first iteration through all the hierarchy of
75 + /// certificates. Will break when the function returns false
76 + 1812 template <typename function> void for_each(function func) {
77 +
+ 1/2 +
+
✓ Branch 1 taken 906 times.
+
✗ Branch 2 not taken.
+
+
+
1812 std::queue<std::reference_wrapper<X509Node>> queue;
78 +
+ 2/2 +
+
✓ Branch 5 taken 868 times.
+
✓ Branch 6 taken 600 times.
+
+
+
2936 for (auto& root : hierarchy) {
79 + // Process roots
80 +
+ 4/4 +
+
✓ Branch 1 taken 844 times.
+
✓ Branch 2 taken 24 times.
+
✓ Branch 3 taken 306 times.
+
✓ Branch 4 taken 538 times.
+
+
+
1736 if (!func(root))
81 + 612 return;
82 +
83 +
+ 2/2 +
+
✓ Branch 4 taken 592 times.
+
✓ Branch 5 taken 562 times.
+
+
+
2308 for (auto& child : root.children) {
84 +
+ 1/2 +
+
✓ Branch 2 taken 592 times.
+
✗ Branch 3 not taken.
+
+
+
1184 queue.push(child);
85 + }
86 + }
87 +
88 +
+ 2/2 +
+
✓ Branch 1 taken 830 times.
+
✓ Branch 2 taken 256 times.
+
+
+
2172 while (!queue.empty()) {
89 + 1660 X509Node& top = queue.front();
90 + 1660 queue.pop();
91 +
92 + // Process node
93 +
+ 4/4 +
+
✓ Branch 1 taken 820 times.
+
✓ Branch 2 taken 10 times.
+
✓ Branch 3 taken 332 times.
+
✓ Branch 4 taken 476 times.
+
+
+
1660 if (!func(top))
94 + 688 return;
95 +
96 +
+ 2/2 +
+
✓ Branch 4 taken 388 times.
+
✓ Branch 5 taken 486 times.
+
+
+
1748 for (auto& child : top.children) {
97 +
+ 1/2 +
+
✓ Branch 2 taken 388 times.
+
✗ Branch 3 not taken.
+
+
+
776 queue.push(child);
98 + }
99 + }
100 +
+ 2/2 +
+
✓ Branch 1 taken 256 times.
+
✓ Branch 2 taken 650 times.
+
+
+
1812 }
101 +
102 + public:
103 + /// @brief Depth-first descendant iteration
104 + 462 template <typename function> static void for_each_descendant(function func, const X509Node& node, int depth = 0) {
105 +
+ 2/2 +
+
✓ Branch 1 taken 12 times.
+
✓ Branch 2 taken 100 times.
+
+
+
462 if (node.children.empty())
106 + 44 return;
107 +
108 +
+ 2/2 +
+
✓ Branch 5 taken 100 times.
+
✓ Branch 6 taken 100 times.
+
+
+
876 for (const auto& child : node.children) {
109 +
+ 1/2 +
+
✓ Branch 1 taken 100 times.
+
✗ Branch 2 not taken.
+
+
+
458 func(child, depth);
110 +
111 +
+ 2/2 +
+
✓ Branch 1 taken 50 times.
+
✓ Branch 2 taken 50 times.
+
+
+
458 if (!child.children.empty()) {
112 +
+ 1/2 +
+
✓ Branch 1 taken 50 times.
+
✗ Branch 2 not taken.
+
+
+
216 for_each_descendant(func, child, (depth + 1));
113 + }
114 + }
115 + }
116 +
117 + public:
118 + /// @brief Builds a proper certificate hierarchy from the provided certificates. The
119 + /// hierarchy can be incomplete, in case orphan certificates are present in the list
120 + static X509CertificateHierarchy build_hierarchy(std::vector<X509Wrapper>& certificates);
121 +
122 + 132 template <typename... Args> static X509CertificateHierarchy build_hierarchy(Args... certificates) {
123 + 132 X509CertificateHierarchy ordered;
124 +
125 +
+ 2/4 +
+
✓ Branch 3 taken 132 times.
+
✗ Branch 4 not taken.
+
✓ Branch 8 taken 132 times.
+
✗ Branch 9 not taken.
+
+
+
132 (std::for_each(certificates.begin(), certificates.end(),
126 + 1420 [&ordered](X509Wrapper& cert) { ordered.insert(std::move(cert)); }),
127 + ...); // Fold expr
128 +
129 + // Prune the tree
130 +
+ 1/2 +
+
✓ Branch 1 taken 132 times.
+
✗ Branch 2 not taken.
+
+
+
132 ordered.prune();
131 +
132 + 132 return ordered;
133 + }
134 +
135 + private:
136 + /// @brief Inserts the certificate in the hierarchy. If it is not a root
137 + /// and a parent is not found, it will be inserted as a temporary orphan
138 + void insert(X509Wrapper&& certificate);
139 +
140 + /// @brief After inserting all certificates in the hierarchy, attempts
141 + /// to parent all temporarly orphan certificates, marking the ones that
142 + /// were not successfully parented as permanently orphan
143 + void prune();
144 +
145 + private:
146 + std::vector<X509Node> hierarchy;
147 + };
148 +
149 + } // namespace evse_security
150 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_wrapper.cpp.8c06c3ac7e058b22ffb64e05dbe82228.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_wrapper.cpp.8c06c3ac7e058b22ffb64e05dbe82228.html new file mode 100644 index 00000000..fd3f8492 --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_wrapper.cpp.8c06c3ac7e058b22ffb64e05dbe82228.html @@ -0,0 +1,1671 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:lib/evse_security/certificate/x509_wrapper.cpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:7710871.3%
Functions:232688.5%
Branches:3311828.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #include <evse_security/certificate/x509_wrapper.hpp>
4 +
5 + #include <cctype>
6 + #include <fstream>
7 + #include <iostream>
8 + #include <regex>
9 +
10 + #include <everest/logging.hpp>
11 + #include <evse_security/crypto/evse_crypto.hpp>
12 + #include <evse_security/utils/evse_filesystem.hpp>
13 +
14 + namespace evse_security {
15 +
16 + X509Wrapper::X509Wrapper(const fs::path& file, const EncodingFormat encoding) {
17 + if (fs::is_regular_file(file) == false) {
18 + throw CertificateLoadException("X509Wrapper can only load from files!");
19 + }
20 +
21 + fsstd::ifstream read(file, std::ios::binary);
22 + std::string certificate((std::istreambuf_iterator<char>(read)), std::istreambuf_iterator<char>());
23 +
24 + auto loaded = CryptoSupplier::load_certificates(certificate, encoding);
25 + if (loaded.size() != 1) {
26 + std::string error = "X509Wrapper can only load a single certificate! Loaded: ";
27 + error += std::to_string(loaded.size());
28 +
29 + throw CertificateLoadException(error);
30 + }
31 +
32 + this->file = file;
33 + x509 = std::move(loaded[0]);
34 + update_validity();
35 + }
36 +
37 + 72X509Wrapper::X509Wrapper(const std::string& data, const EncodingFormat encoding) {
38 +
+ 2/2 +
+
✓ Branch 1 taken 70 times.
+
✓ Branch 2 taken 2 times.
+
+
+
72 auto loaded = CryptoSupplier::load_certificates(data, encoding);
39 +
+ 1/2 +
+
✗ Branch 1 not taken.
+
✓ Branch 2 taken 70 times.
+
+
+
70 if (loaded.size() != 1) {
40 + std::string error = "X509Wrapper can only load a single certificate! Loaded: ";
41 + error += std::to_string(loaded.size());
42 +
43 + throw CertificateLoadException(error);
44 + }
45 +
46 + 70 x509 = std::move(loaded[0]);
47 +
+ 1/2 +
+
✓ Branch 1 taken 70 times.
+
✗ Branch 2 not taken.
+
+
+
70 update_validity();
48 + 74}
49 +
50 + 32X509Wrapper::X509Wrapper(X509Handle_ptr&& x509) : x509(std::move(x509)) {
51 +
+ 1/2 +
+
✓ Branch 1 taken 32 times.
+
✗ Branch 2 not taken.
+
+
+
32 update_validity();
52 + 32}
53 +
54 +
+ 1/2 +
+
✓ Branch 3 taken 1530 times.
+
✗ Branch 4 not taken.
+
+
+
1530X509Wrapper::X509Wrapper(X509Handle_ptr&& x509, const fs::path& file) : x509(std::move(x509)), file(file) {
55 +
+ 2/4 +
+
✓ Branch 1 taken 1530 times.
+
✗ Branch 2 not taken.
+
✗ Branch 3 not taken.
+
✓ Branch 4 taken 1530 times.
+
+
+
1530 if (fs::is_regular_file(file) == false) {
56 + throw CertificateLoadException("X509Wrapper can only load from files!");
57 + }
58 +
59 +
+ 1/2 +
+
✓ Branch 1 taken 1530 times.
+
✗ Branch 2 not taken.
+
+
+
1530 update_validity();
60 + 1530}
61 +
62 + 5488X509Wrapper::X509Wrapper(const X509Wrapper& other) :
63 +
+ 1/2 +
+
✓ Branch 2 taken 5488 times.
+
✗ Branch 3 not taken.
+
+
+
5488 x509(std::move(CryptoSupplier::x509_duplicate_unique(other.get()))),
64 + 5488 file(other.file),
65 + 5488 valid_in(other.valid_in),
66 +
+ 1/2 +
+
✓ Branch 1 taken 5488 times.
+
✗ Branch 2 not taken.
+
+
+
5488 valid_to(other.valid_to) {
67 + 5488}
68 +
69 + 11844X509Wrapper::~X509Wrapper() {
70 + 11844}
71 +
72 + 396bool X509Wrapper::operator==(const X509Wrapper& other) const {
73 +
+ 2/2 +
+
✓ Branch 0 taken 4 times.
+
✓ Branch 1 taken 392 times.
+
+
+
396 if (this == &other)
74 + 4 return true;
75 +
76 + 392 return CryptoSupplier::x509_is_equal(get(), other.get());
77 + }
78 +
79 + 1632void X509Wrapper::update_validity() {
80 +
+ 1/2 +
+
✗ Branch 2 not taken.
+
✓ Branch 3 taken 1632 times.
+
+
+
1632 if (false == CryptoSupplier::x509_get_validity(get(), valid_in, valid_to)) {
81 + EVLOG_error << "Could not update validity for certificate: " << get_common_name();
82 + }
83 + 1632}
84 +
85 + 2350bool X509Wrapper::is_child(const X509Wrapper& parent) const {
86 + // A certif can't be it's own parent, use is_selfsigned if that is intended
87 +
+ 2/2 +
+
✓ Branch 0 taken 12 times.
+
✓ Branch 1 taken 2338 times.
+
+
+
2350 if (this == &parent)
88 + 12 return false;
89 +
90 + 2338 return CryptoSupplier::x509_is_child(get(), parent.get());
91 + }
92 +
93 + 1526bool X509Wrapper::is_selfsigned() const {
94 + 1526 return CryptoSupplier::x509_is_selfsigned(get());
95 + }
96 +
97 + 72int64_t X509Wrapper::get_valid_in() const {
98 + 72 return valid_in;
99 + }
100 +
101 + /// \brief Gets valid_in
102 + 1486int64_t X509Wrapper::get_valid_to() const {
103 + 1486 return valid_to;
104 + }
105 +
106 + 72bool X509Wrapper::is_valid() const {
107 + // The valid_in must be in the past and the valid_to must be in the future
108 +
+ 4/4 +
+
✓ Branch 1 taken 70 times.
+
✓ Branch 2 taken 2 times.
+
✓ Branch 4 taken 68 times.
+
✓ Branch 5 taken 2 times.
+
+
+
72 return (get_valid_in() <= 0) && (get_valid_to() >= 0);
109 + }
110 +
111 + 44bool X509Wrapper::is_expired() const {
112 + 44 return (get_valid_to() < 0);
113 + }
114 +
115 + 488std::optional<fs::path> X509Wrapper::get_file() const {
116 + 488 return this->file;
117 + }
118 +
119 + void X509Wrapper::set_file(fs::path& path) {
120 + if (fs::is_directory(path))
121 + throw std::logic_error("update_file must only be used for files, not directories!");
122 +
123 + file = path;
124 + }
125 +
126 + X509CertificateSource X509Wrapper::get_source() const {
127 + if (file.has_value()) {
128 + return X509CertificateSource::FILE;
129 + } else {
130 + return X509CertificateSource::STRING;
131 + }
132 + }
133 +
134 + 324std::string X509Wrapper::get_common_name() const {
135 + 324 return CryptoSupplier::x509_get_common_name(get());
136 + }
137 +
138 + 1106std::string X509Wrapper::get_issuer_name_hash() const {
139 + 1106 return CryptoSupplier::x509_get_issuer_name_hash(get());
140 + }
141 +
142 + 1104std::string X509Wrapper::get_serial_number() const {
143 + 1104 return CryptoSupplier::x509_get_serial_number(get());
144 + }
145 +
146 + 380std::string X509Wrapper::get_issuer_key_hash() const {
147 +
+ 2/2 +
+
✓ Branch 1 taken 378 times.
+
✓ Branch 2 taken 2 times.
+
+
+
380 if (is_selfsigned()) {
148 + 378 return get_key_hash();
149 + } else {
150 + // See 'OCPP 2.0.1 Spec: 2.6. CertificateHashDataType'
151 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 throw std::logic_error("get_issuer_key_hash must only be used on self-signed certs");
152 + }
153 + }
154 +
155 + 1104std::string X509Wrapper::get_key_hash() const {
156 + 1104 return CryptoSupplier::x509_get_key_hash(get());
157 + }
158 +
159 + 380CertificateHashData X509Wrapper::get_certificate_hash_data() const {
160 + 380 CertificateHashData certificate_hash_data;
161 + 380 certificate_hash_data.hash_algorithm = HashAlgorithm::SHA256;
162 +
+ 1/2 +
+
✓ Branch 1 taken 380 times.
+
✗ Branch 2 not taken.
+
+
+
380 certificate_hash_data.issuer_name_hash = this->get_issuer_name_hash();
163 +
+ 2/2 +
+
✓ Branch 1 taken 378 times.
+
✓ Branch 2 taken 2 times.
+
+
+
380 certificate_hash_data.issuer_key_hash = this->get_issuer_key_hash();
164 +
+ 1/2 +
+
✓ Branch 1 taken 378 times.
+
✗ Branch 2 not taken.
+
+
+
378 certificate_hash_data.serial_number = this->get_serial_number();
165 + 378 return certificate_hash_data;
166 + 2}
167 +
168 + 728CertificateHashData X509Wrapper::get_certificate_hash_data(const X509Wrapper& issuer) const {
169 +
+ 2/2 +
+
✓ Branch 3 taken 2 times.
+
✓ Branch 4 taken 726 times.
+
+
+
728 if (CryptoSupplier::x509_is_child(get(), issuer.get()) == false) {
170 +
+ 1/2 +
+
✓ Branch 2 taken 2 times.
+
✗ Branch 3 not taken.
+
+
+
2 throw std::logic_error("The specified issuer is not the correct issuer for this certificate.");
171 + }
172 +
173 + 726 CertificateHashData certificate_hash_data;
174 + 726 certificate_hash_data.hash_algorithm = HashAlgorithm::SHA256;
175 +
+ 1/2 +
+
✓ Branch 1 taken 726 times.
+
✗ Branch 2 not taken.
+
+
+
726 certificate_hash_data.issuer_name_hash = this->get_issuer_name_hash();
176 +
177 + // OCPP 2.0.1 Spec: 2.6. CertificateHashDataType
178 + // issuerKeyHash: The hash of the DER encoded public key: the
179 + // value (excluding tag and length) of the subject public key
180 + // field in the issuer’s certificate.
181 +
182 + // Issuer key hash
183 +
+ 1/2 +
+
✓ Branch 1 taken 726 times.
+
✗ Branch 2 not taken.
+
+
+
726 certificate_hash_data.issuer_key_hash = issuer.get_key_hash();
184 +
+ 1/2 +
+
✓ Branch 1 taken 726 times.
+
✗ Branch 2 not taken.
+
+
+
726 certificate_hash_data.serial_number = this->get_serial_number();
185 +
186 + 726 return certificate_hash_data;
187 + }
188 +
189 + 20std::string X509Wrapper::get_responder_url() const {
190 + 20 return CryptoSupplier::x509_get_responder_url(get());
191 + }
192 +
193 + 170std::string X509Wrapper::get_export_string() const {
194 + 170 return CryptoSupplier::x509_to_string(get());
195 + }
196 +
197 + } // namespace evse_security
198 +
+
+
+
+ + + diff --git a/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_wrapper.hpp.a21b7a94671767c27929e897e8430763.html b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_wrapper.hpp.a21b7a94671767c27929e897e8430763.html new file mode 100644 index 00000000..903de80d --- /dev/null +++ b/docs/ci-artifacts/EVerest/libevse-security/12396234184/coverage-report/index.x509_wrapper.hpp.a21b7a94671767c27929e897e8430763.html @@ -0,0 +1,1078 @@ + + + + + + GCC Code Coverage Report + + + + + + +
+

GCC Code Coverage Report

+
+
+
+ + + + + + + + + + + + + +
Directory:./
File:include/evse_security/certificate/x509_wrapper.hpp
Date:2024-12-18 15:37:36
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExecTotalCoverage
Lines:4757.1%
Functions:3475.0%
Branches:0240.0%
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineBranchExecSource
1 + // SPDX-License-Identifier: Apache-2.0
2 + // Copyright Pionix GmbH and Contributors to EVerest
3 + #pragma once
4 +
5 + #include <memory>
6 + #include <stdexcept>
7 + #include <string>
8 +
9 + #include <evse_security/crypto/interface/crypto_types.hpp>
10 + #include <evse_security/evse_types.hpp>
11 + #include <evse_security/utils/evse_filesystem_types.hpp>
12 +
13 + namespace evse_security {
14 +
15 + enum class X509CertificateSource {
16 + // Built from a certificate file
17 + FILE,
18 + // Built from a directory of certificates
19 + DIRECTORY,
20 + // Build from a raw string
21 + STRING
22 + };
23 +
24 + /// @brief Convenience wrapper around openssl X509 certificate
25 + class X509Wrapper {
26 + public:
27 + X509Wrapper(const fs::path& file, const EncodingFormat encoding);
28 + X509Wrapper(const std::string& data, const EncodingFormat encoding);
29 +
30 + explicit X509Wrapper(X509Handle_ptr&& x509);
31 + X509Wrapper(X509Handle_ptr&& x509, const fs::path& file);
32 +
33 + X509Wrapper(const X509Wrapper& other);
34 + 4724 X509Wrapper(X509Wrapper&& other) = default;
35 +
36 + ~X509Wrapper();
37 +
38 + public:
39 + /// @brief Returns true if this certificate is the child of the provided parent
40 + bool is_child(const X509Wrapper& parent) const;
41 +
42 + /// @brief Returns true if this certificate is self-signed
43 + bool is_selfsigned() const;
44 +
45 + public:
46 + /// @brief Gets x509 raw handle
47 + 19614 inline X509Handle* get() const {
48 + 19614 return x509.get();
49 + }
50 +
51 + /// @brief Gets valid_in
52 + /// @return seconds until certificate is valid; if > 0 cert is not yet valid
53 + int64_t get_valid_in() const;
54 +
55 + /// @brief Gets valid_in
56 + /// @return seconds until certificate is expired; if < 0 cert has expired
57 + int64_t get_valid_to() const;
58 +
59 + /// @brief Gets optional file of certificate
60 + /// @result
61 + std::optional<fs::path> get_file() const;
62 +
63 + void set_file(fs::path& path);
64 +
65 + /// @brief Gets the source of this certificate, if it is from a file it's 'FILE'
66 + /// but it can also be constructed from a string, or another certificate
67 + X509CertificateSource get_source() const;
68 +
69 + /// @brief Gets CN of certificate
70 + /// @result
71 + std::string get_common_name() const;
72 +
73 + /// @brief Gets issuer name hash of certificate
74 + /// @result
75 + std::string get_issuer_name_hash() const;
76 +
77 + /// @brief Gets issuer key hash of certificate. As per the specification
78 + /// this is not the hash of our public key, but the hash of the pubkey of
79 + /// the parent certificate. If it is a self-signed root, then the key hash
80 + /// and the issuer key hash are the same
81 + /// @result
82 + std::string get_issuer_key_hash() const;
83 +
84 + /// @brief Gets key hash of this certificate
85 + /// @result
86 + std::string get_key_hash() const;
87 +
88 + /// @brief Gets serial number of certificate
89 + /// @result
90 + std::string get_serial_number() const;
91 +
92 + /// @brief Gets certificate hash data of a self-signed certificate
93 + /// @return
94 + CertificateHashData get_certificate_hash_data() const;
95 +
96 + /// @brief Gets certificate hash data of certificate with an issuer
97 + /// @return
98 + CertificateHashData get_certificate_hash_data(const X509Wrapper& issuer) const;
99 +
100 + /// @brief Gets OCSP responder URL of certificate if present, else returns an empty string
101 + /// @return
102 + std::string get_responder_url() const;
103 +
104 + /// @brief Gets the export string representation for this certificate
105 + /// @return
106 + std::string get_export_string() const;
107 +
108 + /// @brief If the certificate is within the validity date. Can return false in 2 cases,
109 + /// if it is expired (current date > valid_to) or if (current data < valid_in), that is
110 + /// we are not in force yet
111 + bool is_valid() const;
112 +
113 + /// @brief If the certificate has expired
114 + bool is_expired() const;
115 +
116 + public:
117 + 1510 X509Wrapper& operator=(X509Wrapper&& other) = default;
118 +
119 + /// @return true if the two certificates are the same
120 + bool operator==(const X509Wrapper& other) const;
121 +
122 + bool operator==(const CertificateHashData& other) const {
123 + return get_issuer_name_hash() == other.issuer_name_hash && get_issuer_key_hash() == other.issuer_key_hash &&
124 + get_serial_number() == other.serial_number;
125 + }
126 +
127 + private:
128 + void update_validity();
129 +
130 + private:
131 + X509Handle_ptr x509; // X509 wrapper object
132 + std::int64_t valid_in; // seconds; if > 0 cert is not yet valid, negative value means past, positive is in future
133 + std::int64_t valid_to; // seconds; if < 0 cert has expired, negative value means past, positive is in future
134 +
135 + // Relevant file in which this certificate resides
136 + std::optional<fs::path> file;
137 + };
138 +
139 + } // namespace evse_security
140 +
+
+
+
+ + +