diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/.gitignore b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/.gitignore new file mode 100644 index 00000000..4ec51d54 --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/.gitignore @@ -0,0 +1,17 @@ +cmake-build-debug/ +bin/ +lib/ +*.o + +secure_sync +secure_sync.exe + +*.pem +*.key + +.DS_Store +.vscode/ +.idea/ + +SHA256SUMS +SHA256SUMS.sign diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/CMakelists.txt b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/CMakelists.txt new file mode 100644 index 00000000..3bb687f8 --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/CMakelists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.15) + +project(SecureSync VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(OpenSSL REQUIRED) + +# set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) + +add_executable(secure_sync src/main.cpp) + +target_include_directories(secure_sync PRIVATE $${OPENSSL_INCLUDE_DIR}) + +target_link_libraries(secure_sync PRIVATE OpenSSL::Crypto) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + target_link_libraries(secure_sync PRIVATE stdc++fs) +endif() + +message(STATUS "Build setup ready. OpenSSL found at: $${OPENSSL_INCLUDE_DIR}") diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/README.md b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/README.md new file mode 100644 index 00000000..822bb165 --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/README.md @@ -0,0 +1,63 @@ +# secure_sync — Merkle-tree file integrity checker + +`secure_sync` builds a **Merkle tree** over a directory, writes the root hash +to a manifest file, and signs it with an **Ed25519 private key**. On the +receiving end it re-hashes the directory, verifies the Ed25519 signature, and +compares the Merkle root — detecting any tampering or corruption. + +## Artifact layout + +| File | Contents | +|------|----------| +| `SHA256SUMS` | Merkle root hash of the directory (plain text) | +| `SHA256SUMS.sign` | Ed25519 binary signature over `SHA256SUMS` | + +## Build + +```bash +cmake -S . -B build +cmake --build build +# binary: build/secure_sync +``` + +Requires **OpenSSL ≥ 1.1** and a C++17 compiler. + +## Key generation + +```bash +./generate_keys.sh +# produces: private.pem (keep secret!), public.pem (distribute freely) +``` + +## Usage + +### Sign a directory + +```bash +./build/secure_sync --sign private.pem +``` + +1. Hashes every regular file under `` with SHA-256. +2. Builds a Merkle tree and writes the root hash to `/SHA256SUMS`. +3. Signs `SHA256SUMS` with the Ed25519 private key → `/SHA256SUMS.sign`. + +### Verify a directory + +```bash +./build/secure_sync --check public.pem +``` + +1. Verifies the Ed25519 signature (`SHA256SUMS.sign`) against `SHA256SUMS`. +2. Re-computes the Merkle root from the current files. +3. Compares it against the trusted root stored in `SHA256SUMS`. +4. Exits with code 0 on success, 1 on any mismatch. + +## How it works + +Every regular file is hashed with **SHA-256** (leaf nodes of the Merkle tree). +The hashes are sorted by relative path, concatenated, and hashed again — +producing the **Merkle root**. The root is written to `SHA256SUMS`, which is +then signed with **Ed25519** via OpenSSL, producing `SHA256SUMS.sign`. + +On verification the signature is checked first (ensuring the manifest was not +replaced), then the Merkle root is recomputed and compared. diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/CMakeCache.txt b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/CMakeCache.txt new file mode 100644 index 00000000..85f01e51 --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/CMakeCache.txt @@ -0,0 +1,437 @@ +# This is the CMakeCache file. +# For build in directory: /Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build +# It was generated by CMake: /opt/homebrew/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//Path to a program. +CMAKE_ADDR2LINE:FILEPATH=CMAKE_ADDR2LINE-NOTFOUND + +//Path to a program. +CMAKE_AR:FILEPATH=/usr/bin/ar + +//Choose the type of build, options are: None Debug Release RelWithDebInfo +// MinSizeRel ... +CMAKE_BUILD_TYPE:STRING= + +//Enable/Disable color output during build. +CMAKE_COLOR_MAKEFILE:BOOL=ON + +//CXX compiler +CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++ + +//Flags used by the CXX compiler during all build types. +CMAKE_CXX_FLAGS:STRING= + +//Flags used by the CXX compiler during DEBUG builds. +CMAKE_CXX_FLAGS_DEBUG:STRING=-g + +//Flags used by the CXX compiler during MINSIZEREL builds. +CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the CXX compiler during RELEASE builds. +CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the CXX compiler during RELWITHDEBINFO builds. +CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//Path to a program. +CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND + +//Flags used by the linker during all build types. +CMAKE_EXE_LINKER_FLAGS:STRING= + +//Flags used by the linker during DEBUG builds. +CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during MINSIZEREL builds. +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during RELEASE builds. +CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during RELWITHDEBINFO builds. +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Enable/Disable output of compile commands during generation. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL= + +//Value Computed by CMake. +CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/CMakeFiles/pkgRedirects + +//Path to a program. +CMAKE_INSTALL_NAME_TOOL:FILEPATH=/usr/bin/install_name_tool + +//Install path prefix, prepended onto install directories. +CMAKE_INSTALL_PREFIX:PATH=/usr/local + +//Path to a program. +CMAKE_LINKER:FILEPATH=/usr/bin/ld + +//Path to a program. +CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make + +//Flags used by the linker during the creation of modules during +// all build types. +CMAKE_MODULE_LINKER_FLAGS:STRING= + +//Flags used by the linker during the creation of modules during +// DEBUG builds. +CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during the creation of modules during +// MINSIZEREL builds. +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during the creation of modules during +// RELEASE builds. +CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during the creation of modules during +// RELWITHDEBINFO builds. +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_NM:FILEPATH=/usr/bin/nm + +//Path to a program. +CMAKE_OBJCOPY:FILEPATH=CMAKE_OBJCOPY-NOTFOUND + +//Path to a program. +CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump + +//Build architectures for OSX +CMAKE_OSX_ARCHITECTURES:STRING= + +//Minimum OS X version to target for deployment (at runtime); newer +// APIs weak linked. Set to empty string for default value. +CMAKE_OSX_DEPLOYMENT_TARGET:STRING= + +//The product will be built against the headers and libraries located +// inside the indicated SDK. +CMAKE_OSX_SYSROOT:STRING= + +//Value Computed by CMake +CMAKE_PROJECT_COMPAT_VERSION:STATIC= + +//Value Computed by CMake +CMAKE_PROJECT_DESCRIPTION:STATIC= + +//Value Computed by CMake +CMAKE_PROJECT_HOMEPAGE_URL:STATIC= + +//Value Computed by CMake +CMAKE_PROJECT_NAME:STATIC=SecureSync + +//Value Computed by CMake +CMAKE_PROJECT_SPDX_LICENSE:STATIC= + +//Value Computed by CMake +CMAKE_PROJECT_VERSION:STATIC=1.0.0 + +//Value Computed by CMake +CMAKE_PROJECT_VERSION_MAJOR:STATIC=1 + +//Value Computed by CMake +CMAKE_PROJECT_VERSION_MINOR:STATIC=0 + +//Value Computed by CMake +CMAKE_PROJECT_VERSION_PATCH:STATIC=0 + +//Value Computed by CMake +CMAKE_PROJECT_VERSION_TWEAK:STATIC= + +//Path to a program. +CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib + +//Path to a program. +CMAKE_READELF:FILEPATH=CMAKE_READELF-NOTFOUND + +//Flags used by the linker during the creation of shared libraries +// during all build types. +CMAKE_SHARED_LINKER_FLAGS:STRING= + +//Flags used by the linker during the creation of shared libraries +// during DEBUG builds. +CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during the creation of shared libraries +// during MINSIZEREL builds. +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during the creation of shared libraries +// during RELEASE builds. +CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during the creation of shared libraries +// during RELWITHDEBINFO builds. +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//If set, runtime paths are not added when installing shared libraries, +// but are added when building. +CMAKE_SKIP_INSTALL_RPATH:BOOL=NO + +//If set, runtime paths are not added when using shared libraries. +CMAKE_SKIP_RPATH:BOOL=NO + +//Flags used by the archiver during the creation of static libraries +// during all build types. +CMAKE_STATIC_LINKER_FLAGS:STRING= + +//Flags used by the archiver during the creation of static libraries +// during DEBUG builds. +CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the archiver during the creation of static libraries +// during MINSIZEREL builds. +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the archiver during the creation of static libraries +// during RELEASE builds. +CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the archiver during the creation of static libraries +// during RELWITHDEBINFO builds. +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_STRIP:FILEPATH=/usr/bin/strip + +//Path to a program. +CMAKE_TAPI:FILEPATH=/Library/Developer/CommandLineTools/usr/bin/tapi + +//If this value is on, makefiles will be generated without the +// .SILENT directive, and all commands will be echoed to the console +// during the make. This is useful for debugging only. With Visual +// Studio IDE projects all commands are done without /nologo. +CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE + +//Path to a library. +OPENSSL_CRYPTO_LIBRARY:FILEPATH=/opt/homebrew/Cellar/openssl@3/3.6.1/lib/libcrypto.dylib + +//Path to a file. +OPENSSL_INCLUDE_DIR:PATH=/opt/homebrew/Cellar/openssl@3/3.6.1/include + +//Path to a library. +OPENSSL_SSL_LIBRARY:FILEPATH=/opt/homebrew/Cellar/openssl@3/3.6.1/lib/libssl.dylib + +//Arguments to supply to pkg-config +PKG_CONFIG_ARGN:STRING= + +//pkg-config executable +PKG_CONFIG_EXECUTABLE:FILEPATH=/opt/homebrew/bin/pkg-config + +//Value Computed by CMake +SecureSync_BINARY_DIR:STATIC=/Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build + +//Value Computed by CMake +SecureSync_IS_TOP_LEVEL:STATIC=ON + +//Value Computed by CMake +SecureSync_SOURCE_DIR:STATIC=/Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904 + +//Path to a library. +pkgcfg_lib__OPENSSL_crypto:FILEPATH=/opt/homebrew/Cellar/openssl@3/3.6.1/lib/libcrypto.dylib + +//Path to a library. +pkgcfg_lib__OPENSSL_ssl:FILEPATH=/opt/homebrew/Cellar/openssl@3/3.6.1/lib/libssl.dylib + + +######################## +# INTERNAL cache entries +######################## + +//ADVANCED property for variable: CMAKE_ADDR2LINE +CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_AR +CMAKE_AR-ADVANCED:INTERNAL=1 +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=2 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=3 +//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE +CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/opt/homebrew/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/opt/homebrew/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/opt/homebrew/bin/ctest +//ADVANCED property for variable: CMAKE_CXX_COMPILER +CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS +CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG +CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL +CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE +CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO +CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_DLLTOOL +CMAKE_DLLTOOL-ADVANCED:INTERNAL=1 +//Path to cache edit program executable. +CMAKE_EDIT_COMMAND:INTERNAL=/opt/homebrew/bin/ccmake +//Executable file format +CMAKE_EXECUTABLE_FORMAT:INTERNAL=MACHO +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS +CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG +CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE +CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS +CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1 +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Unix Makefiles +//Generator instance identifier. +CMAKE_GENERATOR_INSTANCE:INTERNAL= +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904 +//ADVANCED property for variable: CMAKE_INSTALL_NAME_TOOL +CMAKE_INSTALL_NAME_TOOL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_LINKER +CMAKE_LINKER-ADVANCED:INTERNAL=1 +//Name of CMakeLists files to read +CMAKE_LIST_FILE_NAME:INTERNAL=CMakeLists.txt +//ADVANCED property for variable: CMAKE_MAKE_PROGRAM +CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS +CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG +CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE +CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_NM +CMAKE_NM-ADVANCED:INTERNAL=1 +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1 +//ADVANCED property for variable: CMAKE_OBJCOPY +CMAKE_OBJCOPY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_OBJDUMP +CMAKE_OBJDUMP-ADVANCED:INTERNAL=1 +//Platform information initialized +CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RANLIB +CMAKE_RANLIB-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_READELF +CMAKE_READELF-ADVANCED:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/opt/homebrew/share/cmake +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS +CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG +CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE +CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH +CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_RPATH +CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS +CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG +CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE +CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STRIP +CMAKE_STRIP-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_TAPI +CMAKE_TAPI-ADVANCED:INTERNAL=1 +//uname command +CMAKE_UNAME:INTERNAL=/usr/bin/uname +//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE +CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 +//Details about finding OpenSSL +FIND_PACKAGE_MESSAGE_DETAILS_OpenSSL:INTERNAL=[/opt/homebrew/Cellar/openssl@3/3.6.1/lib/libcrypto.dylib][/opt/homebrew/Cellar/openssl@3/3.6.1/include][ ][v3.6.1()] +//ADVANCED property for variable: OPENSSL_CRYPTO_LIBRARY +OPENSSL_CRYPTO_LIBRARY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: OPENSSL_INCLUDE_DIR +OPENSSL_INCLUDE_DIR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: OPENSSL_SSL_LIBRARY +OPENSSL_SSL_LIBRARY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: PKG_CONFIG_ARGN +PKG_CONFIG_ARGN-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: PKG_CONFIG_EXECUTABLE +PKG_CONFIG_EXECUTABLE-ADVANCED:INTERNAL=1 +_OPENSSL_CFLAGS:INTERNAL=-I/opt/homebrew/Cellar/openssl@3/3.6.1/include +_OPENSSL_CFLAGS_I:INTERNAL= +_OPENSSL_CFLAGS_OTHER:INTERNAL= +_OPENSSL_FOUND:INTERNAL=1 +_OPENSSL_INCLUDEDIR:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1/include +_OPENSSL_INCLUDE_DIRS:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1/include +_OPENSSL_LDFLAGS:INTERNAL=-L/opt/homebrew/Cellar/openssl@3/3.6.1/lib;-lssl;-lcrypto +_OPENSSL_LDFLAGS_OTHER:INTERNAL= +_OPENSSL_LIBDIR:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1/lib +_OPENSSL_LIBRARIES:INTERNAL=ssl;crypto +_OPENSSL_LIBRARY_DIRS:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1/lib +_OPENSSL_LIBS:INTERNAL= +_OPENSSL_LIBS_L:INTERNAL= +_OPENSSL_LIBS_OTHER:INTERNAL= +_OPENSSL_LIBS_PATHS:INTERNAL= +_OPENSSL_MODULE_NAME:INTERNAL=openssl +_OPENSSL_PREFIX:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1 +_OPENSSL_STATIC_CFLAGS:INTERNAL=-I/opt/homebrew/Cellar/openssl@3/3.6.1/include +_OPENSSL_STATIC_CFLAGS_I:INTERNAL= +_OPENSSL_STATIC_CFLAGS_OTHER:INTERNAL= +_OPENSSL_STATIC_INCLUDE_DIRS:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1/include +_OPENSSL_STATIC_LDFLAGS:INTERNAL=-L/opt/homebrew/Cellar/openssl@3/3.6.1/lib;-lssl;-lcrypto +_OPENSSL_STATIC_LDFLAGS_OTHER:INTERNAL= +_OPENSSL_STATIC_LIBDIR:INTERNAL= +_OPENSSL_STATIC_LIBRARIES:INTERNAL=ssl;crypto +_OPENSSL_STATIC_LIBRARY_DIRS:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1/lib +_OPENSSL_STATIC_LIBS:INTERNAL= +_OPENSSL_STATIC_LIBS_L:INTERNAL= +_OPENSSL_STATIC_LIBS_OTHER:INTERNAL= +_OPENSSL_STATIC_LIBS_PATHS:INTERNAL= +_OPENSSL_VERSION:INTERNAL=3.6.1 +_OPENSSL_openssl_INCLUDEDIR:INTERNAL= +_OPENSSL_openssl_LIBDIR:INTERNAL= +_OPENSSL_openssl_PREFIX:INTERNAL= +_OPENSSL_openssl_VERSION:INTERNAL= +__pkg_config_arguments__OPENSSL:INTERNAL=QUIET;openssl +__pkg_config_checked__OPENSSL:INTERNAL=1 +//ADVANCED property for variable: pkgcfg_lib__OPENSSL_crypto +pkgcfg_lib__OPENSSL_crypto-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: pkgcfg_lib__OPENSSL_ssl +pkgcfg_lib__OPENSSL_ssl-ADVANCED:INTERNAL=1 +prefix_result:INTERNAL=/opt/homebrew/Cellar/openssl@3/3.6.1/lib + diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/Makefile b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/Makefile new file mode 100644 index 00000000..27c9409c --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/Makefile @@ -0,0 +1,181 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 4.2 + +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + +# Disable VCS-based implicit rules. +% : %,v + +# Disable VCS-based implicit rules. +% : RCS/% + +# Disable VCS-based implicit rules. +% : RCS/%,v + +# Disable VCS-based implicit rules. +% : SCCS/s.% + +# Disable VCS-based implicit rules. +% : s.% + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Command-line flag to silence nested $(MAKE). +$(VERBOSE)MAKESILENT = -s + +#Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /opt/homebrew/bin/cmake + +# The command to remove a file. +RM = /opt/homebrew/bin/cmake -E rm -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904 + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Running CMake cache editor..." + /opt/homebrew/bin/ccmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Running CMake to regenerate build system..." + /opt/homebrew/bin/cmake --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache +.PHONY : rebuild_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/CMakeFiles /Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build//CMakeFiles/progress.marks + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named secure_sync + +# Build rule for target. +secure_sync: cmake_check_build_system + $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 secure_sync +.PHONY : secure_sync + +# fast build rule for target. +secure_sync/fast: + $(MAKE) $(MAKESILENT) -f CMakeFiles/secure_sync.dir/build.make CMakeFiles/secure_sync.dir/build +.PHONY : secure_sync/fast + +src/main.o: src/main.cpp.o +.PHONY : src/main.o + +# target to build an object file +src/main.cpp.o: + $(MAKE) $(MAKESILENT) -f CMakeFiles/secure_sync.dir/build.make CMakeFiles/secure_sync.dir/src/main.cpp.o +.PHONY : src/main.cpp.o + +src/main.i: src/main.cpp.i +.PHONY : src/main.i + +# target to preprocess a source file +src/main.cpp.i: + $(MAKE) $(MAKESILENT) -f CMakeFiles/secure_sync.dir/build.make CMakeFiles/secure_sync.dir/src/main.cpp.i +.PHONY : src/main.cpp.i + +src/main.s: src/main.cpp.s +.PHONY : src/main.s + +# target to generate assembly for a file +src/main.cpp.s: + $(MAKE) $(MAKESILENT) -f CMakeFiles/secure_sync.dir/build.make CMakeFiles/secure_sync.dir/src/main.cpp.s +.PHONY : src/main.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... edit_cache" + @echo "... rebuild_cache" + @echo "... secure_sync" + @echo "... src/main.o" + @echo "... src/main.i" + @echo "... src/main.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/cmake_install.cmake b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/cmake_install.cmake new file mode 100644 index 00000000..6a65b42f --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/cmake_install.cmake @@ -0,0 +1,61 @@ +# Install script for directory: /Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904 + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/usr/bin/objdump") +endif() + +string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT + "${CMAKE_INSTALL_MANIFEST_FILES}") +if(CMAKE_INSTALL_LOCAL_ONLY) + file(WRITE "/Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/install_local_manifest.txt" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") +endif() +if(CMAKE_INSTALL_COMPONENT) + if(CMAKE_INSTALL_COMPONENT MATCHES "^[a-zA-Z0-9_.+-]+$") + set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") + else() + string(MD5 CMAKE_INST_COMP_HASH "${CMAKE_INSTALL_COMPONENT}") + set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INST_COMP_HASH}.txt") + unset(CMAKE_INST_COMP_HASH) + endif() +else() + set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") +endif() + +if(NOT CMAKE_INSTALL_LOCAL_ONLY) + file(WRITE "/Users/marat237/Desktop/DS/practice/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/build/${CMAKE_INSTALL_MANIFEST}" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") +endif() diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/destination_files/SHA256SUMS b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/destination_files/SHA256SUMS new file mode 100644 index 00000000..082cffa1 --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/destination_files/SHA256SUMS @@ -0,0 +1 @@ +cdb1e1e98543021609a9c5f72c5a5a28828bba7d37037a60c0e289f7c8b9af00 diff --git a/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/destination_files/SHA256SUMS.sign b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/destination_files/SHA256SUMS.sign new file mode 100644 index 00000000..e50f6031 --- /dev/null +++ b/0b-kernel/merkletree_KatrinaVL_Quattro2372_Areg1904/destination_files/SHA256SUMS.sign @@ -0,0 +1 @@ +"0$.MBFv{^+:xe2\dFe]F +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace fs = std::filesystem; + +const std::string MANIFEST_FILENAME = "SHA256SUMS"; +const std::string SIG_FILENAME = "SHA256SUMS.sign"; + +class Crypto { +public: + using ScopedCTX = std::unique_ptr; + using ScopedPKEY = std::unique_ptr; + using ScopedBIO = std::unique_ptr; + + static std::string sha256_file_hex(const fs::path& path); + static std::string sha256_string_hex(const std::string& input); + static void sign_file(const fs::path& dataPath, const fs::path& sigPath, const fs::path& privKeyPath); + static bool verify_file(const fs::path& dataPath, const fs::path& sigPath, const fs::path& pubKeyPath); + +private: + static void handle_errors(const std::string& context); + static std::string get_openssl_error_string(); + static ScopedPKEY load_key(const fs::path& path, bool isPrivate); + static std::vector get_raw_file_hash(const fs::path& path); +}; + +struct FileRecord { + std::string relPath; + std::string hash; + bool operator<(const FileRecord& other) const; +}; + +class MerkleTree { +public: + explicit MerkleTree(fs::path dir); + void build(); + void save_manifest() const; + std::string get_root_hash() const; + const std::vector& get_records() const; + static std::string load_manifest(const fs::path& dir); + +private: + fs::path rootDir; + std::vector records; + std::string rootHash; +}; + +void run_generation_mode(const fs::path& dir, const fs::path& key); +void run_verification_mode(const fs::path& dir, const fs::path& key); + +int main(int argc, char* argv[]) { + if (argc < 4) { + std::cerr << "Usage:\n" + << " Generate: " << argv[0] << " --sign \n" + << " Verify: " << argv[0] << " --check \n"; + return 1; + } + + OpenSSL_add_all_algorithms(); + + std::string mode = argv[1]; + fs::path targetDir = argv[2]; + fs::path keyPath = argv[3]; + + std::cout << "[*] Target directory : " << fs::absolute(targetDir) << "\n"; + std::cout << "[*] Key file : " << fs::absolute(keyPath) << "\n"; + + try { + if (mode == "--sign") { + run_generation_mode(targetDir, keyPath); + } else if (mode == "--check") { + run_verification_mode(targetDir, keyPath); + } else { + std::cerr << "[ERROR] Unknown mode: " << mode << "\n"; + std::cerr << " Expected --sign or --check\n"; + return 1; + } + } catch (const std::exception& ex) { + std::cerr << "[ERROR] Fatal exception: " << ex.what() << "\n"; + return 1; + } + + return 0; +} + +void run_generation_mode(const fs::path& dir, const fs::path& key) { + std::cout << "[*] Mode: GENERATE & SIGN\n"; + + if (!fs::exists(dir)) { + throw std::runtime_error("Target directory does not exist: " + dir.string()); + } + if (!fs::is_directory(dir)) { + throw std::runtime_error("Target path is not a directory: " + dir.string()); + } + if (!fs::exists(key)) { + throw std::runtime_error("Private key file not found: " + key.string()); + } + + MerkleTree tree(dir); + std::cout << " Scanning directory: " << dir << "...\n"; + tree.build(); + + const auto& records = tree.get_records(); + std::cout << " Files indexed: " << records.size() << "\n"; + for (const auto& rec : records) { + std::cout << " " << rec.hash << " " << rec.relPath << "\n"; + } + + tree.save_manifest(); + std::cout << " Manifest written : " << (dir / MANIFEST_FILENAME) << "\n"; + std::cout << " Merkle Root : " << tree.get_root_hash() << "\n"; + + std::cout << " Signing manifest...\n"; + Crypto::sign_file(dir / MANIFEST_FILENAME, dir / SIG_FILENAME, key); + std::cout << " Signature written: " << (dir / SIG_FILENAME) << "\n"; + + std::cout << "[+] Protected. Ready for transfer.\n"; +} + +void run_verification_mode(const fs::path& dir, const fs::path& key) { + std::cout << "[*] Mode: VERIFY INTEGRITY\n"; + + if (!fs::exists(dir)) { + throw std::runtime_error("Target directory does not exist: " + dir.string()); + } + + fs::path manifestPath = dir / MANIFEST_FILENAME; + fs::path sigPath = dir / SIG_FILENAME; + + if (!fs::exists(manifestPath)) { + throw std::runtime_error("Manifest file not found: " + manifestPath.string()); + } + if (!fs::exists(sigPath)) { + throw std::runtime_error("Signature file not found: " + sigPath.string() + + "\n Was the directory signed with --sign first?"); + } + if (!fs::exists(key)) { + throw std::runtime_error("Public key file not found: " + key.string()); + } + + std::cout << " Manifest path : " << manifestPath << "\n"; + std::cout << " Signature path: " << sigPath << "\n"; + + std::cout << " Checking digital signature...\n"; + if (!Crypto::verify_file(manifestPath, sigPath, key)) { + std::cerr << "[!] Signature verification FAILED.\n"; + std::cerr << " Possible reasons:\n"; + std::cerr << " - Wrong public key (does not match the private key used for signing)\n"; + std::cerr << " - Manifest file was modified after signing\n"; + std::cerr << " - Signature file was corrupted or replaced\n"; + std::cerr << " Manifest : " << manifestPath << " (" + << fs::file_size(manifestPath) << " bytes)\n"; + std::cerr << " Signature: " << sigPath << " (" + << fs::file_size(sigPath) << " bytes)\n"; + throw std::runtime_error("CRITICAL: Signature is INVALID. The manifest has been tampered with!"); + } + std::cout << " Signature OK. Manifest is trusted.\n"; + + std::string expectedRoot = MerkleTree::load_manifest(dir); + std::cout << " Expected Merkle Root: " << expectedRoot << "\n"; + + std::cout << " Re-scanning filesystem...\n"; + MerkleTree currentTree(dir); + currentTree.build(); + + const auto& currentRecords = currentTree.get_records(); + std::cout << " Files found on disk: " << currentRecords.size() << "\n"; + + std::string actualRoot = currentTree.get_root_hash(); + std::cout << " Actual Merkle Root : " << actualRoot << "\n"; + + if (actualRoot == expectedRoot) { + std::cout << "[+] SUCCESS: Filesystem is intact. All " + << currentRecords.size() << " file(s) match.\n"; + } else { + std::cerr << "[!] FAILURE: Root hash mismatch.\n"; + std::cerr << " Expected : " << expectedRoot << "\n"; + std::cerr << " Actual : " << actualRoot << "\n"; + std::cerr << "\n Detailed file scan:\n"; + for (const auto& rec : currentRecords) { + std::cerr << " " << rec.hash << " " << rec.relPath << "\n"; + } + exit(1); + } +} + +bool FileRecord::operator<(const FileRecord& other) const { + return relPath < other.relPath; +} + +MerkleTree::MerkleTree(fs::path dir) : rootDir(std::move(dir)) {} + +void MerkleTree::build() { + records.clear(); + std::string hashAccumulator; + + if (!fs::exists(rootDir)) { + throw std::runtime_error("Directory not found: " + rootDir.string()); + } + + for (const auto& entry : fs::recursive_directory_iterator(rootDir)) { + if (!entry.is_regular_file()) continue; + std::string fname = entry.path().filename().string(); + if (fname == MANIFEST_FILENAME || fname == SIG_FILENAME) continue; + + std::string rel = fs::relative(entry.path(), rootDir).string(); + std::replace(rel.begin(), rel.end(), '\\', '/'); + + try { + std::string hash = Crypto::sha256_file_hex(entry.path()); + records.push_back({rel, hash}); + } catch (const std::exception& ex) { + std::cerr << "[WARN] Cannot hash file, skipping: " << entry.path() + << "\n Reason: " << ex.what() << "\n"; + } + } + + std::sort(records.begin(), records.end()); + for (const auto& rec : records) hashAccumulator += rec.hash + rec.relPath; + rootHash = Crypto::sha256_string_hex(hashAccumulator); +} + +void MerkleTree::save_manifest() const { + fs::path outPath = rootDir / MANIFEST_FILENAME; + std::ofstream out(outPath); + if (!out) { + throw std::runtime_error("Cannot write manifest file: " + outPath.string() + + "\n Check directory permissions."); + } + out << rootHash << "\n"; + if (!out) { + throw std::runtime_error("Write error while saving manifest: " + outPath.string()); + } +} + +std::string MerkleTree::get_root_hash() const { return rootHash; } +const std::vector& MerkleTree::get_records() const { return records; } + +std::string MerkleTree::load_manifest(const fs::path& dir) { + fs::path manifestPath = dir / MANIFEST_FILENAME; + std::ifstream in(manifestPath); + if (!in) { + throw std::runtime_error("Cannot open manifest for reading: " + manifestPath.string()); + } + std::string root; + std::getline(in, root); + if (root.empty()) { + throw std::runtime_error("Manifest is empty or malformed: " + manifestPath.string()); + } + return root; +} + +std::string Crypto::get_openssl_error_string() { + char buf[256]; + unsigned long errCode = ERR_get_error(); + if (errCode == 0) return "(no OpenSSL error queued)"; + ERR_error_string_n(errCode, buf, sizeof(buf)); + return std::string(buf); +} + +void Crypto::handle_errors(const std::string& context) { + throw std::runtime_error(context + " | OpenSSL: " + get_openssl_error_string()); +} + +Crypto::ScopedPKEY Crypto::load_key(const fs::path& path, bool isPrivate) { + if (!fs::exists(path)) { + throw std::runtime_error("Key file does not exist: " + path.string()); + } + + ScopedBIO bio(BIO_new_file(path.string().c_str(), "r"), BIO_free); + if (!bio) { + throw std::runtime_error("Cannot open key file: " + path.string() + + " | OpenSSL: " + get_openssl_error_string()); + } + + EVP_PKEY* pkey = isPrivate + ? PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr) + : PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr); + + if (!pkey) { + std::string keyType = isPrivate ? "private" : "public"; + throw std::runtime_error("Failed to parse " + keyType + " key from: " + path.string() + + "\n Make sure the file is a valid PEM-encoded " + keyType + " key." + + "\n OpenSSL: " + get_openssl_error_string()); + } + + return ScopedPKEY(pkey, EVP_PKEY_free); +} + +std::vector Crypto::get_raw_file_hash(const fs::path& path) { + ScopedCTX ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); + if (!ctx) { + handle_errors("Failed to allocate EVP_MD_CTX for file: " + path.string()); + } + if (!EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr)) { + handle_errors("DigestInit failed for file: " + path.string()); + } + + std::ifstream file(path, std::ios::binary); + if (!file) { + throw std::runtime_error("Cannot open file for hashing: " + path.string() + + "\n Check file permissions."); + } + + char buffer[16384]; + std::streamsize totalRead = 0; + while (file.read(buffer, sizeof(buffer)) || file.gcount()) { + std::streamsize count = file.gcount(); + totalRead += count; + if (!EVP_DigestUpdate(ctx.get(), buffer, static_cast(count))) { + handle_errors("DigestUpdate failed at offset ~" + std::to_string(totalRead) + + " in file: " + path.string()); + } + if (file.eof()) break; + } + + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int len = 0; + if (!EVP_DigestFinal_ex(ctx.get(), hash, &len)) { + handle_errors("DigestFinal failed for file: " + path.string()); + } + + return std::vector(hash, hash + len); +} + +std::string Crypto::sha256_file_hex(const fs::path& path) { + auto raw = get_raw_file_hash(path); + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (auto c : raw) ss << std::setw(2) << static_cast(c); + return ss.str(); +} + +std::string Crypto::sha256_string_hex(const std::string& input) { + ScopedCTX ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); + if (!ctx) handle_errors("Failed to allocate EVP_MD_CTX for string hashing"); + + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int len = 0; + + if (!EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr)) handle_errors("DigestInit (string)"); + if (!EVP_DigestUpdate(ctx.get(), input.data(), input.size())) handle_errors("DigestUpdate (string)"); + if (!EVP_DigestFinal_ex(ctx.get(), hash, &len)) handle_errors("DigestFinal (string)"); + + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (unsigned int i = 0; i < len; i++) ss << std::setw(2) << static_cast(hash[i]); + return ss.str(); +} + +void Crypto::sign_file(const fs::path& dataPath, const fs::path& sigPath, const fs::path& privKeyPath) { + std::cout << " Hashing manifest for signing: " << dataPath << "\n"; + auto dataHash = get_raw_file_hash(dataPath); + std::cout << " Loading private key: " << privKeyPath << "\n"; + auto pkey = load_key(privKeyPath, true); + + ScopedCTX ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); + if (!ctx) handle_errors("Failed to allocate EVP_MD_CTX for signing"); + + if (EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()) <= 0) { + handle_errors("DigestSignInit failed" + "\n Ensure the key type supports raw signing (e.g. Ed25519)"); + } + + size_t len = 0; + if (EVP_DigestSign(ctx.get(), nullptr, &len, dataHash.data(), dataHash.size()) <= 0) { + handle_errors("DigestSign (length query) failed"); + } + + std::vector sig(len); + if (EVP_DigestSign(ctx.get(), sig.data(), &len, dataHash.data(), dataHash.size()) <= 0) { + handle_errors("DigestSign (final) failed"); + } + sig.resize(len); + + std::ofstream out(sigPath, std::ios::binary); + if (!out) { + throw std::runtime_error("Cannot write signature file: " + sigPath.string() + + "\n Check directory permissions."); + } + out.write(reinterpret_cast(sig.data()), static_cast(len)); + if (!out) { + throw std::runtime_error("Write error while saving signature: " + sigPath.string()); + } + std::cout << " Signature size: " << len << " bytes\n"; +} + +bool Crypto::verify_file(const fs::path& dataPath, const fs::path& sigPath, const fs::path& pubKeyPath) { + try { + std::cerr << " Hashing manifest for verification: " << dataPath << "\n"; + auto dataHash = get_raw_file_hash(dataPath); + + std::ifstream sigIn(sigPath, std::ios::binary | std::ios::ate); + if (!sigIn) { + std::cerr << " [!] Cannot open signature file: " << sigPath << "\n"; + return false; + } + std::streamsize sigLen = sigIn.tellg(); + if (sigLen <= 0) { + std::cerr << " [!] Signature file is empty: " << sigPath << "\n"; + return false; + } + sigIn.seekg(0); + std::vector sig(static_cast(sigLen)); + sigIn.read(reinterpret_cast(sig.data()), sigLen); + std::cerr << " Signature size read: " << sigLen << " bytes\n"; + + std::cerr << " Loading public key: " << pubKeyPath << "\n"; + auto pkey = load_key(pubKeyPath, false); + + ScopedCTX ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); + if (!ctx) { + std::cerr << " [!] Failed to allocate EVP_MD_CTX\n"; + return false; + } + + if (EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()) <= 0) { + std::cerr << " [!] DigestVerifyInit failed | OpenSSL: " + << get_openssl_error_string() << "\n"; + return false; + } + + int res = EVP_DigestVerify( + ctx.get(), + sig.data(), static_cast(sigLen), + dataHash.data(), dataHash.size() + ); + + if (res != 1) { + std::cerr << " [!] EVP_DigestVerify returned " << res + << " | OpenSSL: " << get_openssl_error_string() << "\n"; + } + return res == 1; + + } catch (const std::exception& ex) { + std::cerr << " [!] Exception during verification: " << ex.what() << "\n"; + return false; + } +}