Files
dpm-core/modules/build/src/checksums.cpp

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();
}