diff --git a/include/evse_security/crypto/interface/crypto_supplier.hpp b/include/evse_security/crypto/interface/crypto_supplier.hpp index ca263db..b69a270 100644 --- a/include/evse_security/crypto/interface/crypto_supplier.hpp +++ b/include/evse_security/crypto/interface/crypto_supplier.hpp @@ -76,7 +76,9 @@ class AbstractCryptoSupplier { public: // Digesting/decoding utils static bool digest_file_sha256(const fs::path& path, std::vector& out_digest); - static bool decode_base64_signature(const std::string& signature, std::vector& out_decoded); + + static bool encode_base64_bytes(const std::vector& bytes, std::string& out_encoded); + static bool decode_base64_string(const std::string& string, std::vector& out_decoded); }; } // namespace evse_security \ No newline at end of file diff --git a/include/evse_security/crypto/openssl/openssl_supplier.hpp b/include/evse_security/crypto/openssl/openssl_supplier.hpp index 5c553ec..f180a00 100644 --- a/include/evse_security/crypto/openssl/openssl_supplier.hpp +++ b/include/evse_security/crypto/openssl/openssl_supplier.hpp @@ -45,7 +45,9 @@ class OpenSSLSupplier : public AbstractCryptoSupplier { public: static bool digest_file_sha256(const fs::path& path, std::vector& out_digest); - static bool decode_base64_signature(const std::string& signature, std::vector& out_decoded); + + static bool encode_base64_bytes(const std::vector& bytes, std::string& out_encoded); + static bool decode_base64_string(const std::string& string, std::vector& out_decoded); }; } // namespace evse_security \ No newline at end of file diff --git a/include/evse_security/evse_security.hpp b/include/evse_security/evse_security.hpp index b0d6843..d82c4c2 100644 --- a/include/evse_security/evse_security.hpp +++ b/include/evse_security/evse_security.hpp @@ -219,6 +219,16 @@ class EvseSecurity { static bool verify_file_signature(const fs::path& path, const std::string& signing_certificate, const std::string signature); + /// @brief Decodes the base64 encoded string to the raw byte representation + /// @param encoded_string + /// @return + static std::vector base64_decode(const std::string& encoded_string); + + /// @brief Encodes the raw bytes to a base64 string + /// @param decoded_bytes + /// @return + static std::string base64_encode(const std::vector& decoded_bytes); + private: // Internal versions of the functions do not lock the mutex CertificateValidationResult verify_certificate_internal(const std::string& certificate_chain, diff --git a/lib/evse_security/crypto/interface/crypto_supplier.cpp b/lib/evse_security/crypto/interface/crypto_supplier.cpp index de3d912..a25469d 100644 --- a/lib/evse_security/crypto/interface/crypto_supplier.cpp +++ b/lib/evse_security/crypto/interface/crypto_supplier.cpp @@ -99,8 +99,11 @@ bool AbstractCryptoSupplier::digest_file_sha256(const fs::path& path, std::vecto default_crypto_supplier_usage_error() return false; } -bool AbstractCryptoSupplier::decode_base64_signature(const std::string& signature, - std::vector& out_decoded) { +static bool encode_base64_bytes(const std::vector& bytes, std::string& out_encoded) { + default_crypto_supplier_usage_error() return false; +} + +static bool decode_base64_string(const std::string& string, std::vector& out_decoded) { default_crypto_supplier_usage_error() return false; } diff --git a/lib/evse_security/crypto/openssl/openssl_supplier.cpp b/lib/evse_security/crypto/openssl/openssl_supplier.cpp index 641134e..0c91a8c 100644 --- a/lib/evse_security/crypto/openssl/openssl_supplier.cpp +++ b/lib/evse_security/crypto/openssl/openssl_supplier.cpp @@ -850,8 +850,41 @@ bool OpenSSLSupplier::digest_file_sha256(const fs::path& path, std::vector& out_decoded) { - // decode base64 encoded signature +bool OpenSSLSupplier::encode_base64_bytes(const std::vector& bytes, std::string& out_encoded) { + EVP_ENCODE_CTX_ptr base64_encode_context_ptr(EVP_ENCODE_CTX_new()); + if (!base64_encode_context_ptr.get()) { + EVLOG_error << "Error during EVP_ENCODE_CTX_new"; + return false; + } + + EVP_EncodeInit(base64_encode_context_ptr.get()); + if (!base64_encode_context_ptr.get()) { + EVLOG_error << "Error during EVP_EncodeInit"; + return false; + } + + const unsigned char* bytes_str = reinterpret_cast(bytes.data()); + + int base64_length = ((bytes.size() * 4) / 3) + 1; + char base64_out[base64_length]; // If it causes issues, replace with 'alloca' on diff platform + + int base64_out_length; + if (EVP_EncodeUpdate(base64_encode_context_ptr.get(), reinterpret_cast(base64_out), + &base64_out_length, bytes_str, base64_length) < 0) { + EVLOG_error << "Error during EVP_EncodeUpdate"; + return false; + } + + int encode_final_out; + EVP_EncodeFinal(base64_encode_context_ptr.get(), reinterpret_cast(base64_out), &encode_final_out); + + out_encoded.clear(); + out_encoded.insert(std::end(out_encoded), base64_out, base64_out + base64_out_length); + + return true; +} + +bool OpenSSLSupplier::decode_base64_string(const std::string& string, std::vector& out_decoded) { EVP_ENCODE_CTX_ptr base64_decode_context_ptr(EVP_ENCODE_CTX_new()); if (!base64_decode_context_ptr.get()) { EVLOG_error << "Error during EVP_ENCODE_CTX_new"; @@ -864,26 +897,26 @@ bool OpenSSLSupplier::decode_base64_signature(const std::string& signature, std: return false; } - const unsigned char* signature_str = reinterpret_cast(signature.data()); - int base64_length = signature.size(); - std::byte signature_out[base64_length]; + const unsigned char* encoded_str = reinterpret_cast(string.data()); + int base64_length = string.size(); + std::byte decoded_out[base64_length]; - int signature_out_length; - if (EVP_DecodeUpdate(base64_decode_context_ptr.get(), reinterpret_cast(signature_out), - &signature_out_length, signature_str, base64_length) < 0) { + int decoded_out_length; + if (EVP_DecodeUpdate(base64_decode_context_ptr.get(), reinterpret_cast(decoded_out), + &decoded_out_length, encoded_str, base64_length) < 0) { EVLOG_error << "Error during DecodeUpdate"; return false; } int decode_final_out; - if (EVP_DecodeFinal(base64_decode_context_ptr.get(), reinterpret_cast(signature_out), + if (EVP_DecodeFinal(base64_decode_context_ptr.get(), reinterpret_cast(decoded_out), &decode_final_out) < 0) { EVLOG_error << "Error during EVP_DecodeFinal"; return false; } out_decoded.clear(); - out_decoded.insert(std::end(out_decoded), signature_out, signature_out + signature_out_length); + out_decoded.insert(std::end(out_decoded), decoded_out, decoded_out + decoded_out_length); return true; } diff --git a/lib/evse_security/evse_security.cpp b/lib/evse_security/evse_security.cpp index 0cf71ce..b223fd7 100644 --- a/lib/evse_security/evse_security.cpp +++ b/lib/evse_security/evse_security.cpp @@ -719,14 +719,14 @@ void EvseSecurity::update_ocsp_cache(const CertificateHashData& certificate_hash try { X509CertificateBundle ca_bundle(ca_bundle_path, EncodingFormat::PEM); - auto &certificate_hierarchy = ca_bundle.get_certficate_hierarchy(); + auto& certificate_hierarchy = ca_bundle.get_certficate_hierarchy(); try { // Find the certificate X509Wrapper cert = certificate_hierarchy.find_certificate(certificate_hash_data); EVLOG_debug << "Writing OCSP Response to filesystem"; - if (cert.get_file().has_value()) { + 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); @@ -739,7 +739,7 @@ void EvseSecurity::update_ocsp_cache(const CertificateHashData& certificate_hash fs << ocsp_response; fs.close(); } - } catch(const NoCertificateFound& e) { + } catch (const NoCertificateFound& e) { EVLOG_error << "Could not find any certificate for ocsp cache update: " << e.what(); } } catch (const CertificateLoadException& e) { @@ -755,19 +755,19 @@ std::optional EvseSecurity::retrieve_ocsp_cache(const CertificateHa try { X509CertificateBundle ca_bundle(ca_bundle_path, EncodingFormat::PEM); - auto &certificate_hierarchy = ca_bundle.get_certficate_hierarchy(); + auto& certificate_hierarchy = ca_bundle.get_certficate_hierarchy(); try { // Find the certificate X509Wrapper cert = certificate_hierarchy.find_certificate(certificate_hash_data); EVLOG_debug << "Reading OCSP Response from filesystem"; - if (cert.get_file().has_value()) { + 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)) { + + if (fs::exists(ocsp_file_path)) { std::ifstream in_fs(ocsp_file_path.c_str()); std::string ocsp_response; @@ -777,7 +777,7 @@ std::optional EvseSecurity::retrieve_ocsp_cache(const CertificateHa return std::make_optional(std::move(ocsp_response)); } } - } catch(const NoCertificateFound& e) { + } catch (const NoCertificateFound& e) { EVLOG_error << "Could not find any certificate for ocsp cache retrieve: " << e.what(); } } catch (const CertificateLoadException& e) { @@ -1153,7 +1153,7 @@ bool EvseSecurity::verify_file_signature(const fs::path& path, const std::string std::vector signature_decoded; - if (false == CryptoSupplier::decode_base64_signature(signature, signature_decoded)) { + if (false == CryptoSupplier::decode_base64_string(signature, signature_decoded)) { EVLOG_error << "Error during decoding signature: " << signature; return false; } @@ -1176,6 +1176,26 @@ bool EvseSecurity::verify_file_signature(const fs::path& path, const std::string return false; } +std::vector EvseSecurity::base64_decode(const std::string& encoded_string) { + std::vector decoded_bytes; + + if (false == CryptoSupplier::decode_base64_string(encoded_string, decoded_bytes)) { + return {}; + } + + return decoded_bytes; +} + +std::string EvseSecurity::base64_encode(const std::vector& bytes) { + std::string encoded_string; + + if (false == CryptoSupplier::encode_base64_bytes(bytes, encoded_string)) { + return {}; + } + + return encoded_string; +} + CertificateValidationResult EvseSecurity::verify_certificate(const std::string& certificate_chain, LeafCertificateType certificate_type) { std::lock_guard guard(EvseSecurity::security_mutex);