From 870340def65539586848438931e4cac69b0ee495 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Fri, 19 Apr 2024 16:56:55 +0300 Subject: [PATCH] - Modified OCSP cache to unique filenames - Refactored get_key_pair for extra info Signed-off-by: AssemblyJohn --- include/evse_security/evse_security.hpp | 8 +- include/evse_security/evse_types.hpp | 28 ++- .../crypto/openssl/openssl_supplier.cpp | 1 - lib/evse_security/evse_security.cpp | 211 ++++++++++++------ lib/evse_security/evse_types.cpp | 14 +- tests/tests.cpp | 12 +- 6 files changed, 185 insertions(+), 89 deletions(-) diff --git a/include/evse_security/evse_security.hpp b/include/evse_security/evse_security.hpp index 9812e10..d3f797a 100644 --- a/include/evse_security/evse_security.hpp +++ b/include/evse_security/evse_security.hpp @@ -186,8 +186,10 @@ class EvseSecurity { /// the leaf including any possible SUBCAs /// @param certificate_type type of the leaf certificate /// @param encoding specifies PEM or DER format + /// @param include_ocsp if OCSP data should be included /// @return contains response result - GetKeyPairResult get_key_pair(LeafCertificateType certificate_type, EncodingFormat encoding); + GetCertificateInfoResult get_leaf_certificate_info(LeafCertificateType certificate_type, EncodingFormat encoding, + bool include_ocsp = false); /// @brief Checks and updates the symlinks for the V2G leaf certificates and keys to the most recent valid one /// @return true if one of the links was updated @@ -243,7 +245,9 @@ class EvseSecurity { // Internal versions of the functions do not lock the mutex CertificateValidationResult verify_certificate_internal(const std::string& certificate_chain, LeafCertificateType certificate_type); - GetKeyPairResult get_key_pair_internal(LeafCertificateType certificate_type, EncodingFormat encoding); + GetCertificateInfoResult get_leaf_certificate_info_internal(LeafCertificateType certificate_type, + EncodingFormat encoding, bool include_ocsp = false); + std::optional retrieve_ocsp_cache_internal(const CertificateHashData& certificate_hash_data); bool is_ca_certificate_installed_internal(CaCertificateType certificate_type); /// @brief Determines if the total filesize of certificates is > than the max_filesystem_usage bytes diff --git a/include/evse_security/evse_types.hpp b/include/evse_security/evse_types.hpp index 360ee90..4d1eabf 100644 --- a/include/evse_security/evse_types.hpp +++ b/include/evse_security/evse_types.hpp @@ -76,7 +76,7 @@ enum class GetInstalledCertificatesStatus { NotFound, }; -enum class GetKeyPairStatus { +enum class GetCertificateInfoStatus { Accepted, Rejected, NotFound, @@ -123,15 +123,25 @@ struct OCSPRequestData { struct OCSPRequestDataList { std::vector ocsp_request_data_list; ///< A list of OCSP request data }; -struct KeyPair { - fs::path key; ///< The path of the PEM or DER encoded private key - fs::path certificate; ///< The path of the PEM or DER encoded certificate chain - fs::path certificate_single; ///< The path of the PEM or DER encoded certificate + +struct CertificateOCSP { + CertificateHashData hash; + std::optional oscsp_data; +}; + +struct CertificateInfo { + fs::path key; ///< The path of the PEM or DER encoded private key + std::optional certificate; ///< The path of the PEM or DER encoded certificate chain if found + std::optional certificate_single; ///< The path of the PEM or DER encoded certificate if found + int certificate_count; ///< The count of certificates in the chain, if the chain is available, or if single 1 std::optional password; ///< Specifies the password for the private key if encrypted + std::vector + oscsp; ///< Contains the ordered list of OCSP certificate data based on the chain file order }; -struct GetKeyPairResult { - GetKeyPairStatus status; - std::optional pair; + +struct GetCertificateInfoResult { + GetCertificateInfoStatus status; + std::optional info; }; namespace conversions { @@ -144,7 +154,7 @@ std::string hash_algorithm_to_string(HashAlgorithm e); std::string install_certificate_result_to_string(InstallCertificateResult e); std::string delete_certificate_result_to_string(DeleteCertificateResult e); std::string get_installed_certificates_status_to_string(GetInstalledCertificatesStatus e); -std::string get_key_pair_status_to_string(GetKeyPairStatus e); +std::string get_certificate_info_status_to_string(GetCertificateInfoStatus e); } // namespace conversions } // namespace evse_security diff --git a/lib/evse_security/crypto/openssl/openssl_supplier.cpp b/lib/evse_security/crypto/openssl/openssl_supplier.cpp index 10f54bf..c9f1373 100644 --- a/lib/evse_security/crypto/openssl/openssl_supplier.cpp +++ b/lib/evse_security/crypto/openssl/openssl_supplier.cpp @@ -630,7 +630,6 @@ bool OpenSSLSupplier::x509_check_private_key(X509Handle* handle, std::string pri } else { provider.set_global_mode(OpenSSLProvider::mode_t::default_provider); } - EVLOG_info << "TPM Key: " << tpm_key; BIO_ptr bio(BIO_new_mem_buf(private_key.c_str(), -1)); // Passing password string since if NULL is provided, the password CB will be called diff --git a/lib/evse_security/evse_security.cpp b/lib/evse_security/evse_security.cpp index 54e764e..ba30b40 100644 --- a/lib/evse_security/evse_security.cpp +++ b/lib/evse_security/evse_security.cpp @@ -525,14 +525,15 @@ EvseSecurity::get_installed_certificates(const std::vector& cer certificate_types.end()) { // Internal since we already acquired the lock - const auto secc_key_pair = this->get_key_pair_internal(LeafCertificateType::V2G, EncodingFormat::PEM); - if (secc_key_pair.status == GetKeyPairStatus::Accepted) { + const auto secc_key_pair = + this->get_leaf_certificate_info_internal(LeafCertificateType::V2G, EncodingFormat::PEM); + if (secc_key_pair.status == GetCertificateInfoStatus::Accepted) { fs::path certificate_path; - if (secc_key_pair.pair.value().certificate.empty() == false) - certificate_path = secc_key_pair.pair.value().certificate; + if (secc_key_pair.info.value().certificate.has_value() == false) + certificate_path = secc_key_pair.info.value().certificate.value(); else - certificate_path = secc_key_pair.pair.value().certificate_single; + certificate_path = secc_key_pair.info.value().certificate_single.value(); // Leaf V2G chain X509CertificateBundle leaf_bundle(certificate_path, EncodingFormat::PEM); @@ -627,15 +628,16 @@ OCSPRequestDataList EvseSecurity::get_v2g_ocsp_request_data() { std::lock_guard guard(EvseSecurity::security_mutex); try { - const auto secc_key_pair = this->get_key_pair_internal(LeafCertificateType::V2G, EncodingFormat::PEM); + const auto secc_key_pair = + this->get_leaf_certificate_info_internal(LeafCertificateType::V2G, EncodingFormat::PEM); - if (secc_key_pair.status != GetKeyPairStatus::Accepted or !secc_key_pair.pair.has_value()) { + if (secc_key_pair.status != GetCertificateInfoStatus::Accepted or !secc_key_pair.info.has_value()) { EVLOG_error << "Could not get key pair, for v2g ocsp request!"; return OCSPRequestDataList(); } - std::vector chain = - std::move(X509CertificateBundle(secc_key_pair.pair.value().certificate, EncodingFormat::PEM).split()); + std::vector chain = std::move( + X509CertificateBundle(secc_key_pair.info.value().certificate.value(), EncodingFormat::PEM).split()); return get_ocsp_request_data_internal(this->ca_bundle_path_map.at(CaCertificateType::V2G), chain); } catch (const CertificateLoadException& e) { EVLOG_error << "Could not get v2g ocsp cache, certificate load failure: " << e.what(); @@ -729,16 +731,30 @@ void EvseSecurity::update_ocsp_cache(const CertificateHashData& certificate_hash EVLOG_debug << "Writing OCSP Response to filesystem"; if (cert.get_file().has_value()) { const auto ocsp_path = cert.get_file().value().parent_path() / "ocsp"; + if (!fs::exists(ocsp_path)) { fs::create_directories(ocsp_path); } - const auto ocsp_file_path = - ocsp_path / cert.get_file().value().filename().replace_extension(".ocsp.der"); + // Randomize filename, since multiple certificates can be stored in same bundle + const auto name = filesystem_utils::get_random_file_name("_ocsp"); + + const auto ocsp_file_path = ocsp_path / name / ".der"; + const auto hash_file_path = ocsp_path / name / ".hash"; + // Write out OCSP data std::ofstream fs(ocsp_file_path.c_str()); fs << ocsp_response; fs.close(); + + // Write out the related hash + std::ofstream hs(hash_file_path.c_str()); + hs << certificate_hash_data.issuer_name_hash; + hs << certificate_hash_data.issuer_key_hash; + hs << certificate_hash_data.serial_number; + hs.close(); + } else { + EVLOG_error << "Could not find OCSP cache patch directory!"; } } catch (const NoCertificateFound& e) { EVLOG_error << "Could not find any certificate for ocsp cache update: " << e.what(); @@ -751,6 +767,11 @@ void EvseSecurity::update_ocsp_cache(const CertificateHashData& certificate_hash std::optional EvseSecurity::retrieve_ocsp_cache(const CertificateHashData& certificate_hash_data) { std::lock_guard guard(EvseSecurity::security_mutex); + return retrieve_ocsp_cache_internal(certificate_hash_data); +} + +std::optional +EvseSecurity::retrieve_ocsp_cache_internal(const CertificateHashData& certificate_hash_data) { // TODO(ioan): shouldn't we also do this for the MO? const auto ca_bundle_path = this->ca_bundle_path_map.at(CaCertificateType::V2G); @@ -763,19 +784,38 @@ std::optional EvseSecurity::retrieve_ocsp_cache(const CertificateHa X509Wrapper cert = certificate_hierarchy.find_certificate(certificate_hash_data); EVLOG_debug << "Reading OCSP Response from filesystem"; + if (cert.get_file().has_value()) { const auto ocsp_path = cert.get_file().value().parent_path() / "ocsp"; - const auto ocsp_file_path = - ocsp_path / cert.get_file().value().filename().replace_extension(".ocsp.der"); - if (fs::exists(ocsp_file_path)) { - std::ifstream in_fs(ocsp_file_path.c_str()); - std::string ocsp_response; + // Search through the OCSP directory and see if we can find any related certificate hash data + for (const auto& ocsp_entry : fs::directory_iterator(ocsp_path)) { + if (ocsp_entry.is_regular_file()) { + if (ocsp_entry.path().extension() == ".hash") { + std::ifstream hs(ocsp_entry.path().c_str()); + + CertificateHashData read_hash; + + hs >> read_hash.issuer_name_hash; + hs >> read_hash.issuer_key_hash; + hs >> read_hash.serial_number; - in_fs >> ocsp_response; - in_fs.close(); + hs.close(); - return std::make_optional(std::move(ocsp_response)); + if (read_hash == certificate_hash_data) { + fs::path replaced_ext = ocsp_entry.path(); + replaced_ext.replace_extension(".der"); + + std::ifstream in_fs(replaced_ext.c_str()); + std::string ocsp_response; + + in_fs >> ocsp_response; + in_fs.close(); + + return std::make_optional(std::move(ocsp_response)); + } + } + } } } } catch (const NoCertificateFound& e) { @@ -889,17 +929,20 @@ std::string EvseSecurity::generate_certificate_signing_request(LeafCertificateTy return generate_certificate_signing_request(certificate_type, country, organization, common, false); } -GetKeyPairResult EvseSecurity::get_key_pair(LeafCertificateType certificate_type, EncodingFormat encoding) { +GetCertificateInfoResult EvseSecurity::get_leaf_certificate_info(LeafCertificateType certificate_type, + EncodingFormat encoding, bool include_ocsp) { std::lock_guard guard(EvseSecurity::security_mutex); - return get_key_pair_internal(certificate_type, encoding); + return get_leaf_certificate_info_internal(certificate_type, encoding, include_ocsp); } -GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certificate_type, EncodingFormat encoding) { - EVLOG_info << "Requesting key/pair: " << conversions::leaf_certificate_type_to_string(certificate_type); +GetCertificateInfoResult EvseSecurity::get_leaf_certificate_info_internal(LeafCertificateType certificate_type, + EncodingFormat encoding, bool include_ocsp) { + EVLOG_info << "Requesting leaf certificate info: " + << conversions::leaf_certificate_type_to_string(certificate_type); - GetKeyPairResult result; - result.pair = std::nullopt; + GetCertificateInfoResult result; + result.info = std::nullopt; fs::path key_dir; fs::path cert_dir; @@ -911,8 +954,8 @@ GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certifi key_dir = this->directories.secc_leaf_key_directory; cert_dir = this->directories.secc_leaf_cert_directory; } else { - EVLOG_warning << "Rejected attempt to retrieve MF key pair"; - result.status = GetKeyPairStatus::Rejected; + EVLOG_warning << "Rejected attempt to retrieve non CSMS/V2G key pair"; + result.status = GetCertificateInfoStatus::Rejected; return result; } @@ -922,7 +965,7 @@ GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certifi if (leaf_certificates.empty()) { EVLOG_warning << "Could not find any key pair"; - result.status = GetKeyPairStatus::NotFound; + result.status = GetCertificateInfoStatus::NotFound; return result; } @@ -937,9 +980,11 @@ GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certifi key_file = private_key_path; X509CertificateBundle leaf_directory(cert_dir, EncodingFormat::PEM); + auto& hierarchy = leaf_directory.get_certficate_hierarchy(); const std::vector* leaf_fullchain = nullptr; const std::vector* leaf_single = nullptr; + int chain_len = 1; // Defaults to 1, single certificate // We are searching for both the full leaf bundle, containing the leaf and the cso1/2 and the single leaf // without the cso1/2 @@ -950,6 +995,7 @@ GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certifi if (bFound) { if (chain.size() > 1) { leaf_fullchain = &chain; + chain_len = chain.size(); } else if (chain.size() == 1) { leaf_single = &chain; } @@ -962,6 +1008,8 @@ GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certifi return true; }); + std::vector certificate_ocsp{}; + if (leaf_fullchain != nullptr) { chain_file = leaf_fullchain->at(0).get_file().value(); } else { @@ -976,21 +1024,45 @@ GetKeyPairResult EvseSecurity::get_key_pair_internal(LeafCertificateType certifi << " single leaf not found at path: " << cert_dir; } - result.pair = {key_file, chain_file, certificate_file, this->private_key_password}; - result.status = GetKeyPairStatus::Accepted; + // Include OCSP data if possible + if (include_ocsp && (leaf_fullchain != nullptr || leaf_single != nullptr)) { + // Search for OCSP data for each certificate + if (leaf_fullchain != nullptr) { + for (const auto& chain_certif : *leaf_fullchain) { + try { + CertificateHashData hash = hierarchy.get_certificate_hash(chain_certif); + std::optional data = retrieve_ocsp_cache_internal(hash); + + certificate_ocsp.push_back({hash, data}); + } catch (const NoCertificateFound& e) { + // Always add to preserve file order + certificate_ocsp.push_back({{}, std::nullopt}); + } + } + } else { + try { + CertificateHashData hash = hierarchy.get_certificate_hash(leaf_single->at(0)); + certificate_ocsp.push_back({hash, retrieve_ocsp_cache_internal(hash)}); + } catch (const NoCertificateFound& e) { + } + } + } + + result.info = {key_file, chain_file, certificate_file, chain_len, this->private_key_password, certificate_ocsp}; + result.status = GetCertificateInfoStatus::Accepted; return result; } catch (const NoPrivateKeyException& e) { EVLOG_warning << "Could not find private key for the selected certificate: (" << e.what() << ")"; - result.status = GetKeyPairStatus::PrivateKeyNotFound; + result.status = GetCertificateInfoStatus::PrivateKeyNotFound; return result; } catch (const NoCertificateValidException& e) { EVLOG_warning << "Could not find valid cerificate"; - result.status = GetKeyPairStatus::NotFoundValid; + result.status = GetCertificateInfoStatus::NotFoundValid; return result; } catch (const CertificateLoadException& e) { EVLOG_warning << "Leaf certificate load exception"; - result.status = GetKeyPairStatus::NotFound; + result.status = GetCertificateInfoStatus::NotFound; return result; } } @@ -1009,28 +1081,31 @@ bool EvseSecurity::update_certificate_links(LeafCertificateType certificate_type fs::path chain_link_path = this->links.cpo_cert_chain_link; // Get the most recent valid certificate (internal since we already locked mutex) - const auto key_pair = this->get_key_pair_internal(certificate_type, EncodingFormat::PEM); - if ((key_pair.status == GetKeyPairStatus::Accepted) && key_pair.pair.has_value()) { + const auto key_pair = this->get_leaf_certificate_info_internal(certificate_type, EncodingFormat::PEM); + if ((key_pair.status == GetCertificateInfoStatus::Accepted) && key_pair.info.has_value()) { // Create or update symlinks to SECC leaf cert if (!cert_link_path.empty()) { - fs::path cert_path = key_pair.pair.value().certificate_single; - if (fs::is_symlink(cert_link_path)) { - if (fs::read_symlink(cert_link_path) != cert_path) { - fs::remove(cert_link_path); + std::optional cert_path = key_pair.info.value().certificate_single; + + if (cert_path.has_value()) { + if (fs::is_symlink(cert_link_path)) { + if (fs::read_symlink(cert_link_path) != cert_path.value()) { + fs::remove(cert_link_path); + changed = true; + } + } + if (!fs::exists(cert_link_path)) { + EVLOG_debug << "SECC cert link: " << cert_link_path << " -> " << cert_path.value(); + fs::create_symlink(cert_path.value(), cert_link_path); changed = true; } } - if (!fs::exists(cert_link_path)) { - EVLOG_debug << "SECC cert link: " << cert_link_path << " -> " << cert_path; - fs::create_symlink(cert_path, cert_link_path); - changed = true; - } } // Create or update symlinks to SECC leaf key if (!key_link_path.empty()) { - fs::path key_path = key_pair.pair.value().key; + fs::path key_path = key_pair.info.value().key; if (fs::is_symlink(key_link_path)) { if (fs::read_symlink(key_link_path) != key_path) { fs::remove(key_link_path); @@ -1045,19 +1120,21 @@ bool EvseSecurity::update_certificate_links(LeafCertificateType certificate_type } // Create or update symlinks to CPO chain - fs::path chain_path = key_pair.pair.value().certificate; - if (!chain_link_path.empty()) { - if (fs::is_symlink(chain_link_path)) { - if (fs::read_symlink(chain_link_path) != chain_path) { - fs::remove(chain_link_path); + if (key_pair.info.value().certificate.has_value()) { + fs::path chain_path = key_pair.info.value().certificate.value(); + if (!chain_link_path.empty()) { + if (fs::is_symlink(chain_link_path)) { + if (fs::read_symlink(chain_link_path) != chain_path) { + fs::remove(chain_link_path); + changed = true; + } + } + if (!fs::exists(chain_link_path)) { + EVLOG_debug << "CPO cert chain link: " << chain_link_path << " -> " << chain_path; + fs::create_symlink(chain_path, chain_link_path); changed = true; } } - if (!fs::exists(chain_link_path)) { - EVLOG_debug << "CPO cert chain link: " << chain_link_path << " -> " << chain_path; - fs::create_symlink(chain_path, chain_link_path); - changed = true; - } } } else { // Remove existing symlinks if no valid certificate is found @@ -1116,21 +1193,25 @@ int EvseSecurity::get_leaf_expiry_days_count(LeafCertificateType certificate_typ EVLOG_info << "Requesting certificate expiry: " << conversions::leaf_certificate_type_to_string(certificate_type); // Internal since we already locked mutex - const auto key_pair = this->get_key_pair_internal(certificate_type, EncodingFormat::PEM); - if (key_pair.status == GetKeyPairStatus::Accepted) { + const auto key_pair = this->get_leaf_certificate_info_internal(certificate_type, EncodingFormat::PEM, false); + if (key_pair.status == GetCertificateInfoStatus::Accepted) { try { fs::path certificate_path; - if (key_pair.pair.value().certificate.empty() == false) - certificate_path = key_pair.pair.value().certificate; - else - certificate_path = key_pair.pair.value().certificate_single; + if (key_pair.info.has_value()) { + if (key_pair.info.value().certificate.has_value()) + certificate_path = key_pair.info.value().certificate.value(); + else + certificate_path = key_pair.info.value().certificate_single.value(); + } - // In case it is a bundle, we know the leaf is always the first - X509CertificateBundle cert(certificate_path, EncodingFormat::PEM); + if (certificate_path.empty() == false) { + // In case it is a bundle, we know the leaf is always the first + X509CertificateBundle cert(certificate_path, EncodingFormat::PEM); - int64_t seconds = cert.split().at(0).get_valid_to(); - return std::chrono::duration_cast(std::chrono::seconds(seconds)).count(); + int64_t seconds = cert.split().at(0).get_valid_to(); + return std::chrono::duration_cast(std::chrono::seconds(seconds)).count(); + } } catch (const CertificateLoadException& e) { EVLOG_error << "Could not obtain leaf expiry certificate: " << e.what(); } diff --git a/lib/evse_security/evse_types.cpp b/lib/evse_security/evse_types.cpp index b599f10..3b8b717 100644 --- a/lib/evse_security/evse_types.cpp +++ b/lib/evse_security/evse_types.cpp @@ -142,20 +142,20 @@ std::string get_installed_certificates_status_to_string(GetInstalledCertificates } }; -std::string get_key_pair_status_to_string(GetKeyPairStatus e) { +std::string get_key_pair_status_to_string(GetCertificateInfoStatus e) { switch (e) { - case GetKeyPairStatus::Accepted: + case GetCertificateInfoStatus::Accepted: return "Accepted"; - case GetKeyPairStatus::Rejected: + case GetCertificateInfoStatus::Rejected: return "Rejected"; - case GetKeyPairStatus::NotFound: + case GetCertificateInfoStatus::NotFound: return "NotFound"; - case GetKeyPairStatus::NotFoundValid: + case GetCertificateInfoStatus::NotFoundValid: return "NotFoundValid"; - case GetKeyPairStatus::PrivateKeyNotFound: + case GetCertificateInfoStatus::PrivateKeyNotFound: return "PrivateKeyNotFound"; default: - throw std::out_of_range("Could not convert GetKeyPairStatus to string"); + throw std::out_of_range("Could not convert GetCertificateInfoStatus to string"); } }; diff --git a/tests/tests.cpp b/tests/tests.cpp index 16decc2..426e092 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -731,7 +731,8 @@ TEST_F(EvseSecurityTests, get_installed_certificates_and_delete_secc_leaf) { } TEST_F(EvseSecurityTests, leaf_cert_starts_in_future_accepted) { - const auto v2g_keypair_before = this->evse_security->get_key_pair(LeafCertificateType::V2G, EncodingFormat::PEM); + const auto v2g_keypair_before = + this->evse_security->get_leaf_certificate_info(LeafCertificateType::V2G, EncodingFormat::PEM); const auto new_root_ca = read_file_to_string(std::filesystem::path("future_leaf/V2G_ROOT_CA.pem")); const auto result_ca = this->evse_security->install_ca_certificate(new_root_ca, CaCertificateType::V2G); @@ -746,10 +747,11 @@ TEST_F(EvseSecurityTests, leaf_cert_starts_in_future_accepted) { ASSERT_TRUE(result_client == InstallCertificateResult::Accepted); // Check: The certificate is installed, but it isn't actually used - const auto v2g_keypair_after = this->evse_security->get_key_pair(LeafCertificateType::V2G, EncodingFormat::PEM); - ASSERT_EQ(v2g_keypair_after.pair.value().certificate, v2g_keypair_before.pair.value().certificate); - ASSERT_EQ(v2g_keypair_after.pair.value().key, v2g_keypair_before.pair.value().key); - ASSERT_EQ(v2g_keypair_after.pair.value().password, v2g_keypair_before.pair.value().password); + const auto v2g_keypair_after = + this->evse_security->get_leaf_certificate_info(LeafCertificateType::V2G, EncodingFormat::PEM); + ASSERT_EQ(v2g_keypair_after.info.value().certificate, v2g_keypair_before.info.value().certificate); + ASSERT_EQ(v2g_keypair_after.info.value().key, v2g_keypair_before.info.value().key); + ASSERT_EQ(v2g_keypair_after.info.value().password, v2g_keypair_before.info.value().password); } TEST_F(EvseSecurityTests, expired_leaf_cert_rejected) {