diff --git a/data/security.conf b/data/security.conf new file mode 100644 index 0000000..a85ec07 --- /dev/null +++ b/data/security.conf @@ -0,0 +1,2 @@ +[cryptography] +checksum_algorithm=sha256 \ No newline at end of file diff --git a/modules/build/CMakeLists.txt b/modules/build/CMakeLists.txt index 4741393..f62e1c8 100644 --- a/modules/build/CMakeLists.txt +++ b/modules/build/CMakeLists.txt @@ -10,6 +10,9 @@ else() set(DPM_ROOT_DIR "${CMAKE_SOURCE_DIR}") endif() +# Find OpenSSL +find_package(OpenSSL REQUIRED) + # Module version - used by DPM add_library(build MODULE build.cpp @@ -17,6 +20,7 @@ add_library(build MODULE src/cli_parsers.cpp src/commands.cpp src/package_staging.cpp + src/checksums.cpp ) # Set output properties @@ -30,10 +34,11 @@ set_target_properties( target_include_directories(build PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${DPM_ROOT_DIR} + ${OPENSSL_INCLUDE_DIR} ) -# Link with filesystem library -target_link_libraries(build stdc++fs) +# Link with filesystem library and OpenSSL +target_link_libraries(build stdc++fs ${OPENSSL_LIBRARIES}) # Standalone version - used for debugging add_executable(build_standalone @@ -42,6 +47,7 @@ add_executable(build_standalone src/cli_parsers.cpp src/commands.cpp src/package_staging.cpp + src/checksums.cpp ) # 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 ${CMAKE_CURRENT_SOURCE_DIR}/include ${DPM_ROOT_DIR} + ${OPENSSL_INCLUDE_DIR} ) -# Link with filesystem library for standalone -target_link_libraries(build_standalone stdc++fs) +# Link with filesystem library and OpenSSL for standalone +target_link_libraries(build_standalone stdc++fs ${OPENSSL_LIBRARIES}) # Set the output name for the standalone executable set_target_properties( diff --git a/modules/build/include/checksums.hpp b/modules/build/include/checksums.hpp new file mode 100644 index 0000000..5dcc539 --- /dev/null +++ b/modules/build/include/checksums.hpp @@ -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 + * + * Part of the Dark Horse Linux Package Manager (DPM) + */ +#pragma once + +#include +#include + +/** + * @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); diff --git a/modules/build/include/package_staging.hpp b/modules/build/include/package_staging.hpp index 2e49e5c..6f41a15 100644 --- a/modules/build/include/package_staging.hpp +++ b/modules/build/include/package_staging.hpp @@ -19,6 +19,7 @@ #include #include #include +#include "checksums.hpp" /** * @brief Stages a DPM package diff --git a/modules/build/src/checksums.cpp b/modules/build/src/checksums.cpp new file mode 100644 index 0000000..ade3827 --- /dev/null +++ b/modules/build/src/checksums.cpp @@ -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 + * + * 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(); +} \ No newline at end of file diff --git a/modules/build/src/package_staging.cpp b/modules/build/src/package_staging.cpp index 7052ca0..d7e90bc 100644 --- a/modules/build/src/package_staging.cpp +++ b/modules/build/src/package_staging.cpp @@ -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 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 std::ofstream manifest_file(manifest_path); if (!manifest_file.is_open()) { @@ -352,8 +356,6 @@ static bool update_contents_manifest(const std::filesystem::path& package_dir) return false; } - dpm_log(LOG_INFO, "Generating contents manifest..."); - // Process each file in the contents directory recursively for (const auto& entry : std::filesystem::recursive_directory_iterator(contents_dir)) { // 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 struct stat file_stat; if (stat(file_path.c_str(), &file_stat) != 0) { - dpm_log(LOG_ERROR, ("Failed to get file stats for: " + file_path.string()).c_str()); - continue; + dpm_log(LOG_FATAL, ("Failed to get file stats for: " + file_path.string()).c_str()); + return false; } // Format permissions as octal @@ -397,8 +399,12 @@ static bool update_contents_manifest(const std::filesystem::path& package_dir) std::string ownership = owner + ":" + group; - // Calculate file checksum (placeholder - would normally use SHA-256) - std::string checksum = "CHECKSUM_PLACEHOLDER"; // Actual hash calculation would be here + // Calculate file checksum using the configured algorithm + 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') char control_designation = 'C';