diff --git a/CMakeLists.txt b/CMakeLists.txt index f01d376..4148bb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,26 +62,27 @@ add_executable(privacyShield) # Add sources for the target target_sources(privacyShield PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryptDecrypt.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryptFiles.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryptStrings.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/passwordManager.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/passwords.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp" + src/encryption/encryptDecrypt.cpp + src/encryption/encryptFiles.cpp + src/encryption/encryptStrings.cpp + src/passwordManager/passwordManager.cpp + src/passwordManager/passwords.cpp + src/main.cpp ) # C++20 Modules target_sources(privacyShield PRIVATE FILE_SET CXX_MODULES FILES - "${CMAKE_CURRENT_SOURCE_DIR}/src/duplicateFinder/duplicateFinder.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/cryptoCipher.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryption.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/fileShredder/fileShredder.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/FuzzyMatcher.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/passwordManager.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/privacyTracks/privacyTracks.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/utils/utils.cppm" - "${CMAKE_CURRENT_SOURCE_DIR}/src/secureAllocator.cppm" + src/duplicateFinder/duplicateFinder.cppm + src/encryption/cryptoCipher.cppm + src/encryption/encryption.cppm + src/fileShredder/fileShredder.cppm + src/passwordManager/FuzzyMatcher.cppm + src/passwordManager/passwordManager.cppm + src/privacyTracks/privacyTracks.cppm + src/utils/utils.cppm + src/secureAllocator.cppm + src/mimallocSTL.cppm ) # Find the required packages @@ -110,6 +111,26 @@ if (NOT TARGET BLAKE3::blake3) endif () +# Mimalloc allocator +find_package(mimalloc 2.1 QUIET) +if (NOT TARGET mimalloc) + message(STATUS "mimalloc not found. Fetching from GitHub...") + + FetchContent_Declare( + mimalloc + GIT_REPOSITORY https://github.com/microsoft/mimalloc.git + GIT_TAG v2.1.7 + EXCLUDE_FROM_ALL + ) +endif () + +set(MI_BUILD_TESTS OFF) # Do not build tests + +FetchContent_MakeAvailable(mimalloc) +target_include_directories(privacyShield PRIVATE "${mimalloc_SOURCE_DIR}/include") +add_library(Mimalloc::mimalloc-static ALIAS mimalloc-static) +add_library(Mimalloc::mimalloc ALIAS mimalloc) + # Fetch Isocline from GitHub FetchContent_Declare( isocline @@ -148,12 +169,12 @@ endif () # Link libraries target_link_libraries(privacyShield PRIVATE - OpenSSL::Crypto Sodium::sodium Gcrypt::Gcrypt BLAKE3::blake3 ISOCline::isocline + Mimalloc::mimalloc-static ) # Install the binary (optional), with 0755 permissions diff --git a/src/duplicateFinder/duplicateFinder.cppm b/src/duplicateFinder/duplicateFinder.cppm index 25630ac..f15343d 100644 --- a/src/duplicateFinder/duplicateFinder.cppm +++ b/src/duplicateFinder/duplicateFinder.cppm @@ -30,6 +30,7 @@ module; export module duplicateFinder; import utils; +import mimallocSTL; namespace fs = std::filesystem; @@ -38,17 +39,17 @@ constexpr std::size_t CHUNK_SIZE = 4096; ///< Read and process files in chunks o /// \brief Represents a file by its path (canonical) and hash. struct FileInfo { - std::string path; ///< the path to the file. - std::string hash; ///< the file's BLAKE3 hash + miSTL::string path; ///< the path to the file. + miSTL::string hash; ///< the file's BLAKE3 hash }; /// \brief Calculates the 256-bit BLAKE3 hash of a file. /// \param filePath path to the file. /// \return Base64-encoded hash of the file. /// \throws std::runtime_error if the file cannot be opened. -std::string calculateBlake3(const std::string &filePath) { +miSTL::string calculateBlake3(const miSTL::string &filePath) { // Open the file - std::ifstream file(filePath, std::ios::binary); + std::ifstream file(filePath.c_str(), std::ios::binary); if (!file) { if (std::error_code ec; fs::exists(filePath, ec)) throw std::runtime_error(std::format("Failed to open '{}' for hashing.", filePath)); @@ -72,7 +73,7 @@ std::string calculateBlake3(const std::string &filePath) { blake3_hasher_update(&hasher, buffer.data(), remainingBytes); // Finalize the hash calculation - std::vector digest(BLAKE3_OUT_LEN); + miSTL::vector digest(BLAKE3_OUT_LEN); blake3_hasher_finalize(&hasher, digest.data(), BLAKE3_OUT_LEN); return base64Encode(digest); @@ -93,7 +94,7 @@ inline void handleAccessError(const std::string_view filename) { /// \brief recursively traverses a directory and collects file information. /// \param directoryPath the directory to process. /// \param files a vector to store the information from the files found in the directory. -void traverseDirectory(const fs::path &directoryPath, std::vector &files) { +void traverseDirectory(const fs::path &directoryPath, miSTL::vector &files) { std::error_code ec; for (const auto &entry: fs::recursive_directory_iterator(directoryPath, @@ -109,7 +110,7 @@ void traverseDirectory(const fs::path &directoryPath, std::vector &fil continue; } // Make sure we can read the entry - if (isReadable(entry.path())) [[likely]] { + if (isReadable(entry.path().string().c_str())) [[likely]] { // process only regular files if (entry.is_regular_file()) [[likely]] { FileInfo fileInfo; @@ -133,14 +134,14 @@ void traverseDirectory(const fs::path &directoryPath, std::vector &fil /// \param files the files to process. /// \param start the index where processing starts. /// \param end the index where processing ends. -void calculateHashes(std::vector &files, const std::size_t start, const std::size_t end) { +void calculateHashes(miSTL::vector &files, const std::size_t start, const std::size_t end) { // Check if the range is valid if (start > end || end > files.size()) throw std::range_error("Invalid range."); // Calculate hashes for the files in the range for (std::size_t i = start; i < end; ++i) - files[i].hash = calculateBlake3(files[i].path); + files[i].hash = calculateBlake3(files[i].path).c_str(); } /// \brief finds duplicate files (by content) in a directory. @@ -148,7 +149,7 @@ void calculateHashes(std::vector &files, const std::size_t start, cons /// \return True if duplicates are found, else False. std::size_t findDuplicates(const fs::path &directoryPath) { // Collect file information - std::vector files; + miSTL::vector files; traverseDirectory(directoryPath, files); const std::size_t filesProcessed = files.size(); if (filesProcessed < 2) return 0; @@ -158,7 +159,7 @@ std::size_t findDuplicates(const fs::path &directoryPath) { const unsigned int numThreads{n ? n : 8}; // Use 8 threads if hardware_concurrency() fails // Divide the files among the threads - std::vector threads; + miSTL::vector threads; const std::size_t filesPerThread = filesProcessed / numThreads; std::size_t start = 0; @@ -174,7 +175,7 @@ std::size_t findDuplicates(const fs::path &directoryPath) { for (auto &thread: threads) thread.join(); // A hash map to map the files to their corresponding hashes - std::unordered_map > hashMap; + miSTL::unordered_map > hashMap; // Iterate over files and identify duplicates for (const auto &[filePath, hash]: files) diff --git a/src/encryption/encryptDecrypt.cpp b/src/encryption/encryptDecrypt.cpp index 6cdc3c9..f968b63 100644 --- a/src/encryption/encryptDecrypt.cpp +++ b/src/encryption/encryptDecrypt.cpp @@ -29,6 +29,7 @@ module; import utils; import secureAllocator; +import mimallocSTL; import passwordManager; module encryption; @@ -63,14 +64,14 @@ constexpr struct { /// \brief Formats a file size into a human-readable string. /// \param size The file size as an unsigned integer. /// \return A string representing the formatted file size. -std::string formatFileSize(const std::uintmax_t &size) { +miSTL::string formatFileSize(const std::uintmax_t &size) { int i{}; auto mantissa = static_cast(size); for (; mantissa >= 1024.; mantissa /= 1024., ++i) { } mantissa = std::ceil(mantissa * 10.) / 10.; - std::string result = std::to_string(mantissa) + "BKMGTPE"[i]; - return i == 0 ? result : result + "B (" + std::to_string(size) + ')'; + miSTL::string result { std::to_string(mantissa) + "BKMGTPE"[i]}; + return i == 0 ? result : result + "B (" + std::to_string(size).c_str() + ')'; } /// \brief Checks for issues with the input file, that may hinder encryption/decryption. @@ -101,7 +102,7 @@ void checkInputFile(const fs::path &inFile, const OperationMode &mode) { std::format("{} is not a regular file.", inFile.string())); // Encrypted files are regular } // Check if the input file is readable - if (auto file = inFile.string(); !isReadable(file)) + if (auto file = inFile.string(); !isReadable(file.c_str())) throw std::runtime_error(std::format("{} is not readable.", file)); } @@ -170,7 +171,7 @@ inline void checkOutputFile(const fs::path &inFile, fs::path &outFile, const Ope throw std::runtime_error("Operation aborted."); // Determine if the output file can be written if it exists - if (auto file = weakly_canonical(outFile).string(); !(isWritable(file) && isReadable(file))) + if (auto file = weakly_canonical(outFile).string(); !(isWritable(file.c_str()) && isReadable(file.c_str()))) throw std::runtime_error(std::format("{} is not writable/readable.", file)); } } @@ -211,7 +212,7 @@ inline void copyLastWrite(const std::string_view srcFile, const std::string_view /// \param password the password to use for encryption/decryption. /// \param algo the algorithm to use for encryption/decryption. /// \param mode the mode of operation: encryption or decryption. -void fileEncryptionDecryption(const std::string &inputFileName, const std::string &outputFileName, +void fileEncryptionDecryption(const miSTL::string &inputFileName, const miSTL::string &outputFileName, const privacy::string &password, const Algorithms &algo, const OperationMode &mode) { // The mode must be valid: must be either encryption or decryption if (mode != OperationMode::Encryption && mode != OperationMode::Decryption) [[unlikely]] { @@ -221,7 +222,7 @@ void fileEncryptionDecryption(const std::string &inputFileName, const std::strin try { /// Encrypts/decrypts a file based on the passed mode and algorithm. - auto encryptDecrypt = [&](const std::string &algorithm) -> void { + auto encryptDecrypt = [&](const miSTL::string &algorithm) -> void { if (mode == OperationMode::Encryption) // Encryption encryptFile(inputFileName, outputFileName, password, algorithm); else // Decryption @@ -275,7 +276,7 @@ void fileEncryptionDecryption(const std::string &inputFileName, const std::strin /// \brief Encrypts and decrypts files. void encryptDecrypt() { // I'm using hashmaps as an alternative to multiple if-else statements - const std::unordered_map algoChoice = { + const miSTL::unordered_map algoChoice = { {0, Algorithms::AES}, // Default {1, Algorithms::AES}, {2, Algorithms::Camellia}, @@ -284,7 +285,7 @@ void encryptDecrypt() { {5, Algorithms::Twofish} }; - const std::unordered_map algoDescription = { + const miSTL::unordered_map algoDescription = { {Algorithms::AES, "256-bit AES in CBC mode"}, {Algorithms::Camellia, "256-bit Camellia in CBC mode"}, {Algorithms::Aria, "256-bit Aria in CBC mode"}, @@ -303,8 +304,8 @@ void encryptDecrypt() { if (const int choice = getResponseInt("Enter your choice: "); choice == 1 || choice == 2) { try { - std::string pre = choice == 1 ? "En" : "De"; // the prefix string - std::string pre_l{pre}; // the prefix in lowercase + miSTL::string pre = choice == 1 ? "En" : "De"; // the prefix string + miSTL::string pre_l{pre}; // the prefix in lowercase // Transform the prefix to lowercase std::ranges::transform(pre_l.begin(), pre_l.end(), pre_l.begin(), @@ -370,7 +371,7 @@ void encryptDecrypt() { printColoredOutput('c', "{}", algoDescription.find(cipher)->second); printColoredOutputln('g', "..."); - fileEncryptionDecryption(canonical(inputPath).string(), weakly_canonical(outputPath).string(), + fileEncryptionDecryption(canonical(inputPath).string().c_str(), weakly_canonical(outputPath).string().c_str(), password, cipher, static_cast(choice)); std::println(""); } catch (const std::exception &ex) { diff --git a/src/encryption/encryptFiles.cpp b/src/encryption/encryptFiles.cpp index 972c707..b45b0c4 100644 --- a/src/encryption/encryptFiles.cpp +++ b/src/encryption/encryptFiles.cpp @@ -30,6 +30,7 @@ module; import cryptoCipher; import secureAllocator; +import mimallocSTL; module encryption; @@ -45,7 +46,7 @@ privacy::vector generateSalt(const int saltSize) { std::mutex m; privacy::vector salt(saltSize); - if (std::scoped_lock lock(m); RAND_bytes(salt.data(), saltSize) != 1) { + if (std::scoped_lock lock(m); RAND_bytes(salt.data(), saltSize) != 1) { std::cerr << "Failed to seed OpenSSL's CSPRNG properly." "\nPlease check your system's randomness utilities." << std::endl; @@ -134,15 +135,15 @@ deriveKey(const privacy::string &password, const privacy::vector /// \details Encryption mode: CBC. /// \details Key derivation function: PBKDF2 with BLAKE2b512 as the digest function (salted). /// \details The IV is generated randomly with a CSPRNG and prepended to the encrypted file. -void encryptFile(const std::string &inputFile, const std::string &outputFile, const privacy::string &password, - const std::string &algo) { +void encryptFile(const miSTL::string &inputFile, const miSTL::string &outputFile, const privacy::string &password, + const miSTL::string &algo) { // Open the input file for reading - std::ifstream inFile(inputFile, std::ios::binary); + std::ifstream inFile(inputFile.c_str(), std::ios::binary); if (!inFile) throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFile)); // Open the output file for writing - std::ofstream outFile(outputFile, std::ios::binary | std::ios::trunc); + std::ofstream outFile(outputFile.c_str(), std::ios::binary | std::ios::trunc); if (!outFile) throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFile)); @@ -227,15 +228,15 @@ void encryptFile(const std::string &inputFile, const std::string &outputFile, co /// \param algo The cipher algorithm used to encrypt the file. /// /// \throws std::runtime_error if the decryption fails, and for other (documented) errors. -void decryptFile(const std::string &inputFile, const std::string &outputFile, const privacy::string &password, - const std::string &algo) { +void decryptFile(const miSTL::string &inputFile, const miSTL::string &outputFile, const privacy::string &password, + const miSTL::string &algo) { // Open the input file for reading - std::ifstream inFile(inputFile, std::ios::binary); + std::ifstream inFile(inputFile.c_str(), std::ios::binary); if (!inFile) throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFile)); // Open the output file for writing - std::ofstream outFile(outputFile, std::ios::binary | std::ios::trunc); + std::ofstream outFile(outputFile.c_str(), std::ios::binary | std::ios::trunc); if (!outFile) throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFile)); @@ -343,15 +344,15 @@ inline void throwSafeError(const gcry_error_t &err, const std::string_view messa /// using PBKDF2 with BLAKE2b-512 as the hash function. /// \details The IV(nonce) is randomly generated and stored in the output file. void -encryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &outputFilePath, +encryptFileWithMoreRounds(const miSTL::string &inputFilePath, const miSTL::string &outputFilePath, const privacy::string &password, const gcry_cipher_algos &algorithm) { // Open the input file for reading - std::ifstream inputFile(inputFilePath, std::ios::binary); + std::ifstream inputFile(inputFilePath.c_str(), std::ios::binary); if (!inputFile) throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFilePath)); // Open the output file for writing - std::ofstream outputFile(outputFilePath, std::ios::binary | std::ios::trunc); + std::ofstream outputFile(outputFilePath.c_str(), std::ios::binary | std::ios::trunc); if (!outputFile) throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFilePath)); @@ -426,15 +427,15 @@ encryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &o /// /// \throws std::runtime_error if the decryption fails, and for other (documented) errors. void -decryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &outputFilePath, +decryptFileWithMoreRounds(const miSTL::string &inputFilePath, const miSTL::string &outputFilePath, const privacy::string &password, const gcry_cipher_algos &algorithm) { // Open the input file for reading - std::ifstream inputFile(inputFilePath, std::ios::binary); + std::ifstream inputFile(inputFilePath.c_str(), std::ios::binary); if (!inputFile) throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFilePath)); // Open the output file for writing - std::ofstream outputFile(outputFilePath, std::ios::binary | std::ios::trunc); + std::ofstream outputFile(outputFilePath.c_str(), std::ios::binary | std::ios::trunc); if (!outputFile) throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFilePath)); diff --git a/src/encryption/encryptStrings.cpp b/src/encryption/encryptStrings.cpp index d781706..b3b26f9 100644 --- a/src/encryption/encryptStrings.cpp +++ b/src/encryption/encryptStrings.cpp @@ -25,6 +25,7 @@ module; import utils; import secureAllocator; +import mimallocSTL; import cryptoCipher; module encryption; @@ -42,7 +43,7 @@ module encryption; /// \details The key is derived from the password using PBKDF2 with 100,000 rounds (salted). /// \details The IV is generated randomly using a CSPRNG and prepended to the ciphertext. privacy::string -encryptString(const privacy::string &plaintext, const privacy::string &password, const std::string &algo) { +encryptString(const privacy::string &plaintext, const privacy::string &password, const miSTL::string &algo) { CryptoCipher cipher; // Create the cipher context @@ -112,7 +113,7 @@ encryptString(const privacy::string &plaintext, const privacy::string &password, /// /// \throws std::runtime_error if the decryption operation fails. privacy::string -decryptString(const std::string_view encodedCiphertext, const privacy::string &password, const std::string &algo) { +decryptString(const std::string_view encodedCiphertext, const privacy::string &password, const miSTL::string &algo) { CryptoCipher cipher; // Create the cipher context @@ -134,7 +135,7 @@ decryptString(const std::string_view encodedCiphertext, const privacy::string &p privacy::vector encryptedText; // Base64 decode the encoded ciphertext - if (std::vector ciphertext = base64Decode(encodedCiphertext); + if (miSTL::vector ciphertext = base64Decode(encodedCiphertext); ciphertext.size() > static_cast(SALT_SIZE) + ivSize) [[likely]] { // Read the salt and IV from the ciphertext salt.assign(ciphertext.begin(), ciphertext.begin() + SALT_SIZE); @@ -277,7 +278,7 @@ decryptStringWithMoreRounds(const std::string_view encodedCiphertext, const priv privacy::vector encryptedText; // Base64-decode the encoded ciphertext - if (std::vector ciphertext = base64Decode(encodedCiphertext); + if (miSTL::vector ciphertext = base64Decode(encodedCiphertext); ciphertext.size() >= SALT_SIZE + ctrSize) [[likely]] { // Read the salt and the counter from the ciphertext salt.assign(ciphertext.begin(), ciphertext.begin() + SALT_SIZE); diff --git a/src/encryption/encryption.cppm b/src/encryption/encryption.cppm index fff6742..6d1284f 100644 --- a/src/encryption/encryption.cppm +++ b/src/encryption/encryption.cppm @@ -22,6 +22,7 @@ module; export module encryption; import secureAllocator; +import mimallocSTL; constexpr int SALT_SIZE = 32; // Default salt length (256 bits) constexpr int KEY_SIZE_256 = 32; // Default key size (256 bits) @@ -37,32 +38,32 @@ export { deriveKey(const privacy::string &password, const privacy::vector &salt, const int &keySize = KEY_SIZE_256); - void encryptFile(const std::string &inputFile, const std::string &outputFile, const privacy::string &password, - const std::string &algo = "AES-256-CBC"); + void encryptFile(const miSTL::string &inputFile, const miSTL::string &outputFile, const privacy::string &password, + const miSTL::string &algo = "AES-256-CBC"); void - encryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &outputFilePath, + encryptFileWithMoreRounds(const miSTL::string &inputFilePath, const miSTL::string &outputFilePath, const privacy::string &password, const gcry_cipher_algos &algorithm = GCRY_CIPHER_SERPENT256); - void decryptFile(const std::string &inputFile, const std::string &outputFile, const privacy::string &password, - const std::string &algo = "AES-256-CBC"); + void decryptFile(const miSTL::string &inputFile, const miSTL::string &outputFile, const privacy::string &password, + const miSTL::string &algo = "AES-256-CBC"); void - decryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &outputFilePath, + decryptFileWithMoreRounds(const miSTL::string &inputFilePath, const miSTL::string &outputFilePath, const privacy::string &password, const gcry_cipher_algos &algorithm = GCRY_CIPHER_SERPENT256); privacy::string encryptString(const privacy::string &plaintext, const privacy::string &password, - const std::string &algo = "AES-256-CBC"); + const miSTL::string &algo = "AES-256-CBC"); privacy::string encryptStringWithMoreRounds(const privacy::string &plaintext, const privacy::string &password, const gcry_cipher_algos &algorithm = GCRY_CIPHER_SERPENT256); privacy::string decryptString(std::string_view encodedCiphertext, const privacy::string &password, - const std::string &algo = "AES-256-CBC"); + const miSTL::string &algo = "AES-256-CBC"); privacy::string decryptStringWithMoreRounds(std::string_view encodedCiphertext, const privacy::string &password, const gcry_cipher_algos &algorithm = GCRY_CIPHER_SERPENT256); diff --git a/src/fileShredder/fileShredder.cppm b/src/fileShredder/fileShredder.cppm index 2385511..a866ace 100644 --- a/src/fileShredder/fileShredder.cppm +++ b/src/fileShredder/fileShredder.cppm @@ -32,6 +32,7 @@ using StatType = struct stat; export module fileShredder; import utils; +import mimallocSTL; namespace fs = std::filesystem; constexpr std::streamoff BUFFER_SIZE = 4096; @@ -56,7 +57,7 @@ void overwriteRandom(std::ofstream &file, const std::size_t fileSize, const int // (Re)seed the Mersenne Twister engine in every pass std::mt19937_64 gen(rd()); - std::vector buffer(BUFFER_SIZE); + miSTL::vector buffer(BUFFER_SIZE); // Overwrite the file with random data for (std::size_t pos = 0; pos < fileSize; pos += BUFFER_SIZE) { @@ -91,7 +92,7 @@ void overwriteConstantByte(std::ofstream &file, T &byte, const auto &fileSize) { // seek to the beginning of the file file.seekp(0, std::ios::beg); - std::vector buffer(BUFFER_SIZE, byte); + miSTL::vector buffer(BUFFER_SIZE, byte); for (std::streamoff pos = 0; pos < fileSize; pos += BUFFER_SIZE) { if (pos + BUFFER_SIZE > fileSize) { @@ -125,10 +126,10 @@ inline void renameAndRemove(const std::string_view filename, int numTimes = 1) { std::uniform_int_distribution numDist(minNameLength, maxNameLength); // Get the file extension using std::filesystem - const std::string fileExtension = fs::path(filename).extension().string(); + const miSTL::string fileExtension = fs::path(filename).extension().string().c_str(); // Generate a random name using the safe characters (Not exhaustive) - const std::string safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + constexpr std::string_view safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; std::uniform_int_distribution dist(0, safeChars.size() - 1); fs::path path(filename); @@ -142,7 +143,7 @@ inline void renameAndRemove(const std::string_view filename, int numTimes = 1) { // Generate a random number of characters for the new name const int numChars = numDist(gen); - std::string newName; + miSTL::string newName; // Generate a random name for (int j = 0; j < numChars; ++j) newName += safeChars[dist(gen)]; @@ -177,9 +178,9 @@ inline void renameAndRemove(const std::string_view filename, int numTimes = 1) { struct FileDescriptor { int fd{-1}; - explicit FileDescriptor(const std::string &filename) : fd(open(filename.c_str(), O_RDWR)) { + explicit FileDescriptor(const miSTL::string &filename) : fd(open(filename.c_str(), O_RDWR)) { if (fd == -1) - throw std::runtime_error("Failed to open file: " + filename + " (" + std::strerror(errno) + ")"); + throw std::runtime_error(std::format("Failed to open file: {} ({})", filename, std::strerror(errno))); } ~FileDescriptor() { if (fd != -1) close(fd); } @@ -203,7 +204,7 @@ struct FileStatInfo { /// \brief wipes the cluster tips of a file. /// \param fileName the path to the file to be wiped. /// \throws std::runtime_error if zeroing the cluster tips fails. -inline void wipeClusterTips(const std::string &fileName) { +inline void wipeClusterTips(const miSTL::string &fileName) { const FileDescriptor fileDescriptor(fileName); const FileStatInfo fileInformation(fileDescriptor.fd); @@ -220,7 +221,7 @@ inline void wipeClusterTips(const std::string &fileName) { } // Write zeros to the cluster tip - const std::vector zeroBuffer(clusterTipSize, 0); + const miSTL::vector zeroBuffer(clusterTipSize, 0); if (write(fileDescriptor.fd, zeroBuffer.data(), zeroBuffer.size()) == static_cast(-1)) { throw std::runtime_error(std::format("Failed to write zeros: ({})", std::strerror(errno))); @@ -233,10 +234,10 @@ inline void wipeClusterTips(const std::string &fileName) { /// \param wipeClusterTip whether to wipe the cluster tips of the file. /// /// \throws std::runtime_error if the file cannot be opened. -void simpleShred(const std::string &filename, const int &nPasses = 3, const bool wipeClusterTip = false) { - std::ofstream file(filename, std::ios::binary | std::ios::in); +void simpleShred(const miSTL::string &filename, const int &nPasses = 3, const bool wipeClusterTip = false) { + std::ofstream file(filename.c_str(), std::ios::binary | std::ios::in); if (!file) - throw std::runtime_error("\nFailed to open file: " + filename); + throw std::runtime_error(std::format("\nFailed to open file: {}", filename)); std::error_code ec; // Read last write time @@ -268,10 +269,10 @@ void simpleShred(const std::string &filename, const int &nPasses = 3, const bool /// \param wipeClusterTip whether to wipe the cluster tips of the file. /// /// \throws std::runtime_error if the file cannot be opened, or if the number of passes is invalid. -void dod5220Shred(const std::string &filename, const int &nPasses = 3, const bool wipeClusterTip = false) { - std::ofstream file(filename, std::ios::binary | std::ios::in); +void dod5220Shred(const miSTL::string &filename, const int &nPasses = 3, const bool wipeClusterTip = false) { + std::ofstream file(filename.c_str(), std::ios::binary | std::ios::in); if (!file) - throw std::runtime_error("\nFailed to open file: " + filename); + throw std::runtime_error(std::format("\nFailed to open file: {}", filename)); std::error_code ec; // Read last write time @@ -359,7 +360,7 @@ static inline bool addReadWritePermissions(const std::string_view fileName) noex /// /// \warning If the filePath is a directory, then all its files and subdirectories /// are shredded without warning. -bool shredFiles(const std::string &filePath, const std::uint_fast8_t &options, const int &simplePasses = 3) { +bool shredFiles(const miSTL::string &filePath, const std::uint_fast8_t &options, const int &simplePasses = 3) { std::error_code ec; const fs::file_status fileStatus = fs::status(filePath, ec); if (ec) { @@ -401,7 +402,7 @@ bool shredFiles(const std::string &filePath, const std::uint_fast8_t &options, c printColoredOutput('b', "{}", canonical(entry.path()).string()); printColoredOutput('c', " ..."); try { - const bool shredded = shredFiles(entry.path().string(), options); + const bool shredded = shredFiles(entry.path().string().c_str(), options); printColoredOutputln(shredded ? 'g' : 'r', "{}", shredded ? "\tshredded successfully." : "\tshredding failed."); @@ -575,7 +576,7 @@ export void fileShredder() { std::cout << "Shredding '"; printColoredOutput('c', "{}", canonicalPath); std::cout << "'..." << std::endl; - const bool shredded = shredFiles(path, preferences, simpleNumPass); + const bool shredded = shredFiles(path.string().c_str(), preferences, simpleNumPass); if (!isDir) { printColoredOutput(shredded ? 'g' : 'r', "{}", shredded ? "Successfully shredded " : "Failed to shred "); diff --git a/src/main.cpp b/src/main.cpp index 9c8669d..b499c55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see https://www.gnu.org/licenses. +#include #include #include #include diff --git a/src/mimallocSTL.cppm b/src/mimallocSTL.cppm new file mode 100644 index 0000000..03d6cd0 --- /dev/null +++ b/src/mimallocSTL.cppm @@ -0,0 +1,294 @@ +module; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __has_include +#if __has_include() +#include // for feature test macros +#endif + +#if __has_include() +#include +#endif + +#if __has_include() +#include +#endif + +#if __has_include() +#include +#endif + +#if __has_include() +#include +#endif + +#if __has_include() +#include +#endif + +#else // __has_include + +#if __cpp_lib_flat_set +#include +#endif + +#if __cpp_lib_flat_map +#include +#endif + +#if __cpp_lib_span +#include +#endif + +#if __cpp_lib_mdspan +#include +#endif + +#if __cpp_lib_syncbuf +#include +#endif + +#endif // __has_include + +export module mimallocSTL; + +export namespace miSTL { + /* ********** Sequence Containers ********** */ + // std::array doesn't need a custom allocator, we include it here for completeness + template + using array = std::array; + + // std::vector + template + using vector = std::vector >; + + // std::deque + template + using deque = std::deque >; + + // std::list + template + using list = std::list >; + + // std::forward_list + template + using forward_list = std::forward_list >; + + /* ********** Associative Containers ********** */ + // std::set + template > + using set = std::set >; + + // std::map + template< + class Key, + class T, + class Compare = std::less > + using map = std::map > >; + + // std::multiset + template< + class Key, + class Compare = std::less > + using multiset = std::multiset >; + + // std::multimap + template< + class Key, + class T, + class Compare = std::less > + using multimap = std::multimap > >; + + /* ********** Unordered Associative Containers ********** */ + // std::unordered_set + template< + class Key, + class Hash = std::hash, + class KeyEqual = std::equal_to > + using unordered_set = std::unordered_set >; + + // std::unordered_map + template< + class Key, + class T, + class Hash = std::hash, + class KeyEqual = std::equal_to > + using unordered_map = std::unordered_map > >; + + // std::unordered_multiset + template< + class Key, + class Hash = std::hash, + class KeyEqual = std::equal_to > + using unordered_multiset = std::unordered_multiset >; + + // std::unordered_multimap + template< + class Key, + class T, + class Hash = std::hash, + class KeyEqual = std::equal_to > + using unordered_multimap = std::unordered_multimap > >; + + /* ********** Container Adaptors ********** */ + // std::stack + template > + using stack = std::stack; + + // std::queue + template > + using queue = std::queue; + + // std::priority_queue + template< + class T, + class Container = vector, + class Compare = std::less > + using priority_queue = std::priority_queue; + + // std::flat_set (C++23) +#if __cpp_lib_flat_set + template< + class Key, + class Compare = std::less, + class KeyContainer = vector > + using flat_set = std::flat_set; +#endif + + // std::flat_map (C++23) +#if __cpp_lib_flat_map + template< + class Key, + class T, + class Compare = std::less, + class KeyContainer = vector, + class MappedContainer = vector > + using flat_map = std::flat_map; +#endif + + // std::flat_multiset (C++23) +#if __cpp_lib_flat_multiset + template< + class Key, + class Compare = std::less, + class KeyContainer = vector > + using flat_multiset = std::flat_multiset; +#endif + + // std::flat_multimap (C++23) +#if __cpp_lib_flat_multimap + template< + class Key, + class T, + class Compare = std::less, + class KeyContainer = vector, + class MappedContainer = vector > + using flat_multimap = std::flat_multimap; +#endif + + /* ********** Views ********** */ + // views do not need custom allocators, they are included here for completeness + // std::span (C++20) +#if __cpp_lib_span + template + using span = std::span; +#endif + + // std::mdspan (C++23) +#if __cpp_lib_mdspan + template< + class T, + class Extents, + class LayoutPolicy = std::layout_right, + class AccessorPolicy = std::default_accessor > + using mdspan = std::mdspan; +#endif + + /* ********** Strings ********** */ + // std::basic_string + template > + using basic_string = std::basic_string >; + + // std::string + using string = basic_string; + + // std::string_view included for completeness, as it doesn't need a custom allocator + template > + using basic_string_view = std::basic_string_view; + using string_view = basic_string_view; + + /* ********** I/O Streams ********** */ + // std::basic_stringbuf + template< + class CharT, + class Traits = std::char_traits, + class Allocator = mi_stl_allocator > + using basic_stringbuf = std::basic_stringbuf; + + // std::basic_istringstream + template< + class CharT, + class Traits = std::char_traits, + class Allocator = mi_stl_allocator > + using basic_istringstream = std::basic_istringstream; + + // std::basic_ostringstream + template< + class CharT, + class Traits = std::char_traits, + class Allocator = mi_stl_allocator > + using basic_ostringstream = std::basic_ostringstream; + + // std::basic_stringstream + template< + class CharT, + class Traits = std::char_traits, + class Allocator = mi_stl_allocator > + using basic_stringstream = std::basic_stringstream; + + // std::basic_syncbuf + template< + class CharT, + class Traits = std::char_traits, + class Allocator = mi_stl_allocator > + using basic_syncbuf = std::basic_syncbuf; + + // std::basic_osyncstream + template< + class CharT, + class Traits = std::char_traits, + class Allocator = mi_stl_allocator > + using basic_osyncstream = std::basic_osyncstream; + + // std::stringbuf + using stringbuf = basic_stringbuf; + + // std::istringstream + using istringstream = basic_istringstream; + + // std::stringstream + using stringstream = basic_stringstream; + + // std::ostringstream + using ostringstream = basic_ostringstream; + + // std::syncbuf + using syncbuf = basic_syncbuf; + + // std::osyncstream + using osyncstream = basic_osyncstream; +} // namespace miSTL diff --git a/src/passwordManager/FuzzyMatcher.cppm b/src/passwordManager/FuzzyMatcher.cppm index 56dacd9..6b748aa 100644 --- a/src/passwordManager/FuzzyMatcher.cppm +++ b/src/passwordManager/FuzzyMatcher.cppm @@ -22,6 +22,7 @@ module; export module FuzzyMatcher; import secureAllocator; +import mimallocSTL; template /// \brief A concept describing a range of strings. @@ -88,8 +89,8 @@ public: /// \param pattern the pattern to match. /// \param maxDistance the maximum Levenshtein Distance to consider a match. /// \return a vector of strings matching the pattern. - [[nodiscard]] std::vector fuzzyMatch(const std::string_view pattern, const int &maxDistance) const { - std::vector matches{}; + [[nodiscard]] miSTL::vector fuzzyMatch(const std::string_view pattern, const int &maxDistance) const { + miSTL::vector matches{}; matches.reserve(stringList.size()); // Worst case: every string in stringList is a match. // The maximum and minimum size of a string to be considered a match const auto maxSize{pattern.size() + maxDistance + 1}; @@ -110,7 +111,7 @@ public: private: - std::vector stringList{}; + miSTL::vector stringList{}; /// \brief Calculates the Levenshtein Distance between two strings. /// \param str1 the first string. @@ -122,7 +123,7 @@ private: const int m = static_cast(str1.length()); const int n = static_cast(str2.length()); - std::vector> dp(m + 1, std::vector(n + 1)); + miSTL::vector> dp(m + 1, miSTL::vector(n + 1)); // Initialize the first row and column for (int i = 0; i <= m; ++i) diff --git a/src/passwordManager/passwordManager.cpp b/src/passwordManager/passwordManager.cpp index 53e0a9a..3ad0a7f 100644 --- a/src/passwordManager/passwordManager.cpp +++ b/src/passwordManager/passwordManager.cpp @@ -31,11 +31,12 @@ module; import utils; import FuzzyMatcher; import secureAllocator; +import mimallocSTL; module passwordManager; namespace fs = std::filesystem; -using string = std::string; +using string = miSTL::string; const string DefaultPasswordFile = getHomeDir() + "/.privacyShield/passwords"; /// \brief A binary predicate for searching, sorting, and deduplication of the password records, @@ -88,7 +89,7 @@ constexpr void computeStrengths #if __clang__ || __GNUC__ [[gnu::always_inline]] #endif -(const privacy::vector &passwords, std::vector &pwStrengths) { +(const privacy::vector &passwords, miSTL::vector &pwStrengths) { pwStrengths.resize(passwords.size()); for (std::size_t i = 0; i < passwords.size(); ++i) { pwStrengths[i] = isPasswordStrong(std::get<2>(passwords[i])); @@ -96,7 +97,7 @@ constexpr void computeStrengths } /// \brief Adds a new password to the saved records. -void addPassword(privacy::vector &passwords, std::vector &strengths) { +void addPassword(privacy::vector &passwords, miSTL::vector &strengths) { privacy::string site{getResponseStr("Enter the name of the site/app: ")}; // The site name must be non-empty if (site.empty()) { @@ -160,7 +161,7 @@ void addPassword(privacy::vector &passwords, std::vector } /// \brief Generates a random password. -void generatePassword(privacy::vector &, std::vector &) { +void generatePassword(privacy::vector &, miSTL::vector &) { int length = getResponseInt("Enter the length of the password to generate: "); int tries{0}; @@ -183,7 +184,7 @@ void generatePassword(privacy::vector &, std::vector &) { } /// \brief Shows all saved passwords. -void viewAllPasswords(privacy::vector &passwords, std::vector &strengths) { +void viewAllPasswords(privacy::vector &passwords, miSTL::vector &strengths) { // Check if there are any passwords saved if (auto &&constPasswordsView = std::ranges::views::as_const(passwords); constPasswordsView.empty()) { printColoredOutputln('r', "You haven't saved any password yet."); @@ -223,7 +224,7 @@ void checkFuzzyMatches(auto &iter, privacy::vector &records, pr [](const auto &lhs, const auto &rhs) noexcept -> bool { return comparator(lhs, rhs); }); - query = std::string{match}; + query = miSTL::string{match}; } } else if (!fuzzyMatched.empty()) { // multiple matches @@ -237,7 +238,7 @@ void checkFuzzyMatches(auto &iter, privacy::vector &records, pr } /// \brief Updates a password record. -void updatePassword(privacy::vector &passwords, std::vector &strengths) { +void updatePassword(privacy::vector &passwords, miSTL::vector &strengths) { if (passwords.empty()) [[unlikely]] { // There is nothing to update printColoredErrorln('r', "No passwords saved yet."); @@ -342,7 +343,7 @@ void updatePassword(privacy::vector &passwords, std::vector &passwords, std::vector &strengths) { +void deletePassword(privacy::vector &passwords, miSTL::vector &strengths) { if (passwords.empty()) { printColoredErrorln('r', "No passwords saved yet."); return; @@ -431,7 +432,7 @@ void deletePassword(privacy::vector &passwords, std::vector &passwords, std::vector &) { +void searchPasswords(privacy::vector &passwords, miSTL::vector &) { if (passwords.empty()) [[unlikely]] { // There is nothing to search printColoredErrorln('r', "No passwords saved yet."); @@ -500,7 +501,7 @@ void searchPasswords(privacy::vector &passwords, std::vector &passwords, std::vector &strengths) { +void importPasswords(privacy::vector &passwords, miSTL::vector &strengths) { const fs::path fileName = getFilesystemPath("Enter the path to the csv file: "); privacy::vector imports{importCsv(fileName)}; @@ -585,7 +586,7 @@ void importPasswords(privacy::vector &passwords, std::vector &passwords, std::vector &) { +void exportPasswords(privacy::vector &passwords, miSTL::vector &) { auto &&constPasswordsView = std::as_const(passwords); if (constPasswordsView.empty()) [[unlikely]] { @@ -605,7 +606,7 @@ void exportPasswords(privacy::vector &passwords, std::vector &passwords, std::vector &strengths) { +void analyzePasswords(privacy::vector &passwords, miSTL::vector &strengths) { if (passwords.empty()) { printColoredOutputln('r', "No passwords to analyze."); return; @@ -627,7 +628,7 @@ void analyzePasswords(privacy::vector &passwords, std::vector > passwordMap; + miSTL::unordered_map > passwordMap; for (const auto &[site, _, password]: constPasswordsView) { // Add the site to the set of sites that use the password passwordMap[password].insert(site); @@ -647,7 +648,7 @@ void analyzePasswords(privacy::vector &passwords, std::vector >; + using PasswordSites = std::pair >; std::multimap > countMap; for (const auto &[password, sites]: passwordMap) { @@ -692,7 +693,7 @@ void analyzePasswords(privacy::vector &passwords, std::vector passwordStrength(passwords.size(), false); + miSTL::vector passwordStrength(passwords.size(), false); for (std::size_t i = 0; i < passwords.size(); ++i) { passwordStrength[i] = isPasswordStrong(std::get<2>(passwords[i])); } // A map of choices and their corresponding functions - std::unordered_map &, std::vector &)> choices = { + miSTL::unordered_map &, miSTL::vector &)> choices = { {1, addPassword}, {2, updatePassword}, {3, deletePassword}, diff --git a/src/passwordManager/passwordManager.cppm b/src/passwordManager/passwordManager.cppm index cce4132..b7ed75b 100644 --- a/src/passwordManager/passwordManager.cppm +++ b/src/passwordManager/passwordManager.cppm @@ -25,6 +25,7 @@ export module passwordManager; import utils; import secureAllocator; +import mimallocSTL; using passwordRecords = std::tuple; @@ -39,7 +40,7 @@ privacy::string generatePassword(int length); bool changePrimaryPassword(privacy::string &primaryPassword); -std::pair initialSetup() noexcept; +std::pair initialSetup() noexcept; privacy::string getHash(std::string_view filePath); diff --git a/src/passwordManager/passwords.cpp b/src/passwordManager/passwords.cpp index 4115526..9b4aa15 100644 --- a/src/passwordManager/passwords.cpp +++ b/src/passwordManager/passwords.cpp @@ -29,6 +29,7 @@ module; import utils; import encryption; import secureAllocator; +import mimallocSTL; module passwordManager; @@ -78,7 +79,7 @@ privacy::string generatePassword(const int length) { throw std::length_error("Password too long."); // generate from a set of printable ascii characters - const std::string characters = + constexpr std::string_view characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()-=_~+[]{}<>"; // Seed the Mersenne Twister engine with a random source (ideally non-deterministic) @@ -155,7 +156,7 @@ encryptDecryptRange(privacy::vector &passwords, const privacy:: for (std::size_t i = start; i < end; ++i) { std::get<2>(passwords[i]) = encrypt ? encryptStringWithMoreRounds(std::get<2>(passwords[i]), key) - : decryptStringWithMoreRounds(std::string{std::get<2>(passwords[i])}, + : decryptStringWithMoreRounds(miSTL::string{std::get<2>(passwords[i])}, key); } } catch (const std::exception &ex) { @@ -183,15 +184,15 @@ encryptDecryptRangeAllFields(privacy::vector &passwords, const for (std::size_t i = start; i < end; ++i) { std::get<0>(passwords[i]) = encrypt ? encryptString(std::get<0>(passwords[i]), key) - : decryptString(std::string{std::get<0>(passwords[i])}, key); + : decryptString(miSTL::string{std::get<0>(passwords[i])}, key); std::get<1>(passwords[i]) = encrypt ? encryptString(std::get<1>(passwords[i]), key) - : decryptString(std::string{std::get<1>(passwords[i])}, key); + : decryptString(miSTL::string{std::get<1>(passwords[i])}, key); std::get<2>(passwords[i]) = encrypt ? encryptString(std::get<2>(passwords[i]), key) - : decryptString(std::string{std::get<2>(passwords[i])}, key); + : decryptString(miSTL::string{std::get<2>(passwords[i])}, key); } } catch (const std::exception &ex) { printColoredErrorln('r', "Error: {}", ex.what()); @@ -212,7 +213,7 @@ encryptDecryptConcurrently(privacy::vector &passwordEntries, co const unsigned int numThreads{std::jthread::hardware_concurrency() ? std::jthread::hardware_concurrency() : 8}; // Divide the password entries among threads - std::vector threads; + miSTL::vector threads; const std::size_t passPerThread = numPasswords / numThreads; std::size_t start = 0; @@ -259,7 +260,7 @@ inline void checkCommonErrors(const std::string_view path) { /// \return True, if successful. bool savePasswords(privacy::vector &passwords, const std::string_view filePath, const privacy::string &encryptionKey) { - auto tempFile = std::string{filePath} + "XXXXXX"; + auto tempFile = miSTL::string{filePath} + "XXXXXX"; // Create a temporary file // If the temporary file couldn't be created, use the original file path @@ -267,7 +268,7 @@ bool savePasswords(privacy::vector &passwords, const std::strin tempFile = filePath; else close(tmpFileFd); // Close the file descriptor - std::ofstream file(tempFile, std::ios::trunc); + std::ofstream file(tempFile.c_str(), std::ios::trunc); if (!file) { try { checkCommonErrors(tempFile); @@ -407,8 +408,8 @@ bool changePrimaryPassword(privacy::string &primaryPassword) { /// \brief Helps with the initial setup of the password manager. /// \return New primary password and/or path to the password file, whichever is applicable. -std::pair initialSetup() noexcept { - std::pair ret{"", ""}; // ret.first = path to file, ret.second = new primary password +std::pair initialSetup() noexcept { + std::pair ret{"", ""}; // ret.first = path to file, ret.second = new primary password std::cout << "Looks like you don't have any passwords saved yet." << std::endl; @@ -459,7 +460,7 @@ std::pair initialSetup() noexcept { continue; } - ret.first = path; + ret.first = path.string().c_str(); break; } if (resp == 3) return ret; @@ -569,7 +570,7 @@ bool exportCsv(const privacy::vector &records, const std::files /// \brief Trims space (whitespace) off the beginning and end of a string. /// \param str the string to trim. -inline void trim(std::string &str) { +inline void trim(miSTL::string &str) { constexpr std::string_view space = " \t\n\r\f\v"; // Trim the leading space @@ -604,7 +605,7 @@ privacy::vector importCsv(const fs::path &filePath) { while (std::getline, privacy::Allocator >(file, line)) { privacy::istringstream iss(line); - privacy::vector tokens; + privacy::vector tokens; while (std::getline, privacy::Allocator >(iss, value, ',')) tokens.emplace_back(value); diff --git a/src/privacyTracks/privacyTracks.cppm b/src/privacyTracks/privacyTracks.cppm index 708b2e1..e061c6f 100644 --- a/src/privacyTracks/privacyTracks.cppm +++ b/src/privacyTracks/privacyTracks.cppm @@ -26,6 +26,7 @@ module; export module privacyTracks; import utils; +import mimallocSTL; namespace fs = std::filesystem; @@ -64,12 +65,12 @@ std::uint_fast8_t detectBrowsers(const std::string_view pathEnv) { } // Split the PATH variable into individual paths - std::string pathEnvStr{pathEnv}; - std::vector paths; + miSTL::string pathEnvStr{pathEnv}; + miSTL::vector paths; paths.reserve(256); std::size_t pos; - while ((pos = pathEnvStr.find(':')) != std::string::npos) { + while ((pos = pathEnvStr.find(':')) != miSTL::string::npos) { paths.emplace_back(pathEnvStr.substr(0, pos)); pathEnvStr.erase(0, pos + 1); } @@ -133,7 +134,7 @@ bool clearFirefoxTracks(const std::string_view configDir) { std::error_code ec; // Find all default profiles - std::vector defaultProfileDirs; + miSTL::vector defaultProfileDirs; for (const auto &entry: fs::directory_iterator(configDir, fs::directory_options::skip_permission_denied | fs::directory_options::follow_directory_symlink, ec)) { handleFileError(ec, "reading", configDir); @@ -159,7 +160,7 @@ bool clearFirefoxTracks(const std::string_view configDir) { } else printColoredErrorln('r', "No default profiles found."); // Treat the other directories as profiles - std::vector profileDirs; + miSTL::vector profileDirs; for (const auto &entry: fs::directory_iterator(configDir, fs::directory_options::skip_permission_denied | fs::directory_options::follow_directory_symlink, ec)) { handleFileError(ec, "reading", configDir); @@ -257,7 +258,7 @@ bool clearChromiumTracks(const std::string_view configDir) { } else printColoredErrorln('r', "Default profile directory not found."); // Find other profile directories - std::vector profileDirs; + miSTL::vector profileDirs; for (const auto &entry: fs::directory_iterator(configDir, fs::directory_options::skip_permission_denied | fs::directory_options::follow_directory_symlink, ec)) { handleFileError(ec, "reading", configDir); @@ -338,7 +339,7 @@ bool clearOperaTracks(const std::string_view profilePath) { ec.clear(); fs::remove(fs::path{profilePath} / "cookies", ec); if (ec) { - handleFileError(ec, "deleting", std::string{profilePath} + "/cookies"); + handleFileError(ec, "deleting", miSTL::string{profilePath} + "/cookies"); ec.clear(); ret = false; // We don't to return yet, we want to try to clear history too } @@ -350,7 +351,7 @@ bool clearOperaTracks(const std::string_view profilePath) { ec.clear(); fs::remove(fs::path{profilePath} / "history", ec); if (ec) { - handleFileError(ec, "deleting", std::string{profilePath} + "/history"); + handleFileError(ec, "deleting", miSTL::string{profilePath} + "/history"); ec.clear(); return false; // No point in continuing } @@ -402,7 +403,7 @@ bool clearOperaTracks() { /// \return true if successful, false otherwise. bool clearSafariTracks() { #if __APPLE__ - const std::string cookiesPath = getHomeDir() + "/Library/Cookies"; + const miSTL::string cookiesPath = getHomeDir() + "/Library/Cookies"; if (!fs::exists(cookiesPath)) { printColoredErrorln('r', "Safari cookies directory not found."); return false; @@ -418,7 +419,7 @@ bool clearSafariTracks() { } } - const std::string historyPath = getHomeDir() + "/Library/Safari"; + const miSTL::string historyPath = getHomeDir() + "/Library/Safari"; if (!fs::exists(historyPath)) { printColoredErrorln('c', "Safari history directory not found."); return false; diff --git a/src/secureAllocator.cppm b/src/secureAllocator.cppm index 3201ed9..aecfd75 100644 --- a/src/secureAllocator.cppm +++ b/src/secureAllocator.cppm @@ -21,6 +21,7 @@ module; #include #include #include +#include export module secureAllocator; @@ -45,15 +46,15 @@ export namespace privacy { /// Copy constructor template - constexpr explicit Allocator(const Allocator &) noexcept {} + constexpr explicit Allocator(const Allocator &) noexcept { + } /// Allocate memory - [[maybe_unused]] [[nodiscard]] constexpr T *allocate(std::size_t n) { + [[maybe_unused]] [[nodiscard]] constexpr T *allocate(const std::size_t n) { if (n > std::numeric_limits::max() / sizeof(T)) throw std::bad_array_new_length(); - if (auto p = static_cast(::operator new(n * sizeof(T)))) { - sodium_mlock(p, n * sizeof(T)); // Lock the allocated memory + if (auto p = static_cast(sodium_malloc(n * sizeof(T)))) { return p; } @@ -61,9 +62,8 @@ export namespace privacy { } /// Deallocate memory - [[maybe_unused]] constexpr void deallocate(T *p, std::size_t n) noexcept { - sodium_munlock(p, n * sizeof(T)); // Unlock and zeroize memory - ::operator delete(p); + [[maybe_unused]] static constexpr void deallocate(T *p, const std::size_t n [[maybe_unused]]) noexcept { + sodium_free(p); } }; @@ -83,7 +83,7 @@ export namespace privacy { using string = std::basic_string, Allocator >; template - using vector = std::vector>; + using vector = std::vector >; using istringstream = std::basic_istringstream, Allocator >; } // namespace privacy diff --git a/src/utils/utils.cppm b/src/utils/utils.cppm index 9fd2282..ca54eb0 100644 --- a/src/utils/utils.cppm +++ b/src/utils/utils.cppm @@ -34,6 +34,7 @@ module; export module utils; import secureAllocator; +import mimallocSTL; namespace fs = std::filesystem; @@ -220,7 +221,7 @@ export { /// \return Base64-encoded string. /// \throws std::bad_alloc if memory allocation fails. /// \throws std::runtime_error if encoding fails. - std::string base64Encode(const uCharVector auto &input) { + miSTL::string base64Encode(const uCharVector auto &input) { // Create a BIO object to encode the data const std::unique_ptr b64(BIO_new(BIO_f_base64()), &BIO_free_all); if (b64 == nullptr) @@ -249,7 +250,7 @@ export { BIO_get_mem_ptr(b64.get(), &bufferPtr); // Create a string from the data - std::string encodedData(bufferPtr->data, bufferPtr->length); + miSTL::string encodedData(bufferPtr->data, bufferPtr->length); return encodedData; } @@ -282,7 +283,7 @@ export { /// \return a vector of the decoded binary data. /// \throws std::bad_alloc if memory allocation fails. /// \throws std::runtime_error if the decoding operation fails. - std::vector base64Decode(const std::string_view encodedData) { + miSTL::vector base64Decode(const std::string_view encodedData) { // Create a BIO object to decode the data std::unique_ptr bio( BIO_new_mem_buf(encodedData.data(), static_cast(encodedData.size())), &BIO_free_all); @@ -300,7 +301,7 @@ export { // Push the base64 BIO to the memory BIO bio.reset(BIO_push(b64, bio.release())); // Transfer ownership to bio - std::vector decodedData(encodedData.size()); + miSTL::vector decodedData(encodedData.size()); // Decode the data const int len = BIO_read(bio.get(), decodedData.data(), static_cast(decodedData.size())); @@ -318,7 +319,7 @@ export { /// from the standard input. /// \param prompt The prompt to display to the user. /// \return The response string entered by the user if successful, else an empty string. - std::string getResponseStr(const char *prompt = "") { + miSTL::string getResponseStr(const char *prompt = "") { std::scoped_lock lock(termutex); // Disable completions ic_set_default_completer(null_completer, nullptr); @@ -326,7 +327,7 @@ export { // Read the response from the user std::puts(prompt); if (char *input = ic_readline("")) { - std::string result{input}; + miSTL::string result{input}; std::free(input); stripString(result); return result; @@ -417,7 +418,7 @@ export { /// to the current user. /// \param filename the path to the file. /// \return true if the current user has write permissions, else false. - bool isWritable(const std::string &filename) { + bool isWritable(const miSTL::string &filename) { return access(filename.c_str(), F_OK | W_OK) == 0; } @@ -425,7 +426,7 @@ export { /// to the current user. /// \param filename the path to the file. /// \return true if the current user has read permissions, else false. - bool isReadable(const std::string &filename) { + bool isReadable(const miSTL::string &filename) { return access(filename.c_str(), F_OK | R_OK) == 0; } @@ -477,7 +478,7 @@ export { /// \param prompt The confirmation prompt. /// \return True if the user confirms the action, else false. bool validateYesNo(const char *prompt = "") { - const std::string resp = getResponseStr(prompt); + const miSTL::string resp = getResponseStr(prompt); if (resp.empty()) return false; return std::tolower(resp.at(0)) == 'y'; } @@ -486,7 +487,7 @@ export { /// \param var an environment variable to query. /// \return the value of the environment variable if it exists, else nullopt (nothing). /// \note The returned value MUST be checked before access. - std::optional getEnv(const char *const var) { + std::optional getEnv(const char *const var) { // Use secure_getenv() if available #if _GNU_SOURCE if (const char *value = secure_getenv(var)) @@ -502,7 +503,7 @@ export { /// \return The home directory read from {'HOME', 'USERPROFILE'} /// environment variables, else the current working directory (or an empty /// string if the current directory couldn't be determined). - std::string getHomeDir() noexcept { + miSTL::string getHomeDir() noexcept { std::error_code ec; // Try to get the home directory from the environment variables if (const auto envHome = getEnv("HOME"); envHome) @@ -513,7 +514,7 @@ export { // If the environment variables are not set, use the current working directory std::cerr << "\nCouldn't find your home directory, using the current working directory instead.." << std::endl; - std::string currentDir = std::filesystem::current_path(ec); + miSTL::string currentDir = std::filesystem::current_path(ec).string().c_str(); if (ec) std::cerr << ec.message() << std::endl; return currentDir;