From fcf18639ea7bcd5a81d9337b5aca7a546c5edced Mon Sep 17 00:00:00 2001 From: pietfried Date: Mon, 18 Mar 2024 22:43:57 +0100 Subject: [PATCH 1/7] Added support for OCPP2.0.1 Plug&Charge: * Changed evse_security.yaml interface: Now two separate functions exist to get ocsp request data to be able to request the ocsp data for MO contract certificates * LeafCertificateType enum was extended with the additional value: MO * Refactored EvseSecurity and the lib/ocpp to address the required types/interface changes * Added conversion function in OCPP201 module * Changed EvseV2G module so that it always includes the OCSP request data of the contract certificate and optionally includes the certificate in the ProvidedIdToken * Added P&C config for OCPP201 Signed-off-by: pietfried --- config/CMakeLists.txt | 1 + config/config-sil-ocpp-pnc.yaml | 2 +- config/config-sil-ocpp201-pnc.yaml | 192 ++++++++++++++++++ interfaces/evse_security.yaml | 19 +- lib/staging/ocpp/evse_security_ocpp.cpp | 47 ++++- lib/staging/ocpp/evse_security_ocpp.hpp | 13 +- modules/EvseSecurity/conversions.cpp | 6 +- .../EvseSecurity/main/evse_securityImpl.cpp | 9 +- .../EvseSecurity/main/evse_securityImpl.hpp | 4 +- modules/EvseV2G/iso_server.cpp | 48 +++-- modules/EvseV2G/tools.cpp | 33 +++ modules/EvseV2G/tools.hpp | 19 ++ modules/OCPP201/OCPP201.cpp | 10 +- modules/OCPP201/conversions.cpp | 9 + modules/OCPP201/conversions.hpp | 6 + types/evse_security.yaml | 1 + 16 files changed, 369 insertions(+), 50 deletions(-) create mode 100644 config/config-sil-ocpp201-pnc.yaml diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index 583f88282..f6349c26c 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -11,6 +11,7 @@ generate_config_run_script(CONFIG sil-gen-pm) generate_config_run_script(CONFIG sil-ocpp) generate_config_run_script(CONFIG sil-ocpp-custom-extension) generate_config_run_script(CONFIG sil-ocpp-pnc) +generate_config_run_script(CONFIG sil-ocpp201-pnc) generate_config_run_script(CONFIG example) # install configs diff --git a/config/config-sil-ocpp-pnc.yaml b/config/config-sil-ocpp-pnc.yaml index bfc4eb956..5c117a6d4 100644 --- a/config/config-sil-ocpp-pnc.yaml +++ b/config/config-sil-ocpp-pnc.yaml @@ -162,7 +162,7 @@ active_modules: evse_security: module: EvseSecurity config_module: - private_key_password: "V2GCaPass2023Valencia" + private_key_password: "123456" token_provider_1: module: DummyTokenProviderManual energy_manager: diff --git a/config/config-sil-ocpp201-pnc.yaml b/config/config-sil-ocpp201-pnc.yaml new file mode 100644 index 000000000..25251b374 --- /dev/null +++ b/config/config-sil-ocpp201-pnc.yaml @@ -0,0 +1,192 @@ +active_modules: + iso15118_charger: + module: EvseV2G + config_module: + device: auto + tls_security: allow + verify_contract_cert_chain: false + connections: + security: + - module_id: evse_security + implementation_id: main + iso15118_car: + module: PyEvJosev + config_module: + device: auto + supported_ISO15118_2: true + tls_active: true + is_cert_install_needed: true + evse_manager_1: + module: EvseManager + config_module: + connector_id: 1 + three_phases: true + has_ventilation: true + country_code: DE + evse_id: "DE*PNX*00001" + session_logging: true + session_logging_xml: false + session_logging_path: /tmp/everest-logs + ac_hlc_enabled: true + ac_hlc_use_5percent: false + ac_enforce_hlc: false + connections: + bsp: + - module_id: yeti_driver_1 + implementation_id: board_support + powermeter_grid_side: + - module_id: yeti_driver_1 + implementation_id: powermeter + slac: + - module_id: slac + implementation_id: evse + hlc: + - module_id: iso15118_charger + implementation_id: charger + evse_manager_2: + module: EvseManager + config_module: + connector_id: 2 + three_phases: true + has_ventilation: true + country_code: DE + evse_id: "2" + session_logging: true + session_logging_xml: false + session_logging_path: /tmp + ac_hlc_enabled: false + ac_hlc_use_5percent: false + ac_enforce_hlc: false + connections: + bsp: + - module_id: yeti_driver_2 + implementation_id: board_support + powermeter_grid_side: + - module_id: yeti_driver_2 + implementation_id: powermeter + slac: + - module_id: slac + implementation_id: evse + hlc: + - module_id: iso15118_charger + implementation_id: charger + yeti_driver_1: + module: JsYetiSimulator + config_module: + connector_id: 1 + yeti_driver_2: + module: JsYetiSimulator + config_module: + connector_id: 2 + slac: + module: JsSlacSimulator + car_simulator_1: + module: JsCarSimulator + config_module: + connector_id: 1 + auto_enable: true + auto_exec: false + auto_exec_commands: sleep 1;iec_wait_pwr_ready;sleep 1;draw_power_regulated 16,3;sleep 30;unplug + connections: + simulation_control: + - module_id: yeti_driver_1 + implementation_id: yeti_simulation_control + ev: + - module_id: iso15118_car + implementation_id: ev + slac: + - module_id: slac + implementation_id: ev + car_simulator_2: + module: JsCarSimulator + config_module: + connector_id: 2 + auto_enable: true + auto_exec: false + connections: + simulation_control: + - module_id: yeti_driver_2 + implementation_id: yeti_simulation_control + ev: + - module_id: iso15118_car + implementation_id: ev + slac: + - module_id: slac + implementation_id: ev + auth: + module: Auth + config_module: + connection_timeout: 120 + selection_algorithm: PlugEvents + connections: + token_provider: + - module_id: token_provider_1 + implementation_id: main + - module_id: ocpp + implementation_id: auth_provider + - module_id: evse_manager_1 + implementation_id: token_provider + - module_id: evse_manager_2 + implementation_id: token_provider + token_validator: + - module_id: ocpp + implementation_id: auth_validator + evse_manager: + - module_id: evse_manager_1 + implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse + ocpp: + module: OCPP201 + connections: + evse_manager: + - module_id: evse_manager_1 + implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse + auth: + - module_id: auth + implementation_id: main + system: + - module_id: system + implementation_id: main + security: + - module_id: evse_security + implementation_id: main + evse_security: + module: EvseSecurity + config_module: + private_key_password: "123456" + token_provider_1: + module: DummyTokenProviderManual + energy_manager: + module: EnergyManager + connections: + energy_trunk: + - module_id: grid_connection_point + implementation_id: energy_grid + grid_connection_point: + module: EnergyNode + config_module: + fuse_limit_A: 40.0 + phase_count: 3 + connections: + price_information: [] + energy_consumer: + - module_id: evse_manager_1 + implementation_id: energy_grid + - module_id: evse_manager_2 + implementation_id: energy_grid + powermeter: + - module_id: yeti_driver_1 + implementation_id: powermeter + api: + module: API + connections: + evse_manager: + - module_id: evse_manager_1 + implementation_id: evse + system: + module: System + +x-module-layout: {} diff --git a/interfaces/evse_security.yaml b/interfaces/evse_security.yaml index 40feba778..85ad527a1 100644 --- a/interfaces/evse_security.yaml +++ b/interfaces/evse_security.yaml @@ -71,10 +71,25 @@ cmds: description: Indicates the result of the command and optional certificate hash data type: object $ref: /evse_security#/GetInstalledCertificatesResult + get_v2g_ocsp_request_data: + description: >- + Command to retrieve the OCSP request data of the V2G certificates. Contains OCSP data for each + certificate that is present in the chain (exluding the root). + result: + description: The OCSP request data of all V2G CA certificates including the Sub CAs (exluding the root) + type: object + $ref: /evse_security#/OCSPRequestDataList get_ocsp_request_data: - description: Command to retrieve the OCSP request data of the V2G certificates + description: >- + Command to retrieve the OCSP request data of the given certificate chain. + arguments: + certificate_chain: + description: Certificate chain for which the OCSP data is retrieved + type: string result: - description: The OCSP request data of all V2G CA certificates including Sub CAs + description: >- + The OCSP request data of the given certificate chain. Contains OCSP data for each + certificate in the given chain. type: object $ref: /evse_security#/OCSPRequestDataList update_ocsp_cache: diff --git a/lib/staging/ocpp/evse_security_ocpp.cpp b/lib/staging/ocpp/evse_security_ocpp.cpp index 27fcf5409..0e3819897 100644 --- a/lib/staging/ocpp/evse_security_ocpp.cpp +++ b/lib/staging/ocpp/evse_security_ocpp.cpp @@ -28,9 +28,8 @@ EvseSecurity::update_leaf_certificate(const std::string& certificate_chain, this->r_security.call_update_leaf_certificate(certificate_chain, conversions::from_ocpp(certificate_type))); } -ocpp::CertificateValidationResult -EvseSecurity::verify_certificate(const std::string& certificate_chain, - const ocpp::CertificateSigningUseEnum& certificate_type) { +ocpp::CertificateValidationResult EvseSecurity::verify_certificate(const std::string& certificate_chain, + const ocpp::LeafCertificateType& certificate_type) { return conversions::to_ocpp( this->r_security.call_verify_certificate(certificate_chain, conversions::from_ocpp(certificate_type))); } @@ -53,16 +52,28 @@ EvseSecurity::get_installed_certificates(const std::vector EvseSecurity::get_ocsp_request_data() { +std::vector EvseSecurity::get_v2g_ocsp_request_data() { std::vector result; - const auto ocsp_request_data = this->r_security.call_get_ocsp_request_data(); + const auto ocsp_request_data = this->r_security.call_get_v2g_ocsp_request_data(); for (const auto& ocsp_request_entry : ocsp_request_data.ocsp_request_data_list) { result.push_back(conversions::to_ocpp(ocsp_request_entry)); } return result; } + +std::vector EvseSecurity::get_ocsp_request_data(const std::string& certificate_chain) { + std::vector result; + + const auto ocsp_request_data = this->r_security.call_get_ocsp_request_data(certificate_chain); + for (const auto& ocsp_request_entry : ocsp_request_data.ocsp_request_data_list) { + result.push_back(conversions::to_ocpp(ocsp_request_entry)); + } + + return result; +} + void EvseSecurity::update_ocsp_cache(const ocpp::CertificateHashDataType& certificate_hash_data, const std::string& ocsp_response) { this->r_security.call_update_ocsp_cache(conversions::from_ocpp(certificate_hash_data), ocsp_response); @@ -123,14 +134,16 @@ ocpp::CaCertificateType to_ocpp(types::evse_security::CaCertificateType other) { } } -ocpp::CertificateSigningUseEnum to_ocpp(types::evse_security::LeafCertificateType other) { +ocpp::LeafCertificateType to_ocpp(types::evse_security::LeafCertificateType other) { switch (other) { case types::evse_security::LeafCertificateType::CSMS: - return ocpp::CertificateSigningUseEnum::ChargingStationCertificate; + return ocpp::LeafCertificateType::CSMS; case types::evse_security::LeafCertificateType::V2G: - return ocpp::CertificateSigningUseEnum::V2GCertificate; + return ocpp::LeafCertificateType::V2G; case types::evse_security::LeafCertificateType::MF: - return ocpp::CertificateSigningUseEnum::ManufacturerCertificate; + return ocpp::LeafCertificateType::MF; + case types::evse_security::LeafCertificateType::MO: + return ocpp::LeafCertificateType::MO; default: throw std::runtime_error( "Could not convert types::evse_security::LeafCertificateType to ocpp::CertificateSigningUseEnum"); @@ -305,6 +318,22 @@ types::evse_security::LeafCertificateType from_ocpp(ocpp::CertificateSigningUseE } } +types::evse_security::LeafCertificateType from_ocpp(ocpp::LeafCertificateType other) { + switch (other) { + case ocpp::LeafCertificateType::CSMS: + return types::evse_security::LeafCertificateType::CSMS; + case ocpp::LeafCertificateType::V2G: + return types::evse_security::LeafCertificateType::V2G; + case ocpp::LeafCertificateType::MF: + return types::evse_security::LeafCertificateType::MF; + case ocpp::LeafCertificateType::MO: + return types::evse_security::LeafCertificateType::MO; + default: + throw std::runtime_error( + "Could not convert ocpp::CertificateSigningUseEnum to types::evse_security::LeafCertificateType"); + } +} + types::evse_security::CertificateType from_ocpp(ocpp::CertificateType other) { switch (other) { case ocpp::CertificateType::V2GRootCertificate: diff --git a/lib/staging/ocpp/evse_security_ocpp.hpp b/lib/staging/ocpp/evse_security_ocpp.hpp index b1882e6b0..00c82c209 100644 --- a/lib/staging/ocpp/evse_security_ocpp.hpp +++ b/lib/staging/ocpp/evse_security_ocpp.hpp @@ -20,15 +20,15 @@ class EvseSecurity : public ocpp::EvseSecurity { const ocpp::CaCertificateType& certificate_type) override; ocpp::DeleteCertificateResult delete_certificate(const ocpp::CertificateHashDataType& certificate_hash_data) override; - ocpp::CertificateValidationResult - verify_certificate(const std::string& certificate_chain, - const ocpp::CertificateSigningUseEnum& certificate_type) override; + ocpp::CertificateValidationResult verify_certificate(const std::string& certificate_chain, + const ocpp::LeafCertificateType& certificate_type) override; ocpp::InstallCertificateResult update_leaf_certificate(const std::string& certificate_chain, const ocpp::CertificateSigningUseEnum& certificate_type) override; std::vector get_installed_certificates(const std::vector& certificate_types) override; - std::vector get_ocsp_request_data() override; + std::vector get_v2g_ocsp_request_data() override; + std::vector get_ocsp_request_data(const std::string& certificate_chain) override; void update_ocsp_cache(const ocpp::CertificateHashDataType& certificate_hash_data, const std::string& ocsp_response) override; bool is_ca_certificate_installed(const ocpp::CaCertificateType& certificate_type) override; @@ -44,7 +44,7 @@ class EvseSecurity : public ocpp::EvseSecurity { namespace conversions { ocpp::CaCertificateType to_ocpp(types::evse_security::CaCertificateType other); -ocpp::CertificateSigningUseEnum to_ocpp(types::evse_security::LeafCertificateType other); +ocpp::LeafCertificateType to_ocpp(types::evse_security::LeafCertificateType other); ocpp::CertificateType to_ocpp(types::evse_security::CertificateType other); ocpp::HashAlgorithmEnumType to_ocpp(types::evse_security::HashAlgorithm other); ocpp::InstallCertificateResult to_ocpp(types::evse_security::InstallCertificateResult other); @@ -58,6 +58,7 @@ ocpp::KeyPair to_ocpp(types::evse_security::KeyPair other); types::evse_security::CaCertificateType from_ocpp(ocpp::CaCertificateType other); types::evse_security::LeafCertificateType from_ocpp(ocpp::CertificateSigningUseEnum other); +types::evse_security::LeafCertificateType from_ocpp(ocpp::LeafCertificateType other); types::evse_security::CertificateType from_ocpp(ocpp::CertificateType other); types::evse_security::HashAlgorithm from_ocpp(ocpp::HashAlgorithmEnumType other); types::evse_security::InstallCertificateResult from_ocpp(ocpp::InstallCertificateResult other); @@ -70,4 +71,4 @@ types::evse_security::KeyPair from_ocpp(ocpp::KeyPair other); }; // namespace conversions -#endif // EVEREST_SECURITY_OCPP_HPP \ No newline at end of file +#endif // EVEREST_SECURITY_OCPP_HPP diff --git a/modules/EvseSecurity/conversions.cpp b/modules/EvseSecurity/conversions.cpp index f2417ad7f..eba1adaaf 100644 --- a/modules/EvseSecurity/conversions.cpp +++ b/modules/EvseSecurity/conversions.cpp @@ -45,6 +45,8 @@ evse_security::LeafCertificateType from_everest(types::evse_security::LeafCertif return evse_security::LeafCertificateType::V2G; case types::evse_security::LeafCertificateType::MF: return evse_security::LeafCertificateType::MF; + case types::evse_security::LeafCertificateType::MO: + return evse_security::LeafCertificateType::MO; default: throw std::runtime_error( "Could not convert types::evse_security::LeafCertificateType to evse_security::LeafCertificateType"); @@ -231,6 +233,8 @@ types::evse_security::LeafCertificateType to_everest(evse_security::LeafCertific return types::evse_security::LeafCertificateType::V2G; case evse_security::LeafCertificateType::MF: return types::evse_security::LeafCertificateType::MF; + case evse_security::LeafCertificateType::MO: + return types::evse_security::LeafCertificateType::MO; default: throw std::runtime_error( "Could not convert evse_security::LeafCertificateType to types::evse_security::LeafCertificateType"); @@ -422,4 +426,4 @@ types::evse_security::KeyPair to_everest(evse_security::KeyPair other) { } // namespace conversions -} // namespace module \ No newline at end of file +} // namespace module diff --git a/modules/EvseSecurity/main/evse_securityImpl.cpp b/modules/EvseSecurity/main/evse_securityImpl.cpp index 579a5d98b..f33c22a5c 100644 --- a/modules/EvseSecurity/main/evse_securityImpl.cpp +++ b/modules/EvseSecurity/main/evse_securityImpl.cpp @@ -63,8 +63,13 @@ types::evse_security::GetInstalledCertificatesResult evse_securityImpl::handle_g return conversions::to_everest(this->evse_security->get_installed_certificates(_certificate_types)); } -types::evse_security::OCSPRequestDataList evse_securityImpl::handle_get_ocsp_request_data() { - return conversions::to_everest(this->evse_security->get_ocsp_request_data()); +types::evse_security::OCSPRequestDataList evse_securityImpl::handle_get_v2g_ocsp_request_data() { + return conversions::to_everest(this->evse_security->get_v2g_ocsp_request_data()); +} + +types::evse_security::OCSPRequestDataList +evse_securityImpl::handle_get_ocsp_request_data(std::string& certificate_chain) { + return conversions::to_everest(this->evse_security->get_ocsp_request_data(certificate_chain)); } void evse_securityImpl::handle_update_ocsp_cache(types::evse_security::CertificateHashData& certificate_hash_data, diff --git a/modules/EvseSecurity/main/evse_securityImpl.hpp b/modules/EvseSecurity/main/evse_securityImpl.hpp index cccfb15be..e20d36910 100644 --- a/modules/EvseSecurity/main/evse_securityImpl.hpp +++ b/modules/EvseSecurity/main/evse_securityImpl.hpp @@ -47,7 +47,9 @@ class evse_securityImpl : public evse_securityImplBase { types::evse_security::LeafCertificateType& certificate_type) override; virtual types::evse_security::GetInstalledCertificatesResult handle_get_installed_certificates(std::vector& certificate_types) override; - virtual types::evse_security::OCSPRequestDataList handle_get_ocsp_request_data() override; + virtual types::evse_security::OCSPRequestDataList handle_get_v2g_ocsp_request_data() override; + virtual types::evse_security::OCSPRequestDataList + handle_get_ocsp_request_data(std::string& certificate_chain) override; virtual void handle_update_ocsp_cache(types::evse_security::CertificateHashData& certificate_hash_data, std::string& ocsp_response) override; virtual bool handle_is_ca_certificate_installed(types::evse_security::CaCertificateType& certificate_type) override; diff --git a/modules/EvseV2G/iso_server.cpp b/modules/EvseV2G/iso_server.cpp index 9850754e7..84be2fe9a 100644 --- a/modules/EvseV2G/iso_server.cpp +++ b/modules/EvseV2G/iso_server.cpp @@ -1220,7 +1220,6 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { } } - std::string contract_cert_chain_pem = ""; /* Only if certificate chain verification should be done locally by the EVSE */ if (conn->ctx->session.verify_contract_cert_chain == true) { std::string v2g_root_cert_path = conn->ctx->certs_path + "/ca/v2g/V2G_ROOT_CA.pem"; @@ -1260,26 +1259,29 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { dlog(DLOG_LEVEL_INFO, "Validation of the contract certificate was successful!"); } else { - // Save the certificate chain in a variable in PEM format to publish it - mbedtls_x509_crt* crt = &conn->ctx->session.contract.crt; - unsigned char* base64Buffer = NULL; - size_t olen; - - while (crt != nullptr && crt->version != 0) { - mbedtls_base64_encode(NULL, 0, &olen, crt->raw.p, crt->raw.len); - base64Buffer = static_cast(malloc(olen)); - if ((base64Buffer == NULL) || - ((mbedtls_base64_encode(base64Buffer, olen, &olen, crt->raw.p, crt->raw.len)) != 0)) { - dlog(DLOG_LEVEL_ERROR, "Unable to encode certificate chain"); - break; - } - contract_cert_chain_pem.append("-----BEGIN CERTIFICATE-----\n"); - contract_cert_chain_pem.append(std::string(reinterpret_cast(base64Buffer), olen)); - contract_cert_chain_pem.append("\n-----END CERTIFICATE-----\n"); + } - free(base64Buffer); - crt = crt->next; + // initialize contract cert chain to retrieve ocsp request data + std::string contract_cert_chain_pem = ""; + // Save the certificate chain in a variable in PEM format to publish it + mbedtls_x509_crt* crt = &conn->ctx->session.contract.crt; + unsigned char* base64Buffer = NULL; + size_t olen; + + while (crt != nullptr && crt->version != 0) { + mbedtls_base64_encode(NULL, 0, &olen, crt->raw.p, crt->raw.len); + base64Buffer = static_cast(malloc(olen)); + if ((base64Buffer == NULL) || + ((mbedtls_base64_encode(base64Buffer, olen, &olen, crt->raw.p, crt->raw.len)) != 0)) { + dlog(DLOG_LEVEL_ERROR, "Unable to encode certificate chain"); + break; } + contract_cert_chain_pem.append("-----BEGIN CERTIFICATE-----\n"); + contract_cert_chain_pem.append(std::string(reinterpret_cast(base64Buffer), olen)); + contract_cert_chain_pem.append("\n-----END CERTIFICATE-----\n"); + + free(base64Buffer); + crt = crt->next; } generate_random_data(&conn->ctx->session.gen_challenge, GEN_CHALLENGE_SIZE); @@ -1287,12 +1289,18 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { res->GenChallenge.bytesLen = GEN_CHALLENGE_SIZE; conn->ctx->session.contract.valid_crt = true; + const auto ocsp_response = conn->ctx->r_security->call_get_ocsp_request_data(contract_cert_chain_pem); + const auto certificate_hash_data_info_vector = convert_to_certificate_hash_data_info_vector(ocsp_response); + // Publish the provided signature certificate chain and eMAID from EVCC // to receive PnC authorization types::authorization::ProvidedIdToken ProvidedIdToken; ProvidedIdToken.id_token = {std::string(cert_emaid), types::authorization::IdTokenType::eMAID}; ProvidedIdToken.authorization_type = types::authorization::AuthorizationType::PlugAndCharge; - if (contract_cert_chain_pem.empty() == false) { + ProvidedIdToken.iso15118CertificateHashData = certificate_hash_data_info_vector; + if (conn->ctx->session.verify_contract_cert_chain == + false) { // chain has not been verified by this module, so we append to contract to the ProvidedIdToken so + // that it can be verified in later stages of the authorization process ProvidedIdToken.certificate = contract_cert_chain_pem; } conn->ctx->p_charger->publish_Require_Auth_PnC(ProvidedIdToken); diff --git a/modules/EvseV2G/tools.cpp b/modules/EvseV2G/tools.cpp index 6d8f4691f..f94db476d 100644 --- a/modules/EvseV2G/tools.cpp +++ b/modules/EvseV2G/tools.cpp @@ -341,3 +341,36 @@ std::string convert_to_hex_str(const uint8_t* data, int len) { return string_stream.str(); } + +types::iso15118_charger::HashAlgorithm +convert_to_hash_algorithm(const types::evse_security::HashAlgorithm hash_algorithm) { + switch (hash_algorithm) { + case types::evse_security::HashAlgorithm::SHA256: + return types::iso15118_charger::HashAlgorithm::SHA256; + case types::evse_security::HashAlgorithm::SHA384: + return types::iso15118_charger::HashAlgorithm::SHA384; + case types::evse_security::HashAlgorithm::SHA512: + return types::iso15118_charger::HashAlgorithm::SHA512; + default: + throw std::runtime_error( + "Could not convert types::evse_security::HashAlgorithm to types::iso15118_charger::HashAlgorithm"); + } +} + +std::vector +convert_to_certificate_hash_data_info_vector(const types::evse_security::OCSPRequestDataList& ocsp_request_data_list) { + std::vector certificate_hash_data_info_vec; + for (const auto& ocsp_request_data : ocsp_request_data_list.ocsp_request_data_list) { + if (ocsp_request_data.responder_url.has_value() and ocsp_request_data.certificate_hash_data.has_value()) { + types::iso15118_charger::CertificateHashDataInfo certificate_hash_data; + certificate_hash_data.hashAlgorithm = + convert_to_hash_algorithm(ocsp_request_data.certificate_hash_data.value().hash_algorithm); + certificate_hash_data.issuerNameHash = ocsp_request_data.certificate_hash_data.value().issuer_name_hash; + certificate_hash_data.issuerKeyHash = ocsp_request_data.certificate_hash_data.value().issuer_key_hash; + certificate_hash_data.serialNumber = ocsp_request_data.certificate_hash_data.value().serial_number; + certificate_hash_data.responderURL = ocsp_request_data.responder_url.value(); + certificate_hash_data_info_vec.push_back(certificate_hash_data); + } + } + return certificate_hash_data_info_vec; +} diff --git a/modules/EvseV2G/tools.hpp b/modules/EvseV2G/tools.hpp index 5e74d90b6..dbd51c0ca 100644 --- a/modules/EvseV2G/tools.hpp +++ b/modules/EvseV2G/tools.hpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #define MAX_FILE_NAME_LENGTH 100 #define MAX_PKI_CA_LENGTH 4 /* leaf up to root certificate */ @@ -117,4 +120,20 @@ uint8_t get_dir_numbered_file_names(char file_names[MAX_PKI_CA_LENGTH][MAX_FILE_ */ std::string convert_to_hex_str(const uint8_t* data, int len); +/** + * \brief convert the given \p hash_algorithm to type types::iso15118_charger::HashAlgorithm + * \param hash_algorithm + * \return types::iso15118_charger::HashAlgorithm + */ +types::iso15118_charger::HashAlgorithm +convert_to_hash_algorithm(const types::evse_security::HashAlgorithm hash_algorithm); + +/** + * \brief convert the given \p ocsp_request_data_list to std::vector + * \param ocsp_request_data_list + * \return std::vector + */ +std::vector +convert_to_certificate_hash_data_info_vector(const types::evse_security::OCSPRequestDataList& ocsp_request_data_list); + #endif /* TOOLS_H */ diff --git a/modules/OCPP201/OCPP201.cpp b/modules/OCPP201/OCPP201.cpp index 391d9a7c4..c610a337b 100644 --- a/modules/OCPP201/OCPP201.cpp +++ b/modules/OCPP201/OCPP201.cpp @@ -491,14 +491,8 @@ void OCPP201::ready() { evse->subscribe_iso15118_certificate_request( [this, evse_id](const types::iso15118_charger::Request_Exi_Stream_Schema& certificate_request) { - // transform request forward to libocpp - ocpp::v201::Get15118EVCertificateRequest ocpp_request; - ocpp_request.exiRequest = certificate_request.exiRequest; - ocpp_request.iso15118SchemaVersion = certificate_request.iso15118SchemaVersion; - ocpp_request.action = - conversions::to_ocpp_certificate_action_enum(certificate_request.certificateAction); - - auto ocpp_response = this->charge_point->on_get_15118_ev_certificate_request(ocpp_request); + auto ocpp_response = this->charge_point->on_get_15118_ev_certificate_request( + conversions::to_ocpp_get_15118_certificate_request(certificate_request)); EVLOG_debug << "Received response from get_15118_ev_certificate_request: " << ocpp_response; // transform response, inject action, send to associated EvseManager const auto everest_response_status = diff --git a/modules/OCPP201/conversions.cpp b/modules/OCPP201/conversions.cpp index df5b9d959..f9c89f917 100644 --- a/modules/OCPP201/conversions.cpp +++ b/modules/OCPP201/conversions.cpp @@ -753,6 +753,15 @@ ocpp::v201::AttributeEnum to_ocpp_attribute_enum(const types::ocpp::AttributeEnu } } +ocpp::v201::Get15118EVCertificateRequest +to_ocpp_get_15118_certificate_request(const types::iso15118_charger::Request_Exi_Stream_Schema& request) { + ocpp::v201::Get15118EVCertificateRequest _request; + _request.iso15118SchemaVersion = request.iso15118SchemaVersion; + _request.exiRequest = request.exiRequest; + _request.action = conversions::to_ocpp_certificate_action_enum(request.certificateAction); + return _request; +} + types::system::UploadLogsRequest to_everest_upload_logs_request(const ocpp::v201::GetLogRequest& request) { types::system::UploadLogsRequest _request; _request.location = request.log.remoteLocation.get(); diff --git a/modules/OCPP201/conversions.hpp b/modules/OCPP201/conversions.hpp index e6ba21e2d..2e3646c73 100644 --- a/modules/OCPP201/conversions.hpp +++ b/modules/OCPP201/conversions.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,11 @@ ocpp::v201::EVSE to_ocpp_evse(const types::ocpp::EVSE& evse); /// \brief Converts a given types::ocpp::AttributeEnum to ocpp::v201::AttributeEnum ocpp::v201::AttributeEnum to_ocpp_attribute_enum(const types::ocpp::AttributeEnum attribute_enum); +/// \brief Converts a given types::types::iso15118_charger::Request_Exi_Stream_Schema to +/// ocpp::v201::Get15118EVCertificateRequest +ocpp::v201::Get15118EVCertificateRequest +to_ocpp_get_15118_certificate_request(const types::iso15118_charger::Request_Exi_Stream_Schema& request); + /// \brief Converts a given ocpp::v201::ReasonEnum \p stop_reason to a types::evse_manager::StopTransactionReason. types::evse_manager::StopTransactionReason to_everest_stop_transaction_reason(const ocpp::v201::ReasonEnum& stop_reason); diff --git a/types/evse_security.yaml b/types/evse_security.yaml index ad9821e47..7709aca3b 100644 --- a/types/evse_security.yaml +++ b/types/evse_security.yaml @@ -21,6 +21,7 @@ types: - CSMS - V2G - MF + - MO CertificateType: description: Enum specifies certificate type of leaf and CA certificates type: string From dd9f29666b4e07b3458eeadc7cbfd3676b93cd91 Mon Sep 17 00:00:00 2001 From: pietfried Date: Tue, 19 Mar 2024 12:40:31 +0100 Subject: [PATCH 2/7] removed unused else Signed-off-by: pietfried --- modules/EvseV2G/iso_server.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/EvseV2G/iso_server.cpp b/modules/EvseV2G/iso_server.cpp index 84be2fe9a..f97fb2abd 100644 --- a/modules/EvseV2G/iso_server.cpp +++ b/modules/EvseV2G/iso_server.cpp @@ -1258,7 +1258,6 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { } dlog(DLOG_LEVEL_INFO, "Validation of the contract certificate was successful!"); - } else { } // initialize contract cert chain to retrieve ocsp request data From 439b7f9e8c008323577c95e4d205d6ac49d83fab Mon Sep 17 00:00:00 2001 From: pietfried Date: Tue, 19 Mar 2024 12:41:03 +0100 Subject: [PATCH 3/7] clang format Signed-off-by: pietfried --- modules/EvseV2G/tools.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/EvseV2G/tools.hpp b/modules/EvseV2G/tools.hpp index dbd51c0ca..b9365e233 100644 --- a/modules/EvseV2G/tools.hpp +++ b/modules/EvseV2G/tools.hpp @@ -4,6 +4,8 @@ #ifndef TOOLS_H #define TOOLS_H +#include +#include #include #include #include @@ -13,8 +15,6 @@ #include #include #include -#include -#include #define MAX_FILE_NAME_LENGTH 100 #define MAX_PKI_CA_LENGTH 4 /* leaf up to root certificate */ From 6b77ae31e4872e3f2566e1ff2eb9078188394d48 Mon Sep 17 00:00:00 2001 From: AssemblyJohn Date: Wed, 20 Mar 2024 13:05:24 +0200 Subject: [PATCH 4/7] Updated interfaces for libevse interoperability Signed-off-by: AssemblyJohn --- interfaces/evse_security.yaml | 7 ++++--- lib/staging/ocpp/evse_security_ocpp.cpp | 4 ++-- lib/staging/ocpp/evse_security_ocpp.hpp | 2 +- modules/EvseSecurity/main/evse_securityImpl.cpp | 4 ++-- modules/EvseSecurity/main/evse_securityImpl.hpp | 2 +- modules/EvseV2G/iso_server.cpp | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/interfaces/evse_security.yaml b/interfaces/evse_security.yaml index 85ad527a1..353d2142e 100644 --- a/interfaces/evse_security.yaml +++ b/interfaces/evse_security.yaml @@ -74,14 +74,15 @@ cmds: get_v2g_ocsp_request_data: description: >- Command to retrieve the OCSP request data of the V2G certificates. Contains OCSP data for each - certificate that is present in the chain (exluding the root). + certificate that is present in the chain (excluding the root). result: description: The OCSP request data of all V2G CA certificates including the Sub CAs (exluding the root) type: object $ref: /evse_security#/OCSPRequestDataList - get_ocsp_request_data: + get_mo_ocsp_request_data: description: >- - Command to retrieve the OCSP request data of the given certificate chain. + Command to retrieve the OCSP request data of the given MO certificate chain. Contains OCSP data + for each certificate that is present in the chain (excluding the root) arguments: certificate_chain: description: Certificate chain for which the OCSP data is retrieved diff --git a/lib/staging/ocpp/evse_security_ocpp.cpp b/lib/staging/ocpp/evse_security_ocpp.cpp index 0e3819897..e8b942d2c 100644 --- a/lib/staging/ocpp/evse_security_ocpp.cpp +++ b/lib/staging/ocpp/evse_security_ocpp.cpp @@ -63,10 +63,10 @@ std::vector EvseSecurity::get_v2g_ocsp_request_data() { return result; } -std::vector EvseSecurity::get_ocsp_request_data(const std::string& certificate_chain) { +std::vector EvseSecurity::get_mo_ocsp_request_data(const std::string& certificate_chain) { std::vector result; - const auto ocsp_request_data = this->r_security.call_get_ocsp_request_data(certificate_chain); + const auto ocsp_request_data = this->r_security.call_get_mo_ocsp_request_data(certificate_chain); for (const auto& ocsp_request_entry : ocsp_request_data.ocsp_request_data_list) { result.push_back(conversions::to_ocpp(ocsp_request_entry)); } diff --git a/lib/staging/ocpp/evse_security_ocpp.hpp b/lib/staging/ocpp/evse_security_ocpp.hpp index 00c82c209..2c4d9d150 100644 --- a/lib/staging/ocpp/evse_security_ocpp.hpp +++ b/lib/staging/ocpp/evse_security_ocpp.hpp @@ -28,7 +28,7 @@ class EvseSecurity : public ocpp::EvseSecurity { std::vector get_installed_certificates(const std::vector& certificate_types) override; std::vector get_v2g_ocsp_request_data() override; - std::vector get_ocsp_request_data(const std::string& certificate_chain) override; + std::vector get_mo_ocsp_request_data(const std::string& certificate_chain) override; void update_ocsp_cache(const ocpp::CertificateHashDataType& certificate_hash_data, const std::string& ocsp_response) override; bool is_ca_certificate_installed(const ocpp::CaCertificateType& certificate_type) override; diff --git a/modules/EvseSecurity/main/evse_securityImpl.cpp b/modules/EvseSecurity/main/evse_securityImpl.cpp index f33c22a5c..cd6572464 100644 --- a/modules/EvseSecurity/main/evse_securityImpl.cpp +++ b/modules/EvseSecurity/main/evse_securityImpl.cpp @@ -68,8 +68,8 @@ types::evse_security::OCSPRequestDataList evse_securityImpl::handle_get_v2g_ocsp } types::evse_security::OCSPRequestDataList -evse_securityImpl::handle_get_ocsp_request_data(std::string& certificate_chain) { - return conversions::to_everest(this->evse_security->get_ocsp_request_data(certificate_chain)); +evse_securityImpl::handle_get_mo_ocsp_request_data(std::string& certificate_chain) { + return conversions::to_everest(this->evse_security->get_mo_ocsp_request_data(certificate_chain)); } void evse_securityImpl::handle_update_ocsp_cache(types::evse_security::CertificateHashData& certificate_hash_data, diff --git a/modules/EvseSecurity/main/evse_securityImpl.hpp b/modules/EvseSecurity/main/evse_securityImpl.hpp index e20d36910..139bd26a2 100644 --- a/modules/EvseSecurity/main/evse_securityImpl.hpp +++ b/modules/EvseSecurity/main/evse_securityImpl.hpp @@ -49,7 +49,7 @@ class evse_securityImpl : public evse_securityImplBase { handle_get_installed_certificates(std::vector& certificate_types) override; virtual types::evse_security::OCSPRequestDataList handle_get_v2g_ocsp_request_data() override; virtual types::evse_security::OCSPRequestDataList - handle_get_ocsp_request_data(std::string& certificate_chain) override; + handle_get_mo_ocsp_request_data(std::string& certificate_chain) override; virtual void handle_update_ocsp_cache(types::evse_security::CertificateHashData& certificate_hash_data, std::string& ocsp_response) override; virtual bool handle_is_ca_certificate_installed(types::evse_security::CaCertificateType& certificate_type) override; diff --git a/modules/EvseV2G/iso_server.cpp b/modules/EvseV2G/iso_server.cpp index f97fb2abd..2d43a9850 100644 --- a/modules/EvseV2G/iso_server.cpp +++ b/modules/EvseV2G/iso_server.cpp @@ -1288,7 +1288,7 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { res->GenChallenge.bytesLen = GEN_CHALLENGE_SIZE; conn->ctx->session.contract.valid_crt = true; - const auto ocsp_response = conn->ctx->r_security->call_get_ocsp_request_data(contract_cert_chain_pem); + const auto ocsp_response = conn->ctx->r_security->call_get_mo_ocsp_request_data(contract_cert_chain_pem); const auto certificate_hash_data_info_vector = convert_to_certificate_hash_data_info_vector(ocsp_response); // Publish the provided signature certificate chain and eMAID from EVCC From af18670be56e352536c6c9206ca8a567fd446802 Mon Sep 17 00:00:00 2001 From: pietfried Date: Wed, 20 Mar 2024 13:46:23 +0100 Subject: [PATCH 5/7] move ocpp module in config Signed-off-by: pietfried --- config/config-sil-ocpp201-pnc.yaml | 46 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/config/config-sil-ocpp201-pnc.yaml b/config/config-sil-ocpp201-pnc.yaml index 25251b374..9fd745110 100644 --- a/config/config-sil-ocpp201-pnc.yaml +++ b/config/config-sil-ocpp201-pnc.yaml @@ -113,29 +113,6 @@ active_modules: slac: - module_id: slac implementation_id: ev - auth: - module: Auth - config_module: - connection_timeout: 120 - selection_algorithm: PlugEvents - connections: - token_provider: - - module_id: token_provider_1 - implementation_id: main - - module_id: ocpp - implementation_id: auth_provider - - module_id: evse_manager_1 - implementation_id: token_provider - - module_id: evse_manager_2 - implementation_id: token_provider - token_validator: - - module_id: ocpp - implementation_id: auth_validator - evse_manager: - - module_id: evse_manager_1 - implementation_id: evse - - module_id: evse_manager_2 - implementation_id: evse ocpp: module: OCPP201 connections: @@ -159,6 +136,29 @@ active_modules: private_key_password: "123456" token_provider_1: module: DummyTokenProviderManual + auth: + module: Auth + config_module: + connection_timeout: 120 + selection_algorithm: PlugEvents + connections: + token_provider: + - module_id: token_provider_1 + implementation_id: main + - module_id: ocpp + implementation_id: auth_provider + - module_id: evse_manager_1 + implementation_id: token_provider + - module_id: evse_manager_2 + implementation_id: token_provider + token_validator: + - module_id: ocpp + implementation_id: auth_validator + evse_manager: + - module_id: evse_manager_1 + implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse energy_manager: module: EnergyManager connections: From 19a99cbebb17a6e6bf1cac87debddc322f0f18fb Mon Sep 17 00:00:00 2001 From: pietfried Date: Wed, 20 Mar 2024 15:46:37 +0100 Subject: [PATCH 6/7] update dependencies on libocpp and libevse-security Signed-off-by: pietfried --- dependencies.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 54aa66164..37993d941 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -49,7 +49,7 @@ libcurl: # OCPP libocpp: git: https://github.com/EVerest/libocpp.git - git_tag: 86adda6 + git_tag: 778e080 cmake_condition: "EVEREST_DEPENDENCY_ENABLED_LIBOCPP" # Josev Josev: @@ -79,7 +79,7 @@ everest-utils: # setting it here can be misleading since it does not affect the version being used libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: v0.4.3 + git_tag: bce1ba4 cmake_condition: "EVEREST_DEPENDENCY_ENABLED_LIBEVSE_SECURITY" # unit testing gtest: From d0a021ee598531d897d86dc1bd1088ff28b727f1 Mon Sep 17 00:00:00 2001 From: pietfried Date: Thu, 21 Mar 2024 14:02:39 +0100 Subject: [PATCH 7/7] only adding iso15118HashData to provided token if contract chain could successfully be validated against MO root Signed-off-by: pietfried --- modules/EvseV2G/iso_server.cpp | 67 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/modules/EvseV2G/iso_server.cpp b/modules/EvseV2G/iso_server.cpp index 2d43a9850..af4e2ca7b 100644 --- a/modules/EvseV2G/iso_server.cpp +++ b/modules/EvseV2G/iso_server.cpp @@ -1220,10 +1220,37 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { } } + // initialize contract cert chain to retrieve ocsp request data + std::string contract_cert_chain_pem = ""; + // Save the certificate chain in a variable in PEM format to publish it + mbedtls_x509_crt* crt = &conn->ctx->session.contract.crt; + unsigned char* base64Buffer = NULL; + size_t olen; + + while (crt != nullptr && crt->version != 0) { + mbedtls_base64_encode(NULL, 0, &olen, crt->raw.p, crt->raw.len); + base64Buffer = static_cast(malloc(olen)); + if ((base64Buffer == NULL) || + ((mbedtls_base64_encode(base64Buffer, olen, &olen, crt->raw.p, crt->raw.len)) != 0)) { + dlog(DLOG_LEVEL_ERROR, "Unable to encode certificate chain"); + break; + } + contract_cert_chain_pem.append("-----BEGIN CERTIFICATE-----\n"); + contract_cert_chain_pem.append(std::string(reinterpret_cast(base64Buffer), olen)); + contract_cert_chain_pem.append("\n-----END CERTIFICATE-----\n"); + + free(base64Buffer); + crt = crt->next; + } + + std::optional> iso15118_certificate_hash_data; + /* Only if certificate chain verification should be done locally by the EVSE */ if (conn->ctx->session.verify_contract_cert_chain == true) { - std::string v2g_root_cert_path = conn->ctx->certs_path + "/ca/v2g/V2G_ROOT_CA.pem"; - std::string mo_root_cert_path = conn->ctx->certs_path + "/ca/mo/MO_ROOT_CA.pem"; + std::string v2g_root_cert_path = + conn->ctx->r_security->call_get_verify_file(types::evse_security::CaCertificateType::V2G); + std::string mo_root_cert_path = + conn->ctx->r_security->call_get_verify_file(types::evse_security::CaCertificateType::MO); mbedtls_x509_crt contract_root_crt; mbedtls_x509_crt_init(&contract_root_crt); uint32_t flags; @@ -1258,29 +1285,10 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { } dlog(DLOG_LEVEL_INFO, "Validation of the contract certificate was successful!"); - } - - // initialize contract cert chain to retrieve ocsp request data - std::string contract_cert_chain_pem = ""; - // Save the certificate chain in a variable in PEM format to publish it - mbedtls_x509_crt* crt = &conn->ctx->session.contract.crt; - unsigned char* base64Buffer = NULL; - size_t olen; - - while (crt != nullptr && crt->version != 0) { - mbedtls_base64_encode(NULL, 0, &olen, crt->raw.p, crt->raw.len); - base64Buffer = static_cast(malloc(olen)); - if ((base64Buffer == NULL) || - ((mbedtls_base64_encode(base64Buffer, olen, &olen, crt->raw.p, crt->raw.len)) != 0)) { - dlog(DLOG_LEVEL_ERROR, "Unable to encode certificate chain"); - break; - } - contract_cert_chain_pem.append("-----BEGIN CERTIFICATE-----\n"); - contract_cert_chain_pem.append(std::string(reinterpret_cast(base64Buffer), olen)); - contract_cert_chain_pem.append("\n-----END CERTIFICATE-----\n"); - free(base64Buffer); - crt = crt->next; + // contract chain ocsp data can only be retrieved if the MO root is present and the chain could be verified + const auto ocsp_response = conn->ctx->r_security->call_get_mo_ocsp_request_data(contract_cert_chain_pem); + iso15118_certificate_hash_data = convert_to_certificate_hash_data_info_vector(ocsp_response); } generate_random_data(&conn->ctx->session.gen_challenge, GEN_CHALLENGE_SIZE); @@ -1288,20 +1296,13 @@ static enum v2g_event handle_iso_payment_details(struct v2g_connection* conn) { res->GenChallenge.bytesLen = GEN_CHALLENGE_SIZE; conn->ctx->session.contract.valid_crt = true; - const auto ocsp_response = conn->ctx->r_security->call_get_mo_ocsp_request_data(contract_cert_chain_pem); - const auto certificate_hash_data_info_vector = convert_to_certificate_hash_data_info_vector(ocsp_response); - // Publish the provided signature certificate chain and eMAID from EVCC // to receive PnC authorization types::authorization::ProvidedIdToken ProvidedIdToken; ProvidedIdToken.id_token = {std::string(cert_emaid), types::authorization::IdTokenType::eMAID}; ProvidedIdToken.authorization_type = types::authorization::AuthorizationType::PlugAndCharge; - ProvidedIdToken.iso15118CertificateHashData = certificate_hash_data_info_vector; - if (conn->ctx->session.verify_contract_cert_chain == - false) { // chain has not been verified by this module, so we append to contract to the ProvidedIdToken so - // that it can be verified in later stages of the authorization process - ProvidedIdToken.certificate = contract_cert_chain_pem; - } + ProvidedIdToken.iso15118CertificateHashData = iso15118_certificate_hash_data; + ProvidedIdToken.certificate = contract_cert_chain_pem; conn->ctx->p_charger->publish_Require_Auth_PnC(ProvidedIdToken); } else {