+
+ Line |
+ Branch |
+ Exec |
+ Source |
+
+
+ 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 |
+
+ |
+ 4 |
+ static 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 |
+
+ |
+ 24 |
+ static 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 |
+
+ |
+ 50 |
+ static 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 |
+
+ |
+ 293 |
+ static 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 |
+
+ |
+ 92 |
+ static 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 |
+
+ |
+ 34 |
+ static 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 |
+
+ |
+ 68 |
+ EvseSecurity::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 |
+
+ |
+ 68 |
+ EvseSecurity::~EvseSecurity() { |
+
+
+ 296 |
+
+ |
+ 68 |
+ } |
+
+
+ 297 |
+
+ |
+ |
+ |
+
+
+ 298 |
+
+ |
+ 34 |
+ InstallCertificateResult 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 |
+
+ |
+ 10 |
+ DeleteCertificateResult 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 |
+
+ |
+ 12 |
+ InstallCertificateResult 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 |
+
+ |
+ 16 |
+ EvseSecurity::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 |
+
+ |
+ 8 |
+ int 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 |
+
+ |
+ 4 |
+ OCSPRequestDataList 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 |
+
+ |
+ 4 |
+ OCSPRequestDataList 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 |
+
+ |
+ 12 |
+ void 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 |
+
+ |
+ 12 |
+ std::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 |
+
+ |
+ 18 |
+ std::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 |
+
+ |
+ 14 |
+ bool 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 |
+
+ |
+ 4 |
+ EvseSecurity::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 |
+
+ |
+ 4 |
+ GetCertificateSignRequestResult 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 |
+
+ |
+ 4 |
+ GetCertificateSignRequestResult 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 |
+
+ |
+ 2 |
+ GetCertificateFullInfoResult 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 |
+
+ |
+ 6 |
+ GetCertificateInfoResult 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 |
+
+ |
+ 18 |
+ GetCertificateInfoResult 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 |
+
+ |
+ 20 |
+ GetCertificateFullInfoResult 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 |
+
+ |
+ 4 |
+ GetCertificateInfoResult 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 |
+
+ |
+ 4 |
+ std::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 |
+
+ |
+ 2 |
+ std::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 |
+
+ |
+ 2 |
+ std::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 |
+
+ |
+ 2 |
+ std::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 |
+
+ |
+ 4 |
+ CertificateValidationResult 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 |
+
+ |
+ 14 |
+ CertificateValidationResult 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 |
+
+ |
+ 14 |
+ void 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 |
+
+ |
+ 72 |
+ bool 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 |
+
+ |
+ |
+ |
+
+