/** * @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 * * Part of the Dark Horse Linux Package Manager (DPM) */ #include "checksums.hpp" #include #include #include #include #include #include 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(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(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(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(hash[i]); } return ss.str(); }