Skip to content

Commit

Permalink
Update for v2g module interoperability:
Browse files Browse the repository at this point in the history
- install certificate check if already contained
- last valid certificate retrieval move in bundle helper
- added certificate directory helper
- added support for the case when the V2G root doesn't contain the full chain

Signed-off-by: ioanbogdan <[email protected]>
  • Loading branch information
ioanbogdan committed Oct 19, 2023
1 parent 5188e4e commit 59499a3
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 153 deletions.
13 changes: 0 additions & 13 deletions include/evse_security.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,6 @@ class EvseSecurity {
std::optional<std::string> private_key_password; // used to decrypt encrypted private keys;
};

/// @brief Custom exception that is thrown when no private key could be found for a selected certificate
class NoPrivateKeyException : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};

/// @brief Custom exception that is thrown when no valid certificate could be found for the specified filesystem
/// locations
class NoCertificateValidException : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};

} // namespace evse_security

#endif // EVSE_SECURITY_HPP
20 changes: 17 additions & 3 deletions include/evse_utilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include <limits>
#include <random>

#include <everest/logging.hpp>
namespace evse_security {
Expand All @@ -16,6 +18,7 @@ class EvseUtils {
if (std::filesystem::is_regular_file(file_path))
return std::filesystem::remove(file_path);

EVLOG_error << "Error deleting file: " << file_path;
return false;
}

Expand All @@ -29,6 +32,7 @@ class EvseUtils {
}
}

EVLOG_error << "Error reading file: " << file_path;
return false;
}

Expand Down Expand Up @@ -56,10 +60,20 @@ class EvseUtils {
}

static std::string get_random_file_name(const std::string& extension) {
char path[] = "XXXXXX";
mktemp(path);
static std::random_device rd;
static std::mt19937 generator(rd());
static std::uniform_int_distribution<int> distribution(1, std::numeric_limits<int>::max());

return std::string(path) + extension;
static int increment = 0;

std::ostringstream buff;

auto now = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(now);
buff << std::put_time(std::gmtime(&time), "%m_%d_%Y_%H_%M_%S_") << std::to_string(++increment) << "_"
<< distribution(generator) << extension;

return buff.str();
}
};

Expand Down
92 changes: 86 additions & 6 deletions include/x509_bundle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,86 @@

namespace evse_security {

/// @brief Custom exception that is thrown when no private key could be found for a selected certificate
class NoPrivateKeyException : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};

/// @brief Custom exception that is thrown when no valid certificate could be found for the specified filesystem
/// locations
class NoCertificateValidException : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};

/// @brief X509 certificate bundle, used for holding multiple X509Wrappers. Supports
/// operations like add/delete importing/exporting certificates
/// operations like add/delete importing/exporting certificates. Can use either a
/// directory with multiple certificates or a single file with one or more certificates
/// in it. A directory that contains certificate bundle files will not work, the entry
/// will be ignored
class X509CertificateBundle {
public:
X509CertificateBundle(const std::filesystem::path& path, const EncodingFormat encoding);
X509CertificateBundle(const std::string& certificate, const EncodingFormat encoding);

X509CertificateBundle(X509CertificateBundle&& other) = default;
X509CertificateBundle(const X509CertificateBundle& other) = delete;

public:
/// @brief Gets if this certificate bundle comes from a single certificate bundle file
/// @return
bool is_using_bundle_file() {
bool is_using_bundle_file() const {
return (source == X509CertificateSource::FILE);
}

/// @brief Gets if this certificate bundle comes from an entire directory
/// @return
bool is_using_directory() {
bool is_using_directory() const {
return (source == X509CertificateSource::DIRECTORY);
}

/// @return True if multiple certificates are contained within
bool is_bundle() const {
return (get_certificate_count() > 1);
}

bool empty() const {
return certificates.empty();
}

/// @return Contained certificate count
int get_certificate_count() const {
return certificates.size();
}

std::filesystem::path get_path() const {
return path;
}

X509CertificateSource get_source() const {
return source;
}

public:
/// @brief Splits the certificate (chain) into single certificates
/// @return vector containing single certificates
std::vector<X509Wrapper> split();

/// @brief If we already have the certificate
bool contains_certificate(const X509Wrapper& certificate);
bool contains_certificate(const X509Wrapper& certificate) const;
/// @brief If we already have the certificate
bool contains_certificate(const CertificateHashData& certificate_hash);
bool contains_certificate(const CertificateHashData& certificate_hash) const;

/// @brief Updates a single certificate in the chain. Only in memory, use @ref export_certificates to filesystem
void add_certificate(X509Wrapper&& certificate);

/// @brief Updates a single certificate in the chain. Only in memory, use @ref export_certificates to filesystem
/// export
/// @param certificate certificate to update
/// @return true if the certificate was found and updated, false otherwise. If true is returned the provided
/// certificate is invalidated
bool update_certificate(X509Wrapper& certificate);
bool update_certificate(X509Wrapper&& certificate);

/// @brief Deletes a single certificate in the chain. Only in memory, use @ref export_certificates to filesystem
/// export
Expand All @@ -68,7 +112,21 @@ class X509CertificateBundle {
bool sync_to_certificate_store();

public:
const X509Wrapper& get_at(int index) {
return certificates.at(index);
}

/// @brief returns the latest valid certificate within this bundle
X509Wrapper get_latest_valid_certificate();

public:
X509CertificateBundle& operator=(X509CertificateBundle&& other) = default;

public:
/// @brief Loads all certificates from the string data that can contain multiple cetifs
static std::vector<X509_ptr> load_certificates(const std::string& data, const EncodingFormat encoding);
/// @brief Returns the latest valid certif that we might contain
static X509Wrapper get_latest_valid_certificate(const std::vector<X509Wrapper>& certificates);

static bool is_certificate_file(const std::filesystem::path& file) {
return std::filesystem::is_regular_file(file) &&
Expand All @@ -90,6 +148,28 @@ class X509CertificateBundle {
X509CertificateSource source;
};

/// @brief Unlike the bundle, this is loaded only from a directory that can contain a
/// combination of bundle files and single files. Used for easier operations on the
/// directory structures. All bundles will use individual files instead of directories
class X509CertificateDirectory {
public:
X509CertificateDirectory(const std::filesystem::path& directory);

public:
/// @brief Iterates through all the contained bundles, while the provided function
/// returns true
template <typename function> void for_each(function func) {
for (const auto& bundle : bundles) {
if (!func(bundle))
break;
}
}

private:
std::vector<X509CertificateBundle> bundles;
std::filesystem::path directory;
};

} // namespace evse_security

#endif // X509_BUNDLE_HPP
5 changes: 5 additions & 0 deletions include/x509_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const std::filesystem::path PEM_EXTENSION = ".pem";
const std::filesystem::path DER_EXTENSION = ".der";
const std::filesystem::path KEY_EXTENSION = ".key";

using ossl_days_to_seconds = std::chrono::duration<std::int64_t, std::ratio<86400>>;

/// @brief Convenience wrapper around openssl X509 certificate
class X509Wrapper {
public:
Expand Down Expand Up @@ -104,6 +106,9 @@ class X509Wrapper {
/// @return
std::string get_export_string() const;

/// @brief If the certificate is within the validity date
bool is_valid() const;

public:
X509Wrapper& operator=(X509Wrapper&& other) = default;

Expand Down
Loading

0 comments on commit 59499a3

Please sign in to comment.