Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update for v2g module interoperability: #8

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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