From 2e2f78720e53feeb567f9af6eb7a26aa49299c7a Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Tue, 13 Jan 2026 03:13:54 -0500 Subject: [PATCH] Added exponential padding to increase security. --- README.md | 25 +++- src/archive.c | 13 +- src/crypto.c | 285 ++++++++++++++++++++++++++++-------------- src/main.h | 12 +- src/padding.c | 92 ++++++++++++++ tests/test_padding.sh | 129 +++++++++++++++++++ 6 files changed, 459 insertions(+), 97 deletions(-) create mode 100644 src/padding.c create mode 100755 tests/test_padding.sh diff --git a/README.md b/README.md index 626889e..c7bd768 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ One-time pads can be trivially encrypted and decrypted using pencil and paper, m ## Features - **Perfect Security**: Implements true one-time pad encryption with information-theoretic security +- **Traffic Analysis Resistance**: Exponential bucketing with ISO/IEC 9797-1 Method 2 (Padmé) padding hides message lengths - **Text & File Encryption**: Supports both inline text and file encryption - **Multiple Output Formats**: Binary (.otp) and ASCII armored (.otp.asc) file formats - **Hardware RNG Support**: Direct entropy collection from TrueRNG USB devices with automatic detection @@ -58,14 +59,14 @@ One-time pads can be trivially encrypted and decrypted using pencil and paper, m ### Download Pre-Built Binaries -**[Download Current Linux x86](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.47/otp-v0.3.47-linux-x86_64)** +**[Download Current Linux x86](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.48/otp-v0.3.48-linux-x86_64)** -**[Download Current Raspberry Pi 64](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.47/otp-v0.3.47-linux-arm64)** +**[Download Current Raspberry Pi 64](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.48/otp-v0.3.48-linux-arm64)** After downloading: ```bash # Rename for convenience, then make executable -mv otp-v0.3.47-linux-x86_64 otp +mv otp-v0.3.48-linux-x86_64 otp chmod +x otp # Run it @@ -189,8 +190,23 @@ git tag v1.0.0 # Next build: v1.0.1 - Custom 256-bit XOR checksum for pad identification (encrypted with pad data) - Read-only pad files to prevent accidental modification - State tracking to prevent pad reuse +- **Message Length Hiding**: Exponential bucketing (256B, 512B, 1KB, 2KB, 4KB...) prevents traffic analysis +- **ISO/IEC 9797-1 Method 2 Padding**: Standard-compliant Padmé padding with 0x80 marker - **Zero external crypto dependencies** - completely self-contained implementation +### Message Padding + +All encrypted messages and files are automatically padded using exponential bucketing to resist traffic analysis attacks: + +- **Minimum size**: 256 bytes +- **Bucket sizes**: 256B → 512B → 1KB → 2KB → 4KB → 8KB → ... +- **Padding method**: ISO/IEC 9797-1 Method 2 (Padmé padding) + - Appends `0x80` byte after message + - Fills remaining space with `0x00` bytes + - Unambiguous padding removal during decryption + +**Example**: A 10-byte message is padded to 256 bytes, while a 300-byte message is padded to 512 bytes. This provides strong protection for small messages where length leakage matters most, with logarithmic overhead for larger messages. + ## Project Structure ``` @@ -205,6 +221,7 @@ otp/ │ ├── ui.c # Interactive user interface and menu system │ ├── state.c # Global state management (pads directory, preferences) │ ├── crypto.c # Core cryptographic operations (XOR, base64) +│ ├── padding.c # Message padding (exponential bucketing, Padmé padding) │ ├── pads.c # Pad management and file operations │ ├── entropy.c # Entropy collection from various sources │ ├── trng.c # Hardware RNG device detection and collection @@ -217,6 +234,7 @@ otp/ ├── pads/ # OTP pad storage directory (created at runtime) ├── files/ # Encrypted file storage (created at runtime) └── tests/ # Test scripts and utilities + └── test_padding.sh # Padding implementation tests ``` ## Architecture @@ -227,6 +245,7 @@ The OTP cipher uses a modular architecture with clean separation of concerns: - **ui.c**: Interactive user interface, menus, and terminal management - **state.c**: Global state management (pads directory, terminal dimensions, preferences) - **crypto.c**: Core cryptographic operations (XOR encryption, base64 encoding) +- **padding.c**: Message padding implementation (exponential bucketing, ISO/IEC 9797-1 Method 2) - **pads.c**: Pad file management, checksums, and state tracking - **entropy.c**: Entropy collection from keyboard, dice, files, and hardware RNG - **trng.c**: Hardware RNG device detection and entropy collection from USB devices diff --git a/src/archive.c b/src/archive.c index c447db6..3afa19c 100644 --- a/src/archive.c +++ b/src/archive.c @@ -476,7 +476,18 @@ int decrypt_and_extract_directory(const char* encrypted_file, const char* output unlink(temp_decrypted); } else { // Not compressed, assume it's already TAR - rename(temp_decrypted, temp_tar); + printf("File is not compressed, using as TAR directly...\n"); + if (rename(temp_decrypted, temp_tar) != 0) { + printf("Error: Failed to rename decrypted file to TAR file\n"); + unlink(temp_decrypted); + return 1; + } + } + + // Verify TAR file exists before extraction + if (access(temp_tar, F_OK) != 0) { + printf("Error: TAR file does not exist at '%s'\n", temp_tar); + return 1; } printf("Extracting archive...\n"); diff --git a/src/crypto.c b/src/crypto.c index 4a137c2..eccf0f7 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -401,18 +401,40 @@ int encrypt_text(const char* pad_identifier, const char* input_text) { return 1; } - // Check if we have enough pad space - struct stat pad_stat; - if (stat(pad_path, &pad_stat) != 0) { - printf("Error: Cannot get pad file size\n"); + // Calculate chunk size for padding (exponential bucketing) + size_t chunk_size = calculate_chunk_size(input_len); + + // Allocate buffer for padded message + unsigned char* padded_buffer = malloc(chunk_size); + if (!padded_buffer) { + printf("Error: Memory allocation failed\n"); + free(pad_chksum); + return 1; + } + + // Copy message to buffer and apply padding + memcpy(padded_buffer, text_buffer, input_len); + if (apply_padme_padding(padded_buffer, input_len, chunk_size) != 0) { + printf("Error: Failed to apply padding\n"); + free(padded_buffer); free(pad_chksum); return 1; } - if (current_offset + input_len > (uint64_t)pad_stat.st_size) { + // Check if we have enough pad space (now using chunk_size instead of input_len) + struct stat pad_stat; + if (stat(pad_path, &pad_stat) != 0) { + printf("Error: Cannot get pad file size\n"); + free(padded_buffer); + free(pad_chksum); + return 1; + } + + if (current_offset + chunk_size > (uint64_t)pad_stat.st_size) { printf("Error: Not enough pad space remaining\n"); printf("Need: %lu bytes, Available: %lu bytes\n", - input_len, (uint64_t)pad_stat.st_size - current_offset); + chunk_size, (uint64_t)pad_stat.st_size - current_offset); + free(padded_buffer); free(pad_chksum); return 1; } @@ -432,37 +454,40 @@ int encrypt_text(const char* pad_identifier, const char* input_text) { return 1; } - unsigned char* pad_data = malloc(input_len); - if (fread(pad_data, 1, input_len, pad_file) != input_len) { + unsigned char* pad_data = malloc(chunk_size); + if (fread(pad_data, 1, chunk_size, pad_file) != chunk_size) { printf("Error: Cannot read pad data\n"); free(pad_data); fclose(pad_file); + free(padded_buffer); free(pad_chksum); return 1; } fclose(pad_file); - // Use universal XOR operation for encryption - unsigned char* ciphertext = malloc(input_len); - if (universal_xor_operation((const unsigned char*)text_buffer, input_len, pad_data, ciphertext) != 0) { + // Use universal XOR operation for encryption (now with padded data) + unsigned char* ciphertext = malloc(chunk_size); + if (universal_xor_operation(padded_buffer, chunk_size, pad_data, ciphertext) != 0) { printf("Error: Encryption operation failed\n"); free(pad_data); free(ciphertext); + free(padded_buffer); free(pad_chksum); return 1; } - // Update state offset - if (write_state_offset(pad_chksum, current_offset + input_len) != 0) { + // Update state offset (now using chunk_size) + if (write_state_offset(pad_chksum, current_offset + chunk_size) != 0) { printf("Warning: Failed to update state file\n"); } - // Use universal ASCII armor generator + // Use universal ASCII armor generator (now with chunk_size) char* ascii_output; - if (generate_ascii_armor(pad_chksum, current_offset, ciphertext, input_len, &ascii_output) != 0) { + if (generate_ascii_armor(pad_chksum, current_offset, ciphertext, chunk_size, &ascii_output) != 0) { printf("Error: Failed to generate ASCII armor\n"); free(pad_data); free(ciphertext); + free(padded_buffer); free(pad_chksum); return 1; } @@ -479,6 +504,7 @@ int encrypt_text(const char* pad_identifier, const char* input_text) { // Cleanup free(pad_data); free(ciphertext); + free(padded_buffer); free(ascii_output); free(pad_chksum); @@ -632,6 +658,22 @@ int universal_decrypt(const char* input_data, const char* output_target, decrypt return 1; } + // Remove padding to get actual message + size_t actual_msg_len; + if (remove_padme_padding(ciphertext, ciphertext_len, &actual_msg_len) != 0) { + if (mode == DECRYPT_MODE_SILENT) { + fprintf(stderr, "Error: Invalid padding - message may be corrupted\n"); + } else { + printf("Error: Invalid padding - message may be corrupted\n"); + } + free(ciphertext); + free(pad_data); + return 1; + } + + // Update ciphertext_len to actual message length + ciphertext_len = actual_msg_len; + // Output based on mode if (mode == DECRYPT_MODE_FILE_TO_FILE) { // Write to output file @@ -740,6 +782,9 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char* return 1; } + // Calculate chunk size for padding (exponential bucketing) + size_t chunk_size = calculate_chunk_size(file_size); + // Check if pad file exists if (access(pad_path, R_OK) != 0) { printf("Error: Pad file %s not found\n", pad_path); @@ -774,10 +819,10 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char* return 1; } - if (current_offset + file_size > (uint64_t)pad_stat.st_size) { + if (current_offset + chunk_size > (uint64_t)pad_stat.st_size) { printf("Error: Not enough pad space remaining\n"); - printf("Need: %lu bytes, Available: %lu bytes\n", - file_size, (uint64_t)pad_stat.st_size - current_offset); + printf("Need: %lu bytes (file: %lu + padding), Available: %lu bytes\n", + chunk_size, file_size, (uint64_t)pad_stat.st_size - current_offset); free(pad_chksum); return 1; } @@ -822,66 +867,93 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char* return 1; } - // Read and encrypt file - unsigned char buffer[64 * 1024]; - unsigned char pad_buffer[64 * 1024]; - unsigned char* encrypted_data = malloc(file_size); - uint64_t bytes_processed = 0; + // Allocate buffer for padded file data + unsigned char* file_data = malloc(file_size); + if (!file_data) { + printf("Error: Memory allocation failed\n"); + fclose(input_fp); + fclose(pad_file); + free(pad_chksum); + return 1; + } printf("Encrypting %s...\n", input_file); - while (bytes_processed < file_size) { - uint64_t chunk_size = sizeof(buffer); - if (file_size - bytes_processed < chunk_size) { - chunk_size = file_size - bytes_processed; - } - - // Read file data - if (fread(buffer, 1, chunk_size, input_fp) != chunk_size) { - printf("Error: Cannot read input file data\n"); - free(encrypted_data); - fclose(input_fp); - fclose(pad_file); - free(pad_chksum); - return 1; - } - - // Read pad data - if (fread(pad_buffer, 1, chunk_size, pad_file) != chunk_size) { - printf("Error: Cannot read pad data\n"); - free(encrypted_data); - fclose(input_fp); - fclose(pad_file); - free(pad_chksum); - return 1; - } - - // Use universal XOR operation for encryption - if (universal_xor_operation(buffer, chunk_size, pad_buffer, &encrypted_data[bytes_processed]) != 0) { - printf("Error: Encryption operation failed\n"); - free(encrypted_data); - fclose(input_fp); - fclose(pad_file); - free(pad_chksum); - return 1; - } - - bytes_processed += chunk_size; - - // Show progress for large files (> 10MB) - if (file_size > 10 * 1024 * 1024 && bytes_processed % (1024 * 1024) == 0) { - // show_progress(bytes_processed, file_size, start_time); // MOVED TO src/util.c - } + // Read entire file + if (fread(file_data, 1, file_size, input_fp) != file_size) { + printf("Error: Cannot read input file\n"); + free(file_data); + fclose(input_fp); + fclose(pad_file); + free(pad_chksum); + return 1; } - - if (file_size > 10 * 1024 * 1024) { - // show_progress(file_size, file_size, start_time); // MOVED TO src/util.c - printf("\n"); - } - fclose(input_fp); + + // Allocate buffer for padded data + unsigned char* padded_data = malloc(chunk_size); + if (!padded_data) { + printf("Error: Memory allocation failed\n"); + free(file_data); + fclose(pad_file); + free(pad_chksum); + return 1; + } + + // Copy file data and apply padding + memcpy(padded_data, file_data, file_size); + free(file_data); + + if (apply_padme_padding(padded_data, file_size, chunk_size) != 0) { + printf("Error: Failed to apply padding\n"); + free(padded_data); + fclose(pad_file); + free(pad_chksum); + return 1; + } + + // Read pad data + unsigned char* pad_data = malloc(chunk_size); + if (!pad_data) { + printf("Error: Memory allocation failed\n"); + free(padded_data); + fclose(pad_file); + free(pad_chksum); + return 1; + } + + if (fread(pad_data, 1, chunk_size, pad_file) != chunk_size) { + printf("Error: Cannot read pad data\n"); + free(padded_data); + free(pad_data); + fclose(pad_file); + free(pad_chksum); + return 1; + } fclose(pad_file); + // Encrypt padded data + unsigned char* encrypted_data = malloc(chunk_size); + if (!encrypted_data) { + printf("Error: Memory allocation failed\n"); + free(padded_data); + free(pad_data); + free(pad_chksum); + return 1; + } + + if (universal_xor_operation(padded_data, chunk_size, pad_data, encrypted_data) != 0) { + printf("Error: Encryption operation failed\n"); + free(padded_data); + free(pad_data); + free(encrypted_data); + free(pad_chksum); + return 1; + } + + free(padded_data); + free(pad_data); + // Write output file if (ascii_armor) { // ASCII armored format - same as message format @@ -893,9 +965,9 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char* return 1; } - // Use universal ASCII armor generator + // Use universal ASCII armor generator (now with chunk_size) char* ascii_output; - if (generate_ascii_armor(pad_chksum, current_offset, encrypted_data, file_size, &ascii_output) != 0) { + if (generate_ascii_armor(pad_chksum, current_offset, encrypted_data, chunk_size, &ascii_output) != 0) { printf("Error: Failed to generate ASCII armor\n"); fclose(output_fp); free(encrypted_data); @@ -941,17 +1013,17 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char* uint32_t file_mode = input_stat.st_mode; fwrite(&file_mode, sizeof(uint32_t), 1, output_fp); - // File size: 8 bytes + // File size: 8 bytes (original file size, not padded) fwrite(&file_size, sizeof(uint64_t), 1, output_fp); - // Encrypted data - fwrite(encrypted_data, 1, file_size, output_fp); + // Encrypted data (padded) + fwrite(encrypted_data, 1, chunk_size, output_fp); fclose(output_fp); } - // Update state offset - if (write_state_offset(pad_chksum, current_offset + file_size) != 0) { + // Update state offset (now using chunk_size) + if (write_state_offset(pad_chksum, current_offset + chunk_size) != 0) { printf("Warning: Failed to update state file\n"); } @@ -1012,14 +1084,14 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) { unsigned char pad_chksum_bin[32]; uint64_t pad_offset; uint32_t file_mode; - uint64_t file_size; + uint64_t original_file_size; if (fread(magic, 1, 4, input_fp) != 4 || fread(&version, sizeof(uint16_t), 1, input_fp) != 1 || fread(pad_chksum_bin, 1, 32, input_fp) != 32 || fread(&pad_offset, sizeof(uint64_t), 1, input_fp) != 1 || fread(&file_mode, sizeof(uint32_t), 1, input_fp) != 1 || - fread(&file_size, sizeof(uint64_t), 1, input_fp) != 1) { + fread(&original_file_size, sizeof(uint64_t), 1, input_fp) != 1) { printf("Error: Cannot read binary header\n"); fclose(input_fp); return 1; @@ -1039,7 +1111,7 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) { pad_chksum_hex[64] = '\0'; printf("Decrypting binary file...\n"); - printf("File size: %lu bytes\n", file_size); + printf("Original file size: %lu bytes\n", original_file_size); // Check if we have the required pad char pad_path[MAX_HASH_LENGTH + 20]; @@ -1064,9 +1136,18 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) { output_file = default_output; } - // Read encrypted data - unsigned char* encrypted_data = malloc(file_size); - if (fread(encrypted_data, 1, file_size, input_fp) != file_size) { + // Calculate chunk size (encrypted data is padded) + size_t chunk_size = calculate_chunk_size(original_file_size); + + // Read encrypted (padded) data + unsigned char* encrypted_data = malloc(chunk_size); + if (!encrypted_data) { + printf("Error: Memory allocation failed\n"); + fclose(input_fp); + return 1; + } + + if (fread(encrypted_data, 1, chunk_size, input_fp) != chunk_size) { printf("Error: Cannot read encrypted data\n"); free(encrypted_data); fclose(input_fp); @@ -1089,8 +1170,15 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) { return 1; } - unsigned char* pad_data = malloc(file_size); - if (fread(pad_data, 1, file_size, pad_file) != file_size) { + unsigned char* pad_data = malloc(chunk_size); + if (!pad_data) { + printf("Error: Memory allocation failed\n"); + free(encrypted_data); + fclose(pad_file); + return 1; + } + + if (fread(pad_data, 1, chunk_size, pad_file) != chunk_size) { printf("Error: Cannot read pad data\n"); free(encrypted_data); free(pad_data); @@ -1100,26 +1188,40 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) { fclose(pad_file); // Use universal XOR operation for decryption - if (universal_xor_operation(encrypted_data, file_size, pad_data, encrypted_data) != 0) { + if (universal_xor_operation(encrypted_data, chunk_size, pad_data, encrypted_data) != 0) { printf("Error: Decryption operation failed\n"); free(encrypted_data); free(pad_data); return 1; } - // Write decrypted file + free(pad_data); + + // Remove padding to get original file + size_t actual_file_size; + if (remove_padme_padding(encrypted_data, chunk_size, &actual_file_size) != 0) { + printf("Error: Invalid padding - file may be corrupted\n"); + free(encrypted_data); + return 1; + } + + // Verify the actual size matches the stored original size + if (actual_file_size != original_file_size) { + printf("Warning: Decrypted size (%lu) doesn't match stored size (%lu)\n", + actual_file_size, original_file_size); + } + + // Write decrypted file (using actual_file_size) FILE* output_fp = fopen(output_file, "wb"); if (!output_fp) { printf("Error: Cannot create output file %s\n", output_file); free(encrypted_data); - free(pad_data); return 1; } - if (fwrite(encrypted_data, 1, file_size, output_fp) != file_size) { + if (fwrite(encrypted_data, 1, actual_file_size, output_fp) != actual_file_size) { printf("Error: Cannot write decrypted data\n"); free(encrypted_data); - free(pad_data); fclose(output_fp); return 1; } @@ -1141,7 +1243,6 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) { // Cleanup free(encrypted_data); - free(pad_data); return 0; } diff --git a/src/main.h b/src/main.h index e1d57a1..86e0efc 100644 --- a/src/main.h +++ b/src/main.h @@ -23,7 +23,7 @@ #include // Version - Updated automatically by build.sh -#define OTP_VERSION "v0.3.47" +#define OTP_VERSION "v0.3.48" // Constants #define MAX_INPUT_SIZE 4096 @@ -267,6 +267,16 @@ const char* get_files_directory(void); void get_default_file_path(const char* filename, char* result_path, size_t result_size); void get_directory_display(const char* file_path, char* result, size_t result_size); +//////////////////////////////////////////////////////////////////////////////// +// MESSAGE PADDING FUNCTIONS +//////////////////////////////////////////////////////////////////////////////// + +// Exponential bucketing and ISO/IEC 9797-1 Method 2 (Padmé) padding +size_t calculate_chunk_size(size_t msg_len); +int apply_padme_padding(unsigned char* buffer, size_t msg_len, size_t chunk_size); +int remove_padme_padding(const unsigned char* buffer, size_t chunk_size, size_t* msg_len); +void format_chunk_size(size_t chunk_size, char* buffer, size_t buffer_size); + //////////////////////////////////////////////////////////////////////////////// // UTILITY FUNCTIONS //////////////////////////////////////////////////////////////////////////////// diff --git a/src/padding.c b/src/padding.c new file mode 100644 index 0000000..aacc945 --- /dev/null +++ b/src/padding.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include "main.h" + +//////////////////////////////////////////////////////////////////////////////// +// MESSAGE PADDING IMPLEMENTATION +// ISO/IEC 9797-1 Method 2 (Padmé Padding) +// with Exponential Bucketing for Traffic Analysis Resistance +//////////////////////////////////////////////////////////////////////////////// + +// Calculate required chunk size using exponential bucketing +// Starting at 256 bytes, doubling each time +// Provides strong protection against length analysis attacks +size_t calculate_chunk_size(size_t msg_len) { + size_t chunk = 256; // Minimum chunk size: 256 bytes + + // Need space for message + 0x80 byte (minimum 1 byte padding) + while (chunk < msg_len + 1) { + chunk *= 2; + } + + return chunk; +} + +// Apply ISO/IEC 9797-1 Method 2 (Padmé) padding +// Appends 0x80 byte followed by 0x00 bytes to reach chunk boundary +// Returns 0 on success, non-zero on error +int apply_padme_padding(unsigned char* buffer, size_t msg_len, size_t chunk_size) { + if (!buffer) { + return 1; // Error: null pointer + } + + if (chunk_size < msg_len + 1) { + return 2; // Error: chunk size too small for message + padding + } + + // Apply padding: 0x80 followed by 0x00 bytes + buffer[msg_len] = 0x80; + + // Fill remaining bytes with 0x00 + if (chunk_size > msg_len + 1) { + memset(buffer + msg_len + 1, 0x00, chunk_size - msg_len - 1); + } + + return 0; // Success +} + +// Remove ISO/IEC 9797-1 Method 2 (Padmé) padding +// Scans backwards for 0x80 marker, validates padding +// Returns 0 on success, non-zero on error +// Sets msg_len to the actual message length (excluding padding) +int remove_padme_padding(const unsigned char* buffer, size_t chunk_size, size_t* msg_len) { + if (!buffer || !msg_len) { + return 1; // Error: null pointer + } + + if (chunk_size == 0) { + return 2; // Error: invalid chunk size + } + + // Scan backwards from end to find 0x80 marker + for (int i = chunk_size - 1; i >= 0; i--) { + if (buffer[i] == 0x80) { + // Found the padding marker + *msg_len = i; + return 0; // Success + } else if (buffer[i] != 0x00) { + // Found non-zero, non-0x80 byte - invalid padding + return 3; // Error: invalid padding (corrupted or tampered data) + } + } + + // No 0x80 marker found - invalid padding + return 4; // Error: no padding marker found +} + +// Helper function to get human-readable chunk size +// Useful for debugging and user feedback +void format_chunk_size(size_t chunk_size, char* buffer, size_t buffer_size) { + if (!buffer || buffer_size == 0) return; + + if (chunk_size < 1024) { + snprintf(buffer, buffer_size, "%zu bytes", chunk_size); + } else if (chunk_size < 1024 * 1024) { + snprintf(buffer, buffer_size, "%.1f KB", chunk_size / 1024.0); + } else if (chunk_size < 1024 * 1024 * 1024) { + snprintf(buffer, buffer_size, "%.1f MB", chunk_size / (1024.0 * 1024.0)); + } else { + snprintf(buffer, buffer_size, "%.1f GB", chunk_size / (1024.0 * 1024.0 * 1024.0)); + } +} diff --git a/tests/test_padding.sh b/tests/test_padding.sh new file mode 100755 index 0000000..cf1a9bc --- /dev/null +++ b/tests/test_padding.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# Test script for message padding implementation + +set -e + +echo "=== Testing Message Padding Implementation ===" +echo "" + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Test counter +TESTS_PASSED=0 +TESTS_FAILED=0 + +# Function to run a test +run_test() { + local test_name="$1" + local test_command="$2" + + echo -n "Testing: $test_name... " + if eval "$test_command" > /dev/null 2>&1; then + echo -e "${GREEN}PASS${NC}" + ((TESTS_PASSED++)) + return 0 + else + echo -e "${RED}FAIL${NC}" + ((TESTS_FAILED++)) + return 1 + fi +} + +# Create a small test pad if it doesn't exist +if [ ! -f "pads/"*.pad ]; then + echo "Creating test pad (1MB)..." + ./build/otp-x86_64 generate 1MB + echo "" +fi + +# Get the first pad checksum +PAD_CHKSUM=$(ls pads/*.pad | head -n 1 | xargs basename | sed 's/.pad$//') + +echo "Using pad: ${PAD_CHKSUM:0:16}..." +echo "" + +# Test 1: Encrypt and decrypt a short message (should be padded to 256 bytes) +echo "Test 1: Short message (10 bytes -> 256 bytes padded)" +TEST_MSG="Hello Test" +ENCRYPTED=$(echo "$TEST_MSG" | ./build/otp-x86_64 encrypt ${PAD_CHKSUM:0:8}) +DECRYPTED=$(echo "$ENCRYPTED" | ./build/otp-x86_64 decrypt) + +if [ "$DECRYPTED" = "$TEST_MSG" ]; then + echo -e "${GREEN}✓ Short message encryption/decryption successful${NC}" + ((TESTS_PASSED++)) +else + echo -e "${RED}✗ Short message failed${NC}" + echo " Expected: $TEST_MSG" + echo " Got: $DECRYPTED" + ((TESTS_FAILED++)) +fi +echo "" + +# Test 2: Encrypt and decrypt a medium message (should be padded to 512 bytes) +echo "Test 2: Medium message (~300 bytes -> 512 bytes padded)" +TEST_MSG=$(printf 'A%.0s' {1..300}) +ENCRYPTED=$(echo "$TEST_MSG" | ./build/otp-x86_64 encrypt ${PAD_CHKSUM:0:8}) +DECRYPTED=$(echo "$ENCRYPTED" | ./build/otp-x86_64 decrypt) + +if [ "$DECRYPTED" = "$TEST_MSG" ]; then + echo -e "${GREEN}✓ Medium message encryption/decryption successful${NC}" + ((TESTS_PASSED++)) +else + echo -e "${RED}✗ Medium message failed${NC}" + ((TESTS_FAILED++)) +fi +echo "" + +# Test 3: Encrypt and decrypt with special characters +echo "Test 3: Special characters and unicode" +TEST_MSG="Hello! @#$%^&*() 测试" +ENCRYPTED=$(echo "$TEST_MSG" | ./build/otp-x86_64 encrypt ${PAD_CHKSUM:0:8}) +DECRYPTED=$(echo "$ENCRYPTED" | ./build/otp-x86_64 decrypt) + +if [ "$DECRYPTED" = "$TEST_MSG" ]; then + echo -e "${GREEN}✓ Special characters encryption/decryption successful${NC}" + ((TESTS_PASSED++)) +else + echo -e "${RED}✗ Special characters failed${NC}" + echo " Expected: $TEST_MSG" + echo " Got: $DECRYPTED" + ((TESTS_FAILED++)) +fi +echo "" + +# Test 4: File encryption/decryption with padding +echo "Test 4: File encryption/decryption" +TEST_FILE="/tmp/otp_test_file.txt" +echo "This is a test file for OTP encryption with padding." > "$TEST_FILE" + +./build/otp-x86_64 -f "$TEST_FILE" ${PAD_CHKSUM:0:8} -a -o /tmp/test_encrypted.otp.asc +./build/otp-x86_64 decrypt /tmp/test_encrypted.otp.asc -o /tmp/test_decrypted.txt + +if diff "$TEST_FILE" /tmp/test_decrypted.txt > /dev/null 2>&1; then + echo -e "${GREEN}✓ File encryption/decryption successful${NC}" + ((TESTS_PASSED++)) +else + echo -e "${RED}✗ File encryption/decryption failed${NC}" + ((TESTS_FAILED++)) +fi + +# Cleanup +rm -f "$TEST_FILE" /tmp/test_encrypted.otp.asc /tmp/test_decrypted.txt +echo "" + +# Summary +echo "=== Test Summary ===" +echo -e "Tests passed: ${GREEN}${TESTS_PASSED}${NC}" +echo -e "Tests failed: ${RED}${TESTS_FAILED}${NC}" +echo "" + +if [ $TESTS_FAILED -eq 0 ]; then + echo -e "${GREEN}All tests passed!${NC}" + exit 0 +else + echo -e "${RED}Some tests failed.${NC}" + exit 1 +fi