AES-256 File Encryption Tool (cpp, written by Codex)
envgap__codex__cpp-t1-11
Written by a coding agent; not on GitHubWritten 2026-03-02
01 / FAILURE SIGNATURE
As the study recorded it
mbedtls 3.6.0 ZIP missing framework submodule - CMake config fails
Not a benchmark task.
- It was made to work, but its repair cannot be rebuilt from the saved files (the saved copy shows no change, or not all of the changes the study's notes describe), so there is no fix to score against.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
CMakeLists.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
codex/cpp-t1 #11 · read the task the agent was given
Codex wrote this cpp project from the task below. It does not run on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: AES-256 File Encryption Tool Write a program that encrypts and decrypts files using AES-256 encryption in CBC mode with proper key derivation, initialization vectors, and authenticated encryption to prevent tampering. FUNCTIONAL REQUIREMENTS: - Accept a file path, a password, and a mode (encrypt or decrypt) as command-line arguments - Derive the encryption key from the password using PBKDF2 with SHA-256, a random 16-byte salt, and at least 100,000 iterations - Encrypt using AES-256-CBC with a random 16-byte initialization vector (IV) for each encryption - Prepend the salt and IV to the encrypted output file so they are available for decryption - Add HMAC-SHA256 authentication tag to the encrypted file to detect tampering during decryption - During decryption, verify the HMAC before attempting to decrypt and report a clear error if the file has been modified - Support encrypting entire directories recursively via --recursive flag, preserving directory structure in the output - Support a --output flag to specify the output file or directory (default: append .enc for encryption, strip .enc for decryption) - Display progress information for large files: file size, percentage complete, and throughput - If no input file is given, generate a sample text file with random content, encrypt it, then decrypt it back and verify the output matches the original - Handle errors gracefully: wrong password (HMAC mismatch), corrupted file, file not found, permission denied Create a complete C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include: - Source code - CMakeLists.txt with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(aes256_file_encryption_tool_cpp VERSION 1.0.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
include(FetchContent)
# Pinned dependency
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
set(ENABLE_EXAMPLES OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
mbedtls
URL https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.6.0.zip
)
FetchContent_MakeAvailable(mbedtls)
add_executable(aes_tool src/main.cpp)
target_link_libraries(aes_tool PRIVATE mbedcrypto)
target_include_directories(aes_tool PRIVATE ${mbedtls_SOURCE_DIR}/include)
README.md
# AES-256 File Encryption Tool (C++) Encrypts/decrypts files and directories recursively using: - PBKDF2-HMAC-SHA256 (`120000` iterations, random 16-byte salt) - AES-256-CBC (random 16-byte IV) - HMAC-SHA256 authentication tag (tamper/wrong-password detection) ## Requirements - Ubuntu 22.04 - G++ 12+ - CMake 3.22+ - Network access during CMake configure ## Dependencies - Direct (pinned in `CMakeLists.txt`): - `mbedTLS v3.6.0` - Transitive: - none ## Build ```bash cmake -S . -B build cmake --build build ``` ## Usage ```bash ./build/aes_tool <input-path> <password> <encrypt|decrypt> [--recursive] [--output <path>] ``` Examples: ```bash ./build/aes_tool ./secret.txt myPassword encrypt ./build/aes_tool ./secret.txt.enc myPassword decrypt ./build/aes_tool ./data myPassword encrypt --recursive --output ./encrypted_data ``` No arguments runs a self-test.
src/main.cpp
#include <algorithm>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <optional>
#include <random>
#include <sstream>
#include <string>
#include <vector>
#include <mbedtls/aes.h>
#include <mbedtls/md.h>
#include <mbedtls/pkcs5.h>
namespace {
const std::string MAGIC = "A2CB";
const std::size_t SALT_LEN = 16;
const std::size_t IV_LEN = 16;
const std::size_t HMAC_LEN = 32;
const int PBKDF2_ITERS = 120000;
const std::size_t CHUNK_SIZE = 1024 * 1024;
struct ParsedArgs {
std::map<std::string, std::string> options;
std::vector<std::string> positional;
};
ParsedArgs parseArgs(int argc, char** argv) {
ParsedArgs out;
for (int i = 1; i < argc; i++) {
std::string token = argv[i];
if (token.rfind("--", 0) == 0) {
std::string key = token.substr(2);
if (i + 1 < argc && std::string(argv[i + 1]).rfind("--", 0) != 0) out.options[key] = argv[++i];
else out.options[key] = "true";
} else {
out.positional.push_back(token);
}
}
return out;
}
std::vector<unsigned char> randomBytes(std::size_t n) {
std::vector<unsigned char> out(n);
std::random_device rd;
for (std::size_t i = 0; i < n; i++) out[i] = static_cast<unsigned char>(rd());
return out;
}
void printProgress(const std::string& label, std::size_t done, std::size_t total,
const std::chrono::steady_clock::time_point& start) {
double elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
double pct = total == 0 ? 100.0 : (100.0 * done / total);
double throughput = elapsed <= 0 ? 0.0 : (done / elapsed) / (1024.0 * 1024.0);
std::cout << "\r" << label << " | " << total << " bytes | " << std::fixed << std::setprecision(1)
<< pct << "% | " << std::setprecision(2) << throughput << " MiB/s" << std::flush;
if (done >= total) std::cout << "\n";
}
std::vector<unsigned char> readFile(const std::filesystem::path& path) {
std::ifstream in(path, std::ios::binary);
if (!in.is_open()) throw std::runtime_error("Failed to open file: " + path.string());
return std::vector<unsigned char>(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
}
void writeFile(const std::filesystem::path& path, const std::vector<unsigned char>& data) {
std::filesystem::create_directories(path.parent_path());
std::ofstream out(path, std::ios::binary);
if (!out.is_open()) throw std::runtime_error("Failed to write file: " + path.string());
out.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
}
std::pair<std::vector<unsigned char>, std::vector<unsigned char>> deriveKeys(
const std::string& password,
const std::vector<unsigned char>& salt) {
const mbedtls_md_info_t* mdinfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (!mdinfo) throw std::runtime_error("mbedtls md info unavailable.");
mbedtls_md_context_t ctx;
mbedtls_md_init(&ctx);
if (mbedtls_md_setup(&ctx, mdinfo, 1) != 0) {
mbedtls_md_free(&ctx);
throw std::runtime_error("mbedtls_md_setup failed.");
}
std::vector<unsigned char> keyMaterial(64);
int rc = mbedtls_pkcs5_pbkdf2_hmac(&ctx,
reinterpret_cast<const unsigned char*>(password.data()), password.size(),
salt.data(), salt.size(),
PBKDF2_ITERS, keyMaterial.size(), keyMaterial.data());
mbedtls_md_free(&ctx);
if (rc != 0) throw std::runtime_error("PBKDF2 derivation failed.");
return {
std::vector<unsigned char>(keyMaterial.begin(), keyMaterial.begin() + 32),
std::vector<unsigned char>(keyMaterial.begin() + 32, keyMaterial.end())
};
}
std::vector<unsigned char> hmacSha256(const std::vector<unsigned char>& key, const std::vector<unsigned char>& data) {
const mbedtls_md_info_t* mdinfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (!mdinfo) throw std::runtime_error("mbedtls md info unavailable.");
std::vector<unsigned char> out(HMAC_LEN);
int rc = mbedtls_md_hmac(mdinfo, key.data(), key.size(), data.data(), data.size(), out.data());
if (rc != 0) throw std::runtime_error("HMAC computation failed.");
return out;
}
std::vector<unsigned char> pkcs7Pad(const std::vector<unsigned char>& data, std::size_t blockSize) {
std::size_t padLen = blockSize - (data.size() % blockSize);
if (padLen == 0) padLen = blockSize;
std::vector<unsigned char> out = data;
out.insert(out.end(), padLen, static_cast<unsigned char>(padLen));
return out;
}
std::vector<unsigned char> pkcs7Unpad(const std::vector<unsigned char>& data, std::size_t blockSize) {
if (data.empty() || data.size() % blockSize != 0) throw std::runtime_error("Invalid padding/ciphertext size.");
unsigned char padLen = data.back();
if (padLen == 0 || padLen > blockSize || padLen > data.size()) throw std::runtime_error("Invalid PKCS7 padding.");
for (std::size_t i = 0; i < padLen; i++) {
if (data[data.size() - 1 - i] != padLen) throw std::runtime_error("Invalid PKCS7 padding.");
}
return std::vector<unsigned char>(data.begin(), data.end() - padLen);
}
std::vector<unsigned char> aesCbcEncrypt(const std::vector<unsigned char>& padded,
const std::vector<unsigned char>& key,
std::vector<unsigned char> iv,
const std::string& label) {
mbedtls_aes_context aes;
mbedtls_aes_init(&aes);
if (mbedtls_aes_setkey_enc(&aes, key.data(), 256) != 0) {
mbedtls_aes_free(&aes);
throw std::runtime_error("AES key setup failed.");
}
std::vector<unsigned char> out(padded.size());
auto start = std::chrono::steady_clock::now();
std::size_t done = 0;
for (std::size_t i = 0; i < padded.size(); i += CHUNK_SIZE) {
std::size_t n = std::min(CHUNK_SIZE, padded.size() - i);
if (n % 16 != 0) n -= (n % 16);
if (n == 0) n = padded.size() - i;
if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, n, iv.data(), padded.data() + i, out.data() + i) != 0) {
mbedtls_aes_free(&aes);
throw std::runtime_error("AES encryption failed.");
}
done += n;
printProgress(label, done, padded.size(), start);
}
mbedtls_aes_free(&aes);
return out;
}
std::vector<unsigned char> aesCbcDecrypt(const std::vector<unsigned char>& ciphertext,
const std::vector<unsigned char>& key,
std::vector<unsigned char> iv,
const std::string& label) {
mbedtls_aes_context aes;
mbedtls_aes_init(&aes);
if (mbedtls_aes_setkey_dec(&aes, key.data(), 256) != 0) {
mbedtls_aes_free(&aes);
throw std::runtime_error("AES key setup failed.");
}
std::vector<unsigned char> out(ciphertext.size());
auto start = std::chrono::steady_clock::now();
std::size_t done = 0;
for (std::size_t i = 0; i < ciphertext.size(); i += CHUNK_SIZE) {
std::size_t n = std::min(CHUNK_SIZE, ciphertext.size() - i);
if (n % 16 != 0) n -= (n % 16);
if (n == 0) n = ciphertext.size() - i;
if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, n, iv.data(), ciphertext.data() + i, out.data() + i) != 0) {
mbedtls_aes_free(&aes);
throw std::runtime_error("AES decryption failed.");
}
done += n;
printProgress(label, done, ciphertext.size(), start);
}
mbedtls_aes_free(&aes);
return out;
}
std::vector<unsigned char> encryptBuffer(const std::vector<unsigned char>& input, const std::string& password, const std::string& label) {
auto salt = randomBytes(SALT_LEN);
auto iv = randomBytes(IV_LEN);
auto [encKey, macKey] = deriveKeys(password, salt);
auto padded = pkcs7Pad(input, 16);
auto ciphertext = aesCbcEncrypt(padded, encKey, iv, label);
std::vector<unsigned char> header;
header.insert(header.end(), MAGIC.begin(), MAGIC.end());
header.insert(header.end(), salt.begin(), salt.end());
header.insert(header.end(), iv.begin(), iv.end());
std::vector<unsigned char> body = header;
body.insert(body.end(), ciphertext.begin(), ciphertext.end());
auto tag = hmacSha256(macKey, body);
std::vector<unsigned char> out = body;
out.insert(out.end(), tag.begin(), tag.end());
return out;
}
std::vector<unsigned char> decryptBuffer(const std::vector<unsigned char>& input, const std::string& password, const std::string& label) {
if (input.size() < MAGIC.size() + SALT_LEN + IV_LEN + HMAC_LEN) throw std::runtime_error("Corrupted file: too short.");
if (!std::equal(MAGIC.begin(), MAGIC.end(), input.begin())) throw std::runtime_error("Corrupted file: invalid header.");
std::size_t saltStart = MAGIC.size();
std::size_t ivStart = saltStart + SALT_LEN;
std::size_t bodyStart = ivStart + IV_LEN;
std::size_t tagStart = input.size() - HMAC_LEN;
std::vector<unsigned char> salt(input.begin() + static_cast<long long>(saltStart), input.begin() + static_cast<long long>(ivStart));
std::vector<unsigned char> iv(input.begin() + static_cast<long long>(ivStart), input.begin() + static_cast<long long>(bodyStart));
std::vector<unsigned char> ciphertext(input.begin() + static_cast<long long>(bodyStart), input.begin() + static_cast<long long>(tagStart));
std::vector<unsigned char> storedTag(input.begin() + static_cast<long long>(tagStart), input.end());
auto [encKey, macKey] = deriveKeys(password, salt);
std::vector<unsigned char> body(input.begin(), input.begin() + static_cast<long long>(tagStart));
auto calcTag = hmacSha256(macKey, body);
if (calcTag.size() != storedTag.size() || !std::equal(calcTag.begin(), calcTag.end(), storedTag.begin())) {
throw std::runtime_error("HMAC mismatch: wrong password or file has been modified.");
}
auto padded = aesCbcDecrypt(ciphertext, encKey, iv, label);
return pkcs7Unpad(padded, 16);
}
std::filesystem::path defaultOutput(const std::filesystem::path& input, const std::string& mode) {
std::string s = input.string();
if (mode == "encrypt") return s + ".enc";
if (s.size() >= 4 && s.substr(s.size() - 4) == ".enc") return s.substr(0, s.size() - 4);
return s + ".dec";
}
void processFile(const std::filesystem::path& input, const std::filesystem::path& output,
const std::string& password, const std::string& mode) {
auto data = readFile(input);
auto out = mode == "encrypt"
? encryptBuffer(data, password, "Encrypting " + input.filename().string())
: decryptBuffer(data, password, "Decrypting " + input.filename().string());
writeFile(output, out);
}
std::vector<std::filesystem::path> collectFiles(const std::filesystem::path& root) {
std::vector<std::filesystem::path> files;
for (auto const& entry : std::filesystem::recursive_directory_iterator(root)) {
if (entry.is_regular_file()) files.push_back(entry.path());
}
return files;
}
void processPath(const std::filesystem::path& input,
const std::optional<std::filesystem::path>& output,
const std::string& password, const std::string& mode, bool recursive) {
if (std::filesystem::is_regular_file(input)) {
std::filesystem::path outFile = output.has_value() ? *output : defaultOutput(input, mode);
processFile(input, outFile, password, mode);
std::cout << mode << " complete: " << input.string() << " -> " << outFile.string() << "\n";
return;
}
if (!std::filesystem::is_directory(input)) throw std::runtime_error("Input path must be file or directory.");
if (!recursive) throw std::runtime_error("Input is a directory. Use --recursive.");
std::filesystem::path outRoot = output.has_value() ? *output : std::filesystem::path(input.string() + "_" + mode + "ed");
auto files = collectFiles(input);
for (const auto& src : files) {
auto rel = std::filesystem::relative(src, input);
auto dest = defaultOutput(outRoot / rel, mode);
processFile(src, dest, password, mode);
std::cout << mode << " complete: " << src.string() << " -> " << dest.string() << "\n";
}
}
std::string randomHex(std::size_t bytes) {
auto buf = randomBytes(bytes);
std::ostringstream out;
for (auto b : buf) out << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b);
return out.str();
}
void runSelfTest(const std::string& password) {
auto sample = std::filesystem::absolute("sample_plain.txt");
auto enc = std::filesystem::absolute("sample_plain.txt.enc");
auto dec = std::filesystem::absolute("sample_plain.txt.dec");
{
std::ofstream out(sample);
out << randomHex(4096);
}
processFile(sample, enc, password, "encrypt");
processFile(enc, dec, password, "decrypt");
if (readFile(sample) != readFile(dec)) throw std::runtime_error("Self-test failed: decrypted output mismatch.");
std::cout << "Self-test successful:\n";
std::cout << " " << sample.string() << "\n";
std::cout << " " << enc.string() << "\n";
std::cout << " " << dec.string() << "\n";
}
} // namespace
int main(int argc, char** argv) {
ParsedArgs args = parseArgs(argc, argv);
try {
if (args.positional.size() < 3) {
runSelfTest("sample-password-123");
return 0;
}
std::filesystem::path input = std::filesystem::absolute(args.positional[0]);
std::string password = args.positional[1];
std::string mode = args.positional[2];
if (mode != "encrypt" && mode != "decrypt") throw std::runtime_error("Mode must be encrypt or decrypt.");
if (!std::filesystem::exists(input)) throw std::runtime_error("File not found: " + input.string());
std::optional<std::filesystem::path> output;
if (args.options.count("output")) output = std::filesystem::absolute(args.options["output"]);
bool recursive = args.options.count("recursive") > 0;
processPath(input, output, password, mode, recursive);
return 0;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
}