139 lines
4.2 KiB
C++
139 lines
4.2 KiB
C++
/**
|
|
* @file checksums.cpp
|
|
* @brief Implementation of cryptographic checksum functions
|
|
*
|
|
* Implements functionality for generating checksums of files using
|
|
* configurable cryptographic hash algorithms via OpenSSL.
|
|
*
|
|
* @copyright Copyright (c) 2025 SILO GROUP LLC
|
|
* @author Chris Punches <chris.punches@silogroup.org>
|
|
*
|
|
* Part of the Dark Horse Linux Package Manager (DPM)
|
|
*/
|
|
|
|
#include "checksums.hpp"
|
|
#include <openssl/evp.h>
|
|
#include <openssl/objects.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <dpmdk/include/CommonModuleAPI.hpp>
|
|
|
|
std::string get_configured_hash_algorithm()
|
|
{
|
|
const char* algorithm = dpm_get_config("cryptography", "checksum_algorithm");
|
|
if (algorithm) {
|
|
return std::string(algorithm);
|
|
}
|
|
return "sha256"; // Default to SHA-256
|
|
}
|
|
|
|
std::string get_available_algorithms()
|
|
{
|
|
std::stringstream algorithms;
|
|
|
|
// The OBJ_NAME_do_all_sorted will call the supplied callback function
|
|
// for each algorithm
|
|
struct AlgorithmCollector {
|
|
static void callback(const OBJ_NAME* obj, void* arg) {
|
|
if (obj->type == OBJ_NAME_TYPE_MD_METH) {
|
|
std::stringstream* ss = static_cast<std::stringstream*>(arg);
|
|
if (!ss->str().empty()) {
|
|
*ss << ", ";
|
|
}
|
|
*ss << obj->name;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Initialize OpenSSL objects
|
|
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
|
|
|
|
// Collect all digest algorithm names
|
|
OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
|
|
reinterpret_cast<void (*)(const OBJ_NAME*, void*)>(AlgorithmCollector::callback),
|
|
&algorithms);
|
|
|
|
return algorithms.str();
|
|
}
|
|
|
|
std::string generate_file_checksum(const std::filesystem::path& file_path)
|
|
{
|
|
// Get configured algorithm
|
|
std::string algorithm_name = get_configured_hash_algorithm();
|
|
dpm_log(LOG_DEBUG, ("Using hash algorithm: " + algorithm_name).c_str());
|
|
|
|
// Initialize OpenSSL EVP context
|
|
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
|
|
if (!mdctx) {
|
|
dpm_log(LOG_ERROR, "Failed to create OpenSSL EVP context");
|
|
return "";
|
|
}
|
|
|
|
// Let OpenSSL look up the algorithm by name
|
|
const EVP_MD* md = EVP_get_digestbyname(algorithm_name.c_str());
|
|
if (!md) {
|
|
std::string available_algorithms = get_available_algorithms();
|
|
std::string error_msg = "Hash algorithm not supported by OpenSSL: " + algorithm_name +
|
|
". Please check your cryptography.checksum_algorithm configuration.\n" +
|
|
"Available algorithms: " + available_algorithms;
|
|
dpm_log(LOG_FATAL, error_msg.c_str());
|
|
EVP_MD_CTX_free(mdctx);
|
|
return "";
|
|
}
|
|
|
|
if (EVP_DigestInit_ex(mdctx, md, nullptr) != 1) {
|
|
dpm_log(LOG_ERROR, "Failed to initialize digest context");
|
|
EVP_MD_CTX_free(mdctx);
|
|
return "";
|
|
}
|
|
|
|
// Open the file for reading in binary mode
|
|
std::ifstream file(file_path, std::ios::binary);
|
|
if (!file) {
|
|
dpm_log(LOG_ERROR, ("Failed to open file for checksum: " + file_path.string()).c_str());
|
|
EVP_MD_CTX_free(mdctx);
|
|
return "";
|
|
}
|
|
|
|
// Buffer for reading file chunks
|
|
const size_t buffer_size = 8192; // 8KB chunks
|
|
unsigned char buffer[buffer_size];
|
|
|
|
// Read and update digest
|
|
while (file) {
|
|
file.read(reinterpret_cast<char*>(buffer), buffer_size);
|
|
size_t bytes_read = file.gcount();
|
|
|
|
if (bytes_read > 0) {
|
|
if (EVP_DigestUpdate(mdctx, buffer, bytes_read) != 1) {
|
|
dpm_log(LOG_ERROR, "Failed to update digest");
|
|
EVP_MD_CTX_free(mdctx);
|
|
file.close();
|
|
return "";
|
|
}
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
|
|
// Finalize the digest
|
|
unsigned char hash[EVP_MAX_MD_SIZE];
|
|
unsigned int hash_len = 0;
|
|
|
|
if (EVP_DigestFinal_ex(mdctx, hash, &hash_len) != 1) {
|
|
dpm_log(LOG_ERROR, "Failed to finalize digest");
|
|
EVP_MD_CTX_free(mdctx);
|
|
return "";
|
|
}
|
|
|
|
EVP_MD_CTX_free(mdctx);
|
|
|
|
// Convert binary hash to hexadecimal string
|
|
std::stringstream ss;
|
|
for (unsigned int i = 0; i < hash_len; i++) {
|
|
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
|
|
}
|
|
|
|
return ss.str();
|
|
} |