Skip to content

Commit

Permalink
Added file signature validation
Browse files Browse the repository at this point in the history
Signed-off-by: Kai-Uwe Hermann <[email protected]>
  • Loading branch information
hikinggrass committed Oct 24, 2023
1 parent 1c3aadc commit d23b1de
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
8 changes: 8 additions & 0 deletions include/evse_security.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ class EvseSecurity {
/// @return day count until the leaf certificate expires
int get_leaf_expiry_days_count(LeafCertificateType certificate_type);

/// @brief Verifies the file at the given \p path using the provided \p signing_certificate and \p signature
/// @param path
/// @param signing_certificate
/// @param signature
/// @return true if the verification was successful, false if not
static bool verify_file_signature(const std::filesystem::path& path, const std::string& signing_certificate,
const std::string signature);

private:
// why not reusing the FilePaths here directly (storage duplication)
std::map<CaCertificateType, std::filesystem::path> ca_bundle_path_map;
Expand Down
34 changes: 33 additions & 1 deletion include/sec_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,54 @@ template <> class std::default_delete<EVP_PKEY> {
}
};

template <> class std::default_delete<EVP_PKEY_CTX> {
public:
void operator()(EVP_PKEY_CTX* ptr) const {
::EVP_PKEY_CTX_free(ptr);
}
};

template <> class std::default_delete<BIO> {
public:
void operator()(BIO* ptr) const {
::BIO_free(ptr);
}
};

template <> class std::default_delete<FILE> {
public:
void operator()(FILE* ptr) const {
::fclose(ptr);
}
};

template <> class std::default_delete<EVP_MD_CTX> {
public:
void operator()(EVP_MD_CTX* ptr) const {
::EVP_MD_CTX_destroy(ptr);
}
};

template <> class std::default_delete<EVP_ENCODE_CTX> {
public:
void operator()(EVP_ENCODE_CTX* ptr) const {
::EVP_ENCODE_CTX_free(ptr);
}
};

namespace evse_security {

using X509_ptr = std::unique_ptr<X509>;
using X509_STORE_ptr = std::unique_ptr<X509_STORE>;
using X509_STORE_CTX_ptr = std::unique_ptr<X509_STORE_CTX>;
using X509_REQ_ptr = std::unique_ptr<X509_REQ>;
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY>;
using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX>;
using BIO_ptr = std::unique_ptr<BIO>;
using FILE_ptr = std::unique_ptr<FILE>;
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX>;
using EVP_ENCODE_CTX_ptr = std::unique_ptr<EVP_ENCODE_CTX>;

} // namespace evse_security

#endif
#endif
110 changes: 110 additions & 0 deletions lib/evse_security.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
#include <iostream>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <openssl/x509v3.h>
#include <stdio.h>

#include <evse_utilities.hpp>
#include <x509_bundle.hpp>
Expand Down Expand Up @@ -622,6 +625,113 @@ int EvseSecurity::get_leaf_expiry_days_count(LeafCertificateType certificate_typ
return 0;
}

bool EvseSecurity::verify_file_signature(const std::filesystem::path& path, const std::string& signing_certificate,
const std::string signature) {
EVLOG_info << "Verifying file signature for " << path.string();

// calculate sha256 of file
FILE_ptr file_ptr(fopen(path.string().c_str(), "rb"));
if (!file_ptr.get()) {
EVLOG_error << "Could not open file at: " << path.string();
return false;
}
EVP_MD_CTX_ptr md_context_ptr(EVP_MD_CTX_create());
if (!md_context_ptr.get()) {
EVLOG_error << "Could not create EVP_MD_CTX";
return false;
}
const EVP_MD* md = EVP_get_digestbyname("SHA256");
if (EVP_DigestInit_ex(md_context_ptr.get(), md, nullptr) == 0) {
EVLOG_error << "Error during EVP_DigestInit_ex";
return false;
}
size_t in_length;
unsigned char file_buffer[BUFSIZ];
do {
in_length = fread(file_buffer, 1, BUFSIZ, file_ptr.get());
if (EVP_DigestUpdate(md_context_ptr.get(), file_buffer, in_length) == 0) {
EVLOG_error << "Error during EVP_DigestUpdate";
return false;
}
} while (in_length == BUFSIZ);
unsigned int sha256_out_length;
unsigned char sha256_out[EVP_MAX_MD_SIZE];
if (EVP_DigestFinal_ex(md_context_ptr.get(), sha256_out, &sha256_out_length) == 0) {
EVLOG_error << "Error during EVP_DigestFinal_ex";
return false;
}

// extract public key
BIO_ptr bio_signing_certificate_ptr(
BIO_new_mem_buf(signing_certificate.data(), static_cast<int>(signing_certificate.size())));
if (!bio_signing_certificate_ptr.get()) {
EVLOG_error << "Error during BIO_new_mem_buf";
return false;
}
X509_ptr x509_signing_cerificate_ptr(PEM_read_bio_X509(bio_signing_certificate_ptr.get(), nullptr, 0, nullptr));
if (!x509_signing_cerificate_ptr.get()) {
EVLOG_error << "Error during PEM_read_bio_X509";
return false;
}
EVP_PKEY_ptr public_key_ptr(X509_get_pubkey(x509_signing_cerificate_ptr.get()));
if (!public_key_ptr.get()) {
EVLOG_error << "Error during X509_get_pubkey";
return false;
}

// decode base64 encoded signature
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";
return false;
}
EVP_DecodeInit(base64_decode_context_ptr.get());
if (!base64_decode_context_ptr.get()) {
EVLOG_error << "Error during EVP_DecodeInit";
return false;
}
const unsigned char* signature_str = reinterpret_cast<const unsigned char*>(signature.data());
int base64_length = signature.size();
unsigned char signature_out[base64_length];
int signature_out_length;
if (EVP_DecodeUpdate(base64_decode_context_ptr.get(), signature_out, &signature_out_length, signature_str,
base64_length) < 0) {
EVLOG_error << "Error during DecodeUpdate";
return false;
}
int decode_final_out;
if (EVP_DecodeFinal(base64_decode_context_ptr.get(), signature_out, &decode_final_out) < 0) {
EVLOG_error << "Error during EVP_DecodeFinal";
return false;
}

// verify firmware signature
EVP_PKEY_CTX_ptr public_key_context_ptr(EVP_PKEY_CTX_new(public_key_ptr.get(), nullptr));
if (!public_key_context_ptr.get()) {
EVLOG_error << "Error setting up public key context";
}
if (EVP_PKEY_verify_init(public_key_context_ptr.get()) <= 0) {
EVLOG_error << "Error during EVP_PKEY_verify_init";
}
if (EVP_PKEY_CTX_set_signature_md(public_key_context_ptr.get(), EVP_sha256()) <= 0) {
EVLOG_error << "Error during EVP_PKEY_CTX_set_signature_md";
};
int result = EVP_PKEY_verify(public_key_context_ptr.get(), reinterpret_cast<const unsigned char*>(signature_out),
signature_out_length, sha256_out, sha256_out_length);

EVP_cleanup();

if (result != 1) {
EVLOG_error << "Failure to verify: " << result;
return false;
} else {
EVLOG_error << "Succesful verification";
return true;
}

return false;
}

InstallCertificateResult EvseSecurity::verify_certificate(const std::string& certificate_chain,
LeafCertificateType certificate_type) {
try {
Expand Down

0 comments on commit d23b1de

Please sign in to comment.