implemented configurable checksum algorithm, defaults to sha256, introduces openssl dependency to build module
parent
1f4f73ff0f
commit
aad077a24a
|
@ -0,0 +1,2 @@
|
||||||
|
[cryptography]
|
||||||
|
checksum_algorithm=sha256
|
|
@ -10,6 +10,9 @@ else()
|
||||||
set(DPM_ROOT_DIR "${CMAKE_SOURCE_DIR}")
|
set(DPM_ROOT_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Find OpenSSL
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
# Module version - used by DPM
|
# Module version - used by DPM
|
||||||
add_library(build MODULE
|
add_library(build MODULE
|
||||||
build.cpp
|
build.cpp
|
||||||
|
@ -17,6 +20,7 @@ add_library(build MODULE
|
||||||
src/cli_parsers.cpp
|
src/cli_parsers.cpp
|
||||||
src/commands.cpp
|
src/commands.cpp
|
||||||
src/package_staging.cpp
|
src/package_staging.cpp
|
||||||
|
src/checksums.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set output properties
|
# Set output properties
|
||||||
|
@ -30,10 +34,11 @@ set_target_properties(
|
||||||
target_include_directories(build PRIVATE
|
target_include_directories(build PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
${DPM_ROOT_DIR}
|
${DPM_ROOT_DIR}
|
||||||
|
${OPENSSL_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Link with filesystem library
|
# Link with filesystem library and OpenSSL
|
||||||
target_link_libraries(build stdc++fs)
|
target_link_libraries(build stdc++fs ${OPENSSL_LIBRARIES})
|
||||||
|
|
||||||
# Standalone version - used for debugging
|
# Standalone version - used for debugging
|
||||||
add_executable(build_standalone
|
add_executable(build_standalone
|
||||||
|
@ -42,6 +47,7 @@ add_executable(build_standalone
|
||||||
src/cli_parsers.cpp
|
src/cli_parsers.cpp
|
||||||
src/commands.cpp
|
src/commands.cpp
|
||||||
src/package_staging.cpp
|
src/package_staging.cpp
|
||||||
|
src/checksums.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Define the BUILD_STANDALONE macro for the standalone build
|
# Define the BUILD_STANDALONE macro for the standalone build
|
||||||
|
@ -51,10 +57,11 @@ target_compile_definitions(build_standalone PRIVATE BUILD_STANDALONE)
|
||||||
target_include_directories(build_standalone PRIVATE
|
target_include_directories(build_standalone PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
${DPM_ROOT_DIR}
|
${DPM_ROOT_DIR}
|
||||||
|
${OPENSSL_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Link with filesystem library for standalone
|
# Link with filesystem library and OpenSSL for standalone
|
||||||
target_link_libraries(build_standalone stdc++fs)
|
target_link_libraries(build_standalone stdc++fs ${OPENSSL_LIBRARIES})
|
||||||
|
|
||||||
# Set the output name for the standalone executable
|
# Set the output name for the standalone executable
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* @file checksums.hpp
|
||||||
|
* @brief Functions for generating cryptographic checksums
|
||||||
|
*
|
||||||
|
* Provides 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)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the configured hash algorithm or defaults to SHA-256
|
||||||
|
*
|
||||||
|
* Retrieves the hash algorithm configured in the cryptography section
|
||||||
|
* or defaults to SHA-256 if not specified.
|
||||||
|
*
|
||||||
|
* @return String containing the name of the hash algorithm to use
|
||||||
|
*/
|
||||||
|
std::string get_configured_hash_algorithm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a list of available digest algorithms from OpenSSL
|
||||||
|
*
|
||||||
|
* Retrieves a list of supported digest algorithms from OpenSSL's
|
||||||
|
* internal algorithms list.
|
||||||
|
*
|
||||||
|
* @return String containing comma-separated list of available algorithms
|
||||||
|
*/
|
||||||
|
std::string get_available_algorithms();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generates a file checksum using the configured hashing algorithm
|
||||||
|
*
|
||||||
|
* Uses OpenSSL to calculate a cryptographic hash of a file's contents
|
||||||
|
* based on the algorithm specified in the configuration.
|
||||||
|
* This method reads the file in chunks to handle large files efficiently.
|
||||||
|
*
|
||||||
|
* @param file_path Path to the file to be hashed
|
||||||
|
* @return String containing the hexadecimal representation of the checksum, or empty string on error
|
||||||
|
*/
|
||||||
|
std::string generate_file_checksum(const std::filesystem::path& file_path);
|
|
@ -19,6 +19,7 @@
|
||||||
#include <dpmdk/include/CommonModuleAPI.hpp>
|
#include <dpmdk/include/CommonModuleAPI.hpp>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
#include "checksums.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Stages a DPM package
|
* @brief Stages a DPM package
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
/**
|
||||||
|
* @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();
|
||||||
|
}
|
|
@ -345,6 +345,10 @@ static bool update_contents_manifest(const std::filesystem::path& package_dir)
|
||||||
std::filesystem::path contents_dir = package_dir / "contents";
|
std::filesystem::path contents_dir = package_dir / "contents";
|
||||||
std::filesystem::path manifest_path = package_dir / "metadata" / "CONTENTS_MANIFEST_DIGEST";
|
std::filesystem::path manifest_path = package_dir / "metadata" / "CONTENTS_MANIFEST_DIGEST";
|
||||||
|
|
||||||
|
// Log which hash algorithm is being used
|
||||||
|
std::string hash_algorithm = get_configured_hash_algorithm();
|
||||||
|
dpm_log(LOG_INFO, ("Generating contents manifest using " + hash_algorithm + " checksums...").c_str());
|
||||||
|
|
||||||
// Open manifest file for writing
|
// Open manifest file for writing
|
||||||
std::ofstream manifest_file(manifest_path);
|
std::ofstream manifest_file(manifest_path);
|
||||||
if (!manifest_file.is_open()) {
|
if (!manifest_file.is_open()) {
|
||||||
|
@ -352,8 +356,6 @@ static bool update_contents_manifest(const std::filesystem::path& package_dir)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dpm_log(LOG_INFO, "Generating contents manifest...");
|
|
||||||
|
|
||||||
// Process each file in the contents directory recursively
|
// Process each file in the contents directory recursively
|
||||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(contents_dir)) {
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(contents_dir)) {
|
||||||
// Skip directories, we only need to record files
|
// Skip directories, we only need to record files
|
||||||
|
@ -369,8 +371,8 @@ static bool update_contents_manifest(const std::filesystem::path& package_dir)
|
||||||
// Get file stats for permissions
|
// Get file stats for permissions
|
||||||
struct stat file_stat;
|
struct stat file_stat;
|
||||||
if (stat(file_path.c_str(), &file_stat) != 0) {
|
if (stat(file_path.c_str(), &file_stat) != 0) {
|
||||||
dpm_log(LOG_ERROR, ("Failed to get file stats for: " + file_path.string()).c_str());
|
dpm_log(LOG_FATAL, ("Failed to get file stats for: " + file_path.string()).c_str());
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format permissions as octal
|
// Format permissions as octal
|
||||||
|
@ -397,8 +399,12 @@ static bool update_contents_manifest(const std::filesystem::path& package_dir)
|
||||||
|
|
||||||
std::string ownership = owner + ":" + group;
|
std::string ownership = owner + ":" + group;
|
||||||
|
|
||||||
// Calculate file checksum (placeholder - would normally use SHA-256)
|
// Calculate file checksum using the configured algorithm
|
||||||
std::string checksum = "CHECKSUM_PLACEHOLDER"; // Actual hash calculation would be here
|
std::string checksum = generate_file_checksum(file_path);
|
||||||
|
if (checksum.empty()) {
|
||||||
|
dpm_log(LOG_FATAL, ("Failed to generate checksum for: " + file_path.string()).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// By default, mark all files as controlled ('C')
|
// By default, mark all files as controlled ('C')
|
||||||
char control_designation = 'C';
|
char control_designation = 'C';
|
||||||
|
|
Loading…
Reference in New Issue