Adds the sdi_toolBox library (temporary version)
This commit is contained in:
81
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/base64.h
Normal file
81
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/base64.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Base64
|
||||
{
|
||||
public:
|
||||
Base64() = default; // Default constructor
|
||||
virtual ~Base64() = default; // Default destructor
|
||||
Base64(const Base64 &obj) = delete; // Copy constructor
|
||||
Base64(Base64 &&obj) noexcept = delete; // Move constructor
|
||||
Base64 &operator=(const Base64 &obj) = delete; // Copy assignment operator
|
||||
Base64 &operator=(Base64 &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
static std::string encode(const std::span<uint8_t> &binary_data); // Encodes binary data into a Base64 string
|
||||
static std::vector<uint8_t> decode(const std::string &base64_string); // Decodes a Base64 string into binary data
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Encodes binary data into a Base64 string */
|
||||
inline std::string Base64::encode(const std::span<uint8_t> &binary_data)
|
||||
{
|
||||
if (binary_data.empty())
|
||||
return "";
|
||||
|
||||
// Calculate the length of the encoded data
|
||||
const size_t output_len_max = EVP_ENCODE_LENGTH(binary_data.size());
|
||||
|
||||
// Allocate output buffer (with null terminator)
|
||||
std::vector<char> output_buffer(output_len_max + 1, 0);
|
||||
|
||||
// OpenSSL encoding
|
||||
const auto final_len = EVP_EncodeBlock(reinterpret_cast<uint8_t *>(output_buffer.data()),
|
||||
binary_data.data(),
|
||||
binary_data.size());
|
||||
if (final_len < 0)
|
||||
throw std::runtime_error("Error: Base64 encoding failed");
|
||||
|
||||
return std::string(output_buffer.data());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Decodes a Base64 string into binary data */
|
||||
inline std::vector<uint8_t> Base64::decode(const std::string &base64_string)
|
||||
{
|
||||
if (base64_string.empty())
|
||||
return {};
|
||||
|
||||
// Calculate the length of the decoded data
|
||||
const auto output_len_max = EVP_DECODE_LENGTH(base64_string.size());
|
||||
|
||||
// Allocate output buffer
|
||||
std::vector<uint8_t> output_buffer(output_len_max);
|
||||
|
||||
// OpenSSL decoding
|
||||
const auto final_len = EVP_DecodeBlock(output_buffer.data(),
|
||||
reinterpret_cast<const unsigned char *>(base64_string.data()),
|
||||
static_cast<int>(base64_string.size()));
|
||||
if (final_len < 0)
|
||||
throw std::runtime_error("Error: Base64 decoding failed: Invalid data");
|
||||
output_buffer.resize(final_len);
|
||||
|
||||
return output_buffer;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
71
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/data.h
Normal file
71
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/data.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Data8
|
||||
{
|
||||
public:
|
||||
Data8() = default; // Default constructor
|
||||
explicit Data8(const std::string &data); // Constructor from string
|
||||
explicit Data8(const std::span<const uint8_t> &data); // Constructor from byte span
|
||||
virtual ~Data8() = default; // Default destructor
|
||||
Data8(const Data8 &obj) = delete; // Copy constructor
|
||||
Data8(Data8 &&obj) noexcept = delete; // Move constructor
|
||||
Data8 &operator=(const Data8 &obj) = delete; // Copy assignment operator
|
||||
Data8 &operator=(Data8 &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
[[nodiscard]] std::string getString() const; // Get data as string
|
||||
[[nodiscard]] std::span<const uint8_t> getBytes() const; // Get data as byte span
|
||||
[[nodiscard]] size_t getSize() const; // Get data size
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> m_data; // Raw data storage
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor from string */
|
||||
inline Data8::Data8(const std::string &data)
|
||||
{
|
||||
m_data = std::vector<uint8_t>(data.begin(), data.end());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline Data8::Data8(const std::span<const uint8_t> &data)
|
||||
{
|
||||
m_data = std::vector<uint8_t>(data.begin(), data.end());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get data as string */
|
||||
inline std::string Data8::getString() const
|
||||
{
|
||||
return std::string(m_data.begin(), m_data.end());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get data as byte span */
|
||||
inline std::span<const uint8_t> Data8::getBytes() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get data size */
|
||||
inline size_t Data8::getSize() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
78
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalHash.h
Normal file
78
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalHash.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <openssl/evp.h>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class DigitalHash
|
||||
{
|
||||
public:
|
||||
DigitalHash() = default; // Default constructor
|
||||
virtual ~DigitalHash() = default; // Default destructor
|
||||
DigitalHash(const DigitalHash &obj) = delete; // Copy constructor
|
||||
DigitalHash(DigitalHash &&obj) noexcept = delete; // Move constructor
|
||||
DigitalHash &operator=(const DigitalHash &obj) = delete; // Copy assignment operator
|
||||
DigitalHash &operator=(DigitalHash &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
static std::string calculateDataHash(const std::span<const uint8_t> &data, const EVP_MD *md_type); // Calculate digital hash from data buffer
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Calculate digital hash from data buffer */
|
||||
inline std::string DigitalHash::calculateDataHash(const std::span<const uint8_t> &data, const EVP_MD *md_type)
|
||||
{
|
||||
// Initialize OpenSSL digest context
|
||||
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
|
||||
if (!mdctx)
|
||||
throw std::runtime_error("Error: Failed to create EVP_MD_CTX");
|
||||
|
||||
// Initialize digest operation
|
||||
if (EVP_DigestInit_ex(mdctx, md_type, nullptr) != 1)
|
||||
{
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("Error: EVP_DigestInit_ex failed");
|
||||
}
|
||||
|
||||
// Update digest with data
|
||||
if (EVP_DigestUpdate(mdctx, data.data(), data.size()) != 1)
|
||||
{
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("Error: EVP_DigestUpdate failed");
|
||||
}
|
||||
|
||||
// Finalize digest computation
|
||||
std::vector<uint8_t> hash(EVP_MD_size(md_type));
|
||||
unsigned int hash_len = 0;
|
||||
if (EVP_DigestFinal_ex(mdctx, hash.data(), &hash_len) != 1)
|
||||
{
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("Error: EVP_DigestFinal_ex failed");
|
||||
}
|
||||
|
||||
// Clean up
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
|
||||
// Convert hash to hexadecimal string
|
||||
std::string hash_hex;
|
||||
for (unsigned int i = 0; i < hash_len; ++i)
|
||||
hash_hex += std::format("{:02x}", hash[i]);
|
||||
|
||||
return hash_hex;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
255
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalSign.h
Normal file
255
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalSign.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class DigitalSign
|
||||
{
|
||||
// Custom deleter for EVP_PKEY to ensure it's automatically freed
|
||||
struct EVP_PKEY_Deleter
|
||||
{
|
||||
void operator()(EVP_PKEY *p) const
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
EVP_PKEY_free(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
using pEVP_PKEY = std::unique_ptr<EVP_PKEY, EVP_PKEY_Deleter>;
|
||||
|
||||
struct BIO_Deleter
|
||||
{
|
||||
void operator()(BIO *b) const
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
BIO_free(b);
|
||||
}
|
||||
}
|
||||
};
|
||||
using pBIO = std::unique_ptr<BIO, BIO_Deleter>;
|
||||
|
||||
struct EVP_MD_CTX_Deleter
|
||||
{
|
||||
void operator()(EVP_MD_CTX *ctx) const
|
||||
{
|
||||
if (ctx)
|
||||
{
|
||||
EVP_MD_CTX_free(ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
using pEVP_MD_CTX = std::unique_ptr<EVP_MD_CTX, EVP_MD_CTX_Deleter>;
|
||||
|
||||
public:
|
||||
DigitalSign() = default; // Default constructor
|
||||
virtual ~DigitalSign() = default; // Default destructor
|
||||
DigitalSign(const DigitalSign &obj) = delete; // Copy constructor
|
||||
DigitalSign(DigitalSign &&obj) noexcept = delete; // Move constructor
|
||||
DigitalSign &operator=(const DigitalSign &obj) = delete; // Copy assignment operator
|
||||
DigitalSign &operator=(DigitalSign &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
void loadPEMKeyFromFile(const std::filesystem::path &filepath, bool is_private); // Load key from PEM file
|
||||
void loadPEMKeyFromMemory(const std::string &key, bool is_private); // Load key from memory
|
||||
|
||||
std::vector<uint8_t> sign(const Data8 &data, const EVP_MD *digest_type = EVP_sha256()) const; // Sign data with private key
|
||||
std::string signHex(const Data8 &data, const EVP_MD *digest_type = EVP_sha256()) const; // Sign data with private key
|
||||
|
||||
bool verify(const Data8 &data, const std::span<const uint8_t> &signature, const EVP_MD *digest_type = EVP_sha256()) const; // Verify signature with public key
|
||||
bool verify(const Data8 &data, const std::string &signature, const EVP_MD *digest_type = EVP_sha256()) const; // Verify signature with public key
|
||||
|
||||
protected:
|
||||
pEVP_PKEY m_privateKey;
|
||||
pEVP_PKEY m_publicKey;
|
||||
|
||||
private:
|
||||
static std::vector<uint8_t> hexToBytes(const std::string &hexString); // Converts a hexadecimal string into a vector of bytes
|
||||
static std::string bytesToHex(const std::span<const uint8_t> &bytes); // Converts a span of bytes into a hexadecimal string representation
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Load key from PEM file */
|
||||
inline void DigitalSign::loadPEMKeyFromFile(const std::filesystem::path &filepath, const bool is_private)
|
||||
{
|
||||
std::ifstream file(filepath, std::ifstream::binary);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Error: Could not open file " + filepath.string());
|
||||
|
||||
const std::string key((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
loadPEMKeyFromMemory(key, is_private);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Load key from memory */
|
||||
inline void DigitalSign::loadPEMKeyFromMemory(const std::string &key, const bool is_private)
|
||||
{
|
||||
if (key.empty())
|
||||
throw std::runtime_error("Error: Input stream is empty or could not be read");
|
||||
|
||||
// Create a memory BIO (Basic Input/Output) for reading the key
|
||||
const auto bio = static_cast<pBIO>(BIO_new_mem_buf(key.data(), static_cast<int>(key.size())));
|
||||
if (!bio)
|
||||
throw std::runtime_error("Error: Failed to create memory BIO");
|
||||
|
||||
// Use the BIO to read the key
|
||||
if (is_private)
|
||||
{
|
||||
m_privateKey = static_cast<pEVP_PKEY>(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
|
||||
if (!m_privateKey)
|
||||
throw std::runtime_error("Error: Failed to read private key from memory buffer");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_publicKey = static_cast<pEVP_PKEY>(PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr));
|
||||
if (!m_publicKey)
|
||||
throw std::runtime_error("Error: Failed to read private key from memory buffer");
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Sign data with private key */
|
||||
inline std::vector<uint8_t> DigitalSign::sign(const Data8 &data, const EVP_MD *digest_type) const
|
||||
{
|
||||
if (!m_privateKey)
|
||||
throw std::runtime_error("Error: Private key is not loaded for signing");
|
||||
|
||||
// Initialize the context for signing
|
||||
const auto md_ctx = static_cast<pEVP_MD_CTX>(EVP_MD_CTX_new());
|
||||
if (!md_ctx)
|
||||
throw std::runtime_error("Error: Failed to create EVP_MD_CTX");
|
||||
|
||||
// Set the context for signing with the private key and digest
|
||||
if (EVP_DigestSignInit(md_ctx.get(), nullptr, digest_type, nullptr, m_privateKey.get()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignInit failed");
|
||||
|
||||
// Provide the data to be signed
|
||||
if (EVP_DigestSignUpdate(md_ctx.get(), data.getBytes().data(), data.getSize()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignUpdate failed");
|
||||
|
||||
// Determine the required signature size
|
||||
size_t sigLen = 0;
|
||||
if (EVP_DigestSignFinal(md_ctx.get(), nullptr, &sigLen) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignFinal (size) failed");
|
||||
|
||||
// Allocate space and generate the signature
|
||||
std::vector<uint8_t> signature(sigLen);
|
||||
if (EVP_DigestSignFinal(md_ctx.get(), signature.data(), &sigLen) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignFinal (signature) failed");
|
||||
|
||||
// Resize signature to the actual length used
|
||||
signature.resize(sigLen);
|
||||
|
||||
return signature;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Sign data */
|
||||
inline std::string DigitalSign::signHex(const Data8 &data, const EVP_MD *digest_type) const
|
||||
{
|
||||
return bytesToHex(sign(data, digest_type));
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Verify signature with public key */
|
||||
inline bool DigitalSign::verify(const Data8 &data, const std::span<const uint8_t> &signature, const EVP_MD *digest_type) const
|
||||
{
|
||||
if (!m_publicKey)
|
||||
throw std::runtime_error("Error: Public key is not loaded for verifying");
|
||||
|
||||
// Initialize the context for verifying
|
||||
const auto md_ctx = static_cast<pEVP_MD_CTX>(EVP_MD_CTX_new());
|
||||
if (!md_ctx)
|
||||
throw std::runtime_error("Error: Failed to create EVP_MD_CTX");
|
||||
|
||||
// Set the context for verifying with the public key and digest
|
||||
if (EVP_DigestVerifyInit(md_ctx.get(), nullptr, digest_type, nullptr, m_publicKey.get()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestVerifyInit failed");
|
||||
|
||||
// Provide the original data
|
||||
if (EVP_DigestVerifyUpdate(md_ctx.get(), data.getBytes().data(), data.getSize()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestVerifyUpdate failed");
|
||||
|
||||
// Verify the signature
|
||||
// Returns 1 for a good signature, 0 for a bad signature, and -1 on error
|
||||
const auto result = EVP_DigestVerifyFinal(md_ctx.get(), signature.data(), signature.size());
|
||||
if (result == 1)
|
||||
return true; // Signature is valid
|
||||
if (result == 0)
|
||||
return false; // Signature is NOT valid
|
||||
|
||||
throw std::runtime_error("Error: EVP_DigestVerifyFinal failed during execution");
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Verify signature with public key */
|
||||
inline bool DigitalSign::verify(const Data8 &data, const std::string &signature, const EVP_MD *digest_type) const
|
||||
{
|
||||
return verify(data, hexToBytes(signature), digest_type);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Converts a hexadecimal string into a vector of bytes */
|
||||
inline std::vector<uint8_t> DigitalSign::hexToBytes(const std::string &hexString)
|
||||
{
|
||||
// A valid hex string must have an even length
|
||||
if (hexString.length() % 2 != 0)
|
||||
throw std::invalid_argument("Hex string must have an even length");
|
||||
|
||||
std::vector<uint8_t> bytes;
|
||||
bytes.reserve(hexString.length() / 2);
|
||||
|
||||
for (size_t i = 0; i < hexString.length(); i += 2)
|
||||
{
|
||||
std::string byteString = hexString.substr(i, 2);
|
||||
unsigned long byteVal;
|
||||
|
||||
try
|
||||
{
|
||||
byteVal = std::stoul(byteString, nullptr, 16);
|
||||
}
|
||||
catch (const std::exception & /*e*/)
|
||||
{
|
||||
throw std::invalid_argument("Invalid hexadecimal character sequence: " + byteString); // Non-hex characters ?
|
||||
}
|
||||
|
||||
// Check if the value fits in a uint8_t (should always be true for 2 chars)
|
||||
if (byteVal > 0xFF)
|
||||
throw std::invalid_argument("Internal error: Converted value exceeds 0xFF");
|
||||
|
||||
bytes.push_back(static_cast<uint8_t>(byteVal));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Converts a span of bytes into a hexadecimal string representation */
|
||||
inline std::string DigitalSign::bytesToHex(const std::span<const uint8_t> &bytes)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::uppercase << std::setfill('0');
|
||||
|
||||
for (const uint8_t byte : bytes)
|
||||
ss << std::format("{0:02X}", byte);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
Reference in New Issue
Block a user