diff --git a/.gitignore b/.gitignore index a12cb66..87ba8f4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ Gemini.md TropicOfCancer-HenryMiller.txt .gitea_token true_rng/ - +Trash/ # Auto-generated files (none currently) diff --git a/otp copy.c b/otp copy.c deleted file mode 100644 index b6a847c..0000000 --- a/otp copy.c +++ /dev/null @@ -1,6278 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#define _DEFAULT_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "nostr_chacha20.h" -#include "otp.h" - -// Custom base64 character set -static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const int base64_decode_table[256] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, - 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, - 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, - -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, - 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 -}; - -#define MAX_INPUT_SIZE 4096 -#define MAX_LINE_LENGTH 1024 -#define MAX_HASH_LENGTH 65 -#define PROGRESS_UPDATE_INTERVAL (64 * 1024 * 1024) // 64MB intervals -#define DEFAULT_PADS_DIR "pads" -#define FILES_DIR "files" - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// GLOBAL VARIABLES -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -static char current_pads_dir[512] = DEFAULT_PADS_DIR; -static char default_pad_path[1024] = ""; -static int is_interactive_mode = 0; - -// Terminal dimensions -static int terminal_width = 80; // Default fallback width -static int terminal_height = 24; // Default fallback height - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// TERMINAL UI FUNCTIONS -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -// Initialize terminal dimensions -void init_terminal_dimensions(void) { - struct winsize ws; - - // Try to get actual terminal size - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0 && ws.ws_row > 0) { - terminal_width = ws.ws_col; - terminal_height = ws.ws_row; - } - // If ioctl fails, keep the default values (80x24) -} - -// Print centered header with = padding, screen clearing, and optional pause -void print_centered_header(const char* text, int pause_before_clear) { - if (!text) return; - - // Phase 1: Pause if requested - if (pause_before_clear) { - printf("\nPress Enter to continue..."); - fflush(stdout); - - // Wait for Enter key - int c; - while ((c = getchar()) != '\n' && c != EOF) { - // Consume any extra characters until newline - } - } - - // Phase 2: Clear screen using terminal height - for (int i = 0; i < terminal_height; i++) { - printf("\n"); - } - - // Phase 3: Display centered header (existing logic) - int text_len = strlen(text); - int available_width = terminal_width; - - // Ensure minimum spacing: at least 1 space on each side - int min_required = text_len + 4; // text + " " + text + " " (spaces around text) - - if (available_width < min_required) { - // Terminal too narrow - just print the text with minimal formatting - printf("=== %s ===\n", text); - return; - } - - // Calculate padding - int total_padding = available_width - text_len - 2; // -2 for spaces around text - int left_padding = total_padding / 2; - int right_padding = total_padding - left_padding; - - // Print the header - for (int i = 0; i < left_padding; i++) { - printf("="); - } - printf(" %s ", text); - for (int i = 0; i < right_padding; i++) { - printf("="); - } - printf("\n"); -} - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// MAIN -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -int main(int argc, char* argv[]) { - // Initialize terminal dimensions first - init_terminal_dimensions(); - - // Load preferences - load_preferences(); - - // Detect interactive mode: only true when running with no arguments - is_interactive_mode = (argc == 1); - - // Check for OTP thumb drive on startup - char otp_drive_path[512]; - if (detect_otp_thumb_drive(otp_drive_path, sizeof(otp_drive_path))) { - // Only show messages in interactive mode - if (is_interactive_mode) { - printf("Detected OTP thumb drive: %s\n", otp_drive_path); - printf("Using as default pads directory for this session.\n\n"); - } - strncpy(current_pads_dir, otp_drive_path, sizeof(current_pads_dir) - 1); - current_pads_dir[sizeof(current_pads_dir) - 1] = '\0'; - } - - if (is_interactive_mode) { - return interactive_mode(); - } else { - return command_line_mode(argc, argv); - } -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// COMMAND LINE MODE -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -int command_line_mode(int argc, char* argv[]) { - // Check for help flags first - if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--h") == 0 || - strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "--help") == 0 || - strcmp(argv[1], "help") == 0) { - print_usage(argv[0]); - return 0; - } - - if (strcmp(argv[1], "generate") == 0 || strcmp(argv[1], "-g") == 0) { - if (argc != 3) { - printf("Usage: %s generate|-g \n", argv[0]); - printf("Size examples: 1024, 1GB, 5TB, 512MB\n"); - return 1; - } - uint64_t size = parse_size_string(argv[2]); - if (size == 0) { - printf("Error: Invalid size format\n"); - return 1; - } - return generate_pad(size, 1); // Use simplified pad generation - } - else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) { - // Check for piped input first - if (has_stdin_data()) { - char* piped_text = read_stdin_text(); - if (piped_text) { - int result = pipe_mode(argc, argv, piped_text); - free(piped_text); - return result; - } - } - - if (argc < 2 || argc > 4) { - printf("Usage: %s encrypt|-e [pad_chksum_or_prefix] [text_to_encrypt]\n", argv[0]); - return 1; - } - - // Check if pad was specified or use default - const char* pad_identifier = NULL; - const char* text = NULL; - - if (argc == 2) { - // Just -e, use default pad, no text (interactive) - pad_identifier = NULL; - text = NULL; - } else if (argc == 3) { - // Could be -e or -e (using default pad) - // Check if default pad is available to determine interpretation - char* default_pad = get_default_pad_path(); - if (default_pad) { - // Default pad available, treat argument as text - pad_identifier = NULL; - text = argv[2]; - free(default_pad); - } else { - // No default pad, treat as pad identifier - pad_identifier = argv[2]; - text = NULL; - } - } else { - // argc == 4: -e - pad_identifier = argv[2]; - text = argv[3]; - } - - // If pad_identifier is NULL, we need to use default pad - if (pad_identifier == NULL) { - char* default_pad = get_default_pad_path(); - if (default_pad) { - // Extract checksum from default pad path - char* filename = strrchr(default_pad, '/'); - if (!filename) filename = default_pad; - else filename++; // Skip the '/' - - // Extract checksum (remove .pad extension) - if (strlen(filename) >= 68 && strstr(filename, ".pad")) { - static char default_checksum[65]; - strncpy(default_checksum, filename, 64); - default_checksum[64] = '\0'; - pad_identifier = default_checksum; - } - free(default_pad); - - // Call encrypt_text and return result - return encrypt_text(pad_identifier, text); - } else { - printf("Error: No default pad configured. Specify pad explicitly or configure default pad.\n"); - return 1; - } - } else { - // Explicit pad specified, normal operation - return encrypt_text(pad_identifier, text); - } - } - else if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) { - if (argc == 2) { - // Check for piped input first - if (has_stdin_data()) { - // Piped decrypt mode - read stdin and decrypt silently - char* piped_message = read_stdin_text(); - if (piped_message) { - int result = decrypt_text(NULL, piped_message); - free(piped_message); - return result; - } - } - // Interactive mode - no arguments needed - return decrypt_text(NULL, NULL); - } - else if (argc == 3) { - // Check if the argument looks like an encrypted message (starts with -----) - if (strncmp(argv[2], "-----BEGIN OTP MESSAGE-----", 27) == 0) { - // Inline decrypt with message only - use silent mode for command line - return decrypt_text(NULL, argv[2]); - } else { - // Check if it's a file (contains . or ends with known extensions) - if (strstr(argv[2], ".") != NULL) { - // Treat as file - return decrypt_file(argv[2], NULL); - } else { - // Interactive decrypt with pad hint (legacy support) - return decrypt_text(argv[2], NULL); - } - } - } - else if (argc == 4) { - // Check for -o flag for output file - if (strcmp(argv[2], "-o") == 0) { - printf("Usage: %s decrypt|-d [-o ]\n", argv[0]); - return 1; - } else { - // Legacy format: pad_chksum and message, or file with output - // Use silent mode for command line when message is provided - return decrypt_text(argv[2], argv[3]); - } - } - else if (argc == 5 && strcmp(argv[3], "-o") == 0) { - // File decryption with output: -d -o - return decrypt_file(argv[2], argv[4]); - } - else { - printf("Usage: %s decrypt|-d [encrypted_message|file] [-o output_file]\n", argv[0]); - printf(" %s decrypt|-d [encrypted_message] (pad info from message)\n", argv[0]); - return 1; - } - } - else if (strcmp(argv[1], "-f") == 0) { - // File encryption mode: -f [-a] [-o ] - if (argc < 4) { - printf("Usage: %s -f [-a] [-o ]\n", argv[0]); - return 1; - } - - const char* input_file = argv[2]; - const char* pad_prefix = argv[3]; - int ascii_armor = 0; - const char* output_file = NULL; - - // Parse optional flags - for (int i = 4; i < argc; i++) { - if (strcmp(argv[i], "-a") == 0) { - ascii_armor = 1; - } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { - output_file = argv[++i]; - } - } - - return encrypt_file(pad_prefix, input_file, output_file, ascii_armor); - } - else if (strcmp(argv[1], "list") == 0 || strcmp(argv[1], "-l") == 0) { - printf("Available pads:\n"); - char* selected = select_pad_interactive("Available pads:", "Select pad (or press Enter to exit)", PAD_FILTER_ALL, 0); - if (selected) { - free(selected); - } - return 0; - } - else { - print_usage(argv[0]); - return 1; - } -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// INTERACTIVE MODE -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -int interactive_mode(void) { - char input[10]; - printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - while (1) { - show_main_menu(); - - if (!fgets(input, sizeof(input), stdin)) { - printf("Goodbye!\n"); - break; - } - - char choice = toupper(input[0]); - - switch (choice) { - case 'T': - handle_text_encrypt(); - break; - case 'F': - handle_file_encrypt(); - break; - case 'D': - handle_decrypt_menu(); - break; - case 'P': - handle_pads_menu(); - break; - case 'X': - case 'Q': - printf("Goodbye!\n"); - return 0; - default: - printf("Invalid choice. Please try again.\n"); - break; - } - } - - return 0; -} - - -void show_main_menu(void) { - printf("\n"); - print_centered_header("Main Menu - OTP v0.3.16", 0); - printf("\n"); - - printf(" \033[4mT\033[0mext encrypt\n"); //TEXT ENCRYPT - printf(" \033[4mF\033[0mile encrypt\n"); //FILE ENCRYPT - printf(" \033[4mD\033[0mecrypt\n"); //DECRYPT - printf(" \033[4mP\033[0mads\n"); //PADS - printf(" E\033[4mx\033[0mit\n"); //EXIT - printf("\nSelect option: "); -} - -int handle_generate_menu(void) { - printf("\n"); - print_centered_header("Generate New Pad", 0); - printf("Enter pad size (examples: 1GB, 5TB, 512MB, 2048): "); - - char size_input[64]; - if (!fgets(size_input, sizeof(size_input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - size_input[strcspn(size_input, "\n")] = 0; - uint64_t size = parse_size_string(size_input); - - if (size == 0) { - printf("Error: Invalid size format\n"); - return 1; - } - - double size_gb = (double)size / (1024.0 * 1024.0 * 1024.0); - printf("Generating %.2f GB pad...\n", size_gb); - printf("Note: Use 'Add entropy' in Pads menu to enhance randomness after creation.\n"); - - return generate_pad(size, 1); -} - -int handle_encrypt_menu(void) { - printf("\n"); - print_centered_header("Encrypt Data", 0); - - printf("Available pads:\n"); - char* selected = select_pad_interactive("Available pads:", "Select pad (or press Enter to continue)", PAD_FILTER_ALL, 0); - int pad_count = 1; // Assume at least 1 pad if function returned - if (selected) { - free(selected); - } else { - pad_count = 0; - } - if (pad_count == 0) { - printf("No pads available. Generate a pad first.\n"); - return 1; - } - - // Ask user to choose between text and file encryption - printf("\nSelect encryption type:\n"); - printf(" 1. Text message\n"); - printf(" 2. File\n"); - printf("Enter choice (1-2): "); - - char choice_input[10]; - if (!fgets(choice_input, sizeof(choice_input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - int choice = atoi(choice_input); - - if (choice == 1) { - // Text encryption - use unified pad selection - char* selected_pad = select_pad_interactive("Select Pad for Text Encryption", - "Select pad (by prefix)", - PAD_FILTER_ALL, 1); - if (!selected_pad) { - printf("Text encryption cancelled.\n"); - return 1; - } - - int result = encrypt_text(selected_pad, NULL); // NULL for interactive mode - free(selected_pad); - return result; - } - else if (choice == 2) { - // File encryption - printf("\nFile selection options:\n"); - printf(" 1. Type file path directly\n"); - printf(" 2. Use file manager\n"); - printf("Enter choice (1-2): "); - - char file_choice[10]; - char input_file[512]; - - if (!fgets(file_choice, sizeof(file_choice), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - if (atoi(file_choice) == 2) { - // Use file manager - if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) { - printf("Falling back to manual file path entry.\n"); - printf("Enter input file path: "); - if (!fgets(input_file, sizeof(input_file), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - input_file[strcspn(input_file, "\n")] = 0; - } - } else { - // Direct file path input - printf("Enter input file path: "); - if (!fgets(input_file, sizeof(input_file), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - input_file[strcspn(input_file, "\n")] = 0; - } - - // Check if file exists - if (access(input_file, R_OK) != 0) { - printf("Error: File '%s' not found or cannot be read\n", input_file); - return 1; - } - - // Use unified pad selection - char* selected_pad = select_pad_interactive("Select Pad for File Encryption", - "Select pad (by prefix)", - PAD_FILTER_ALL, 1); - if (!selected_pad) { - printf("File encryption cancelled.\n"); - return 1; - } - - // Ask for output format - printf("\nSelect output format:\n"); - printf(" 1. Binary (.otp) - preserves file permissions\n"); - printf(" 2. ASCII (.otp.asc) - text-safe format\n"); - printf("Enter choice (1-2): "); - - char format_input[10]; - if (!fgets(format_input, sizeof(format_input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - int ascii_armor = (atoi(format_input) == 2) ? 1 : 0; - - // Generate default output filename with files directory and use enhanced input function - char default_output[1024]; // Increased size to prevent truncation warnings - char temp_default[1024]; - - // Generate base filename with appropriate extension - if (ascii_armor) { - snprintf(temp_default, sizeof(temp_default), "%s.otp.asc", input_file); - } else { - snprintf(temp_default, sizeof(temp_default), "%s.otp", input_file); - } - - // Apply files directory default path - get_default_file_path(temp_default, default_output, sizeof(default_output)); - - char output_file[512]; - if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { - printf("Error: Failed to read input\n"); - return 1; - } - - const char* output_filename = output_file; - - int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor); - free(selected_pad); - return result; - } - else { - printf("Invalid choice. Please enter 1 or 2.\n"); - return 1; - } -} - -int handle_decrypt_menu(void) { - printf("\n"); - print_centered_header("Smart Decrypt", 0); - printf("Enter encrypted data (paste ASCII armor), file path, or press Enter to browse files:\n"); - - char input_line[MAX_LINE_LENGTH]; - if (!fgets(input_line, sizeof(input_line), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - // Remove newline - input_line[strcspn(input_line, "\n")] = 0; - - if (strlen(input_line) == 0) { - // Empty input - launch file manager to browse for files - char selected_file[512]; - if (launch_file_manager(get_files_directory(), selected_file, sizeof(selected_file)) != 0) { - printf("Error: Could not launch file manager\n"); - return 1; - } - - // Generate smart default output filename with files directory and use enhanced input function - char temp_default[512]; - char default_output[512]; - strncpy(temp_default, selected_file, sizeof(temp_default) - 1); - temp_default[sizeof(temp_default) - 1] = '\0'; - - // Remove common encrypted extensions to get a better default - if (strstr(temp_default, ".otp.asc")) { - // Replace .otp.asc with original extension or no extension - char* ext_pos = strstr(temp_default, ".otp.asc"); - *ext_pos = '\0'; - } else if (strstr(temp_default, ".otp")) { - // Replace .otp with original extension or no extension - char* ext_pos = strstr(temp_default, ".otp"); - *ext_pos = '\0'; - } else { - // No recognized encrypted extension, add .decrypted suffix - strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1); - } - - // Apply files directory default path - get_default_file_path(temp_default, default_output, sizeof(default_output)); - - char output_file[512]; - if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { - printf("Error: Failed to read input\n"); - return 1; - } - - return decrypt_file(selected_file, output_file); - } - else if (strncmp(input_line, "-----BEGIN OTP MESSAGE-----", 27) == 0) { - // Looks like ASCII armor - collect the full message - char full_message[MAX_INPUT_SIZE * 4] = {0}; - strcat(full_message, input_line); - strcat(full_message, "\n"); - - printf("Continue pasting the message (end with -----END OTP MESSAGE-----):\n"); - - char line[MAX_LINE_LENGTH]; - while (fgets(line, sizeof(line), stdin)) { - strncat(full_message, line, sizeof(full_message) - strlen(full_message) - 1); - if (strstr(line, "-----END OTP MESSAGE-----")) { - break; - } - } - - return decrypt_text(NULL, full_message); - } - else { - // Check if it looks like a file path - if (access(input_line, R_OK) == 0) { - // It's a valid file - decrypt it with enhanced input for output filename - char temp_default[512]; - char default_output[512]; - strncpy(temp_default, input_line, sizeof(temp_default) - 1); - temp_default[sizeof(temp_default) - 1] = '\0'; - - // Remove common encrypted extensions to get a better default - if (strstr(temp_default, ".otp.asc")) { - // Replace .otp.asc with original extension or no extension - char* ext_pos = strstr(temp_default, ".otp.asc"); - *ext_pos = '\0'; - } else if (strstr(temp_default, ".otp")) { - // Replace .otp with original extension or no extension - char* ext_pos = strstr(temp_default, ".otp"); - *ext_pos = '\0'; - } else { - // No recognized encrypted extension, add .decrypted suffix - strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1); - } - - // Apply files directory default path - get_default_file_path(temp_default, default_output, sizeof(default_output)); - - char output_file[512]; - if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { - printf("Error: Failed to read input\n"); - return 1; - } - - return decrypt_file(input_line, output_file); - } else { - printf("Input not recognized as ASCII armor or valid file path.\n"); - return 1; - } - } -} - -uint64_t parse_size_string(const char* size_str) { - if (!size_str) return 0; - - char* endptr; - double value = strtod(size_str, &endptr); - - if (value <= 0) return 0; - - // Skip whitespace - while (*endptr && isspace(*endptr)) endptr++; - - uint64_t multiplier = 1; - - if (*endptr) { - char unit[4]; - strncpy(unit, endptr, 3); - unit[3] = '\0'; - - // Convert to uppercase - for (int i = 0; unit[i]; i++) { - unit[i] = toupper(unit[i]); - } - - if (strcmp(unit, "K") == 0 || strcmp(unit, "KB") == 0) { - multiplier = 1024ULL; - } else if (strcmp(unit, "M") == 0 || strcmp(unit, "MB") == 0) { - multiplier = 1024ULL * 1024ULL; - } else if (strcmp(unit, "G") == 0 || strcmp(unit, "GB") == 0) { - multiplier = 1024ULL * 1024ULL * 1024ULL; - } else if (strcmp(unit, "T") == 0 || strcmp(unit, "TB") == 0) { - multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL; - } else { - return 0; // Invalid unit - } - } else { - // No unit specified, treat as bytes - multiplier = 1; - } - - return (uint64_t)(value * multiplier); -} - -char* find_pad_by_prefix(const char* prefix) { - DIR* dir = opendir(current_pads_dir); - if (!dir) { - printf("Error: Cannot open pads directory %s\n", current_pads_dir); - return NULL; - } - - struct dirent* entry; - char* matches[100]; // Store up to 100 matches - int match_count = 0; - - // Always try hex prefix matching first - size_t prefix_len = strlen(prefix); - while ((entry = readdir(dir)) != NULL && match_count < 100) { - // Skip . and .. entries, and only process .pad files - if (entry->d_name[0] == '.') continue; - if (!strstr(entry->d_name, ".pad")) continue; - if (strlen(entry->d_name) != 68) continue; // 64 char chksum + ".pad" - - // Compare prefix with the filename (checksum part) - if (strncmp(entry->d_name, prefix, prefix_len) == 0) { - matches[match_count] = malloc(65); - strncpy(matches[match_count], entry->d_name, 64); - matches[match_count][64] = '\0'; - match_count++; - } - } - - // Only prefix matching is supported - - closedir(dir); - - if (match_count == 0) { - printf("No pads found matching '%s'\n", prefix); - printf("Available pads:\n"); - char* selected = select_pad_interactive("Available pads:", "Available pads (press Enter to continue)", PAD_FILTER_ALL, 0); - if (selected) { - free(selected); - } - return NULL; - } else if (match_count == 1) { - char* result = matches[0]; - return result; - } else { - printf("Multiple matches found for '%s':\n", prefix); - for (int i = 0; i < match_count; i++) { - printf(" %.16s...\n", matches[i]); - } - printf("Please be more specific with your prefix.\n"); - - // Free all matches and return NULL - for (int i = 0; i < match_count; i++) { - free(matches[i]); - } - return NULL; - } -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// PADS MENU -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - - -int show_pad_info(const char* chksum) { - char pad_filename[MAX_HASH_LENGTH + 10]; - char state_filename[MAX_HASH_LENGTH + 10]; - - snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum); - snprintf(state_filename, sizeof(state_filename), "%s.state", chksum); - - struct stat st; - if (stat(pad_filename, &st) != 0) { - printf("Pad not found: %s\n", chksum); - return 1; - } - - uint64_t used_bytes; - read_state_offset(chksum, &used_bytes); - - print_centered_header("Pad Information", 0); - printf("ChkSum: %s\n", chksum); - printf("File: %s\n", pad_filename); - - double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0); - double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0); - double remaining_gb = (double)(st.st_size - used_bytes) / (1024.0 * 1024.0 * 1024.0); - - printf("Total size: %.2f GB (%lu bytes)\n", size_gb, st.st_size); - printf("Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes); - printf("Remaining: %.2f GB (%lu bytes)\n", remaining_gb, st.st_size - used_bytes); - printf("Usage: %.1f%%\n", (double)used_bytes / st.st_size * 100.0); - - return 0; -} - - -/* -// MOVED TO src/util.c -void show_progress(uint64_t current, uint64_t total, time_t start_time) { - time_t now = time(NULL); - double elapsed = difftime(now, start_time); - - if (elapsed < 1.0) elapsed = 1.0; // Avoid division by zero - - double percentage = (double)current / total * 100.0; - double speed = (double)current / elapsed / (1024.0 * 1024.0); // MB/s - - uint64_t remaining_bytes = total - current; - double eta = remaining_bytes / (current / elapsed); - - printf("\rProgress: %.1f%% (%.1f MB/s, ETA: %.0fs) ", percentage, speed, eta); - fflush(stdout); -} -*/ - -int generate_pad(uint64_t size_bytes, int display_progress) { - // Ensure pads directory exists - if (ensure_pads_directory() != 0) { - printf("Error: Cannot create pads directory\n"); - return 1; - } - - char temp_filename[1024]; - char pad_path[MAX_HASH_LENGTH + 20]; - char state_path[MAX_HASH_LENGTH + 20]; - char chksum_hex[MAX_HASH_LENGTH]; - - // Create temporary filename in the pads directory to avoid cross-filesystem issues - snprintf(temp_filename, sizeof(temp_filename), "%s/temp_%ld.pad", current_pads_dir, time(NULL)); - - FILE* urandom = fopen("/dev/urandom", "rb"); - if (!urandom) { - printf("Error: Cannot open /dev/urandom\n"); - return 1; - } - - FILE* pad_file = fopen(temp_filename, "wb"); - if (!pad_file) { - printf("Error: Cannot create temporary pad file %s\n", temp_filename); - fclose(urandom); - return 1; - } - - unsigned char buffer[64 * 1024]; // 64KB buffer - uint64_t bytes_written = 0; - time_t start_time = time(NULL); - - if (display_progress) { - printf("Generating pad...\n"); - } - - while (bytes_written < size_bytes) { - uint64_t chunk_size = sizeof(buffer); - if (size_bytes - bytes_written < chunk_size) { - chunk_size = size_bytes - bytes_written; - } - - if (fread(buffer, 1, (size_t)chunk_size, urandom) != (size_t)chunk_size) { - printf("Error: Failed to read from /dev/urandom\n"); - fclose(urandom); - fclose(pad_file); - unlink(temp_filename); - return 1; - } - - if (fwrite(buffer, 1, (size_t)chunk_size, pad_file) != (size_t)chunk_size) { - printf("Error: Failed to write to pad file\n"); - fclose(urandom); - fclose(pad_file); - unlink(temp_filename); - return 1; - } - - bytes_written += chunk_size; - - if (display_progress && bytes_written % PROGRESS_UPDATE_INTERVAL == 0) { - show_progress(bytes_written, size_bytes, start_time); - } - } - - if (display_progress) { - show_progress(size_bytes, size_bytes, start_time); - printf("\n"); - } - - fclose(urandom); - fclose(pad_file); - - // Calculate XOR checksum of the pad file - if (display_progress) { - printf("Calculating pad checksum...\n"); - } - if (calculate_checksum_with_progress(temp_filename, chksum_hex, display_progress, size_bytes) != 0) { - printf("Error: Cannot calculate pad checksum\n"); - unlink(temp_filename); - return 1; - } - - // Get final paths in pads directory - get_pad_path(chksum_hex, pad_path, state_path); - - // Rename temporary file to final name (atomic operation within same directory) - if (rename(temp_filename, pad_path) != 0) { - printf("Error: Cannot rename temporary pad file to final name\n"); - unlink(temp_filename); - return 1; - } - - // Set pad file to read-only - if (chmod(pad_path, S_IRUSR) != 0) { - printf("Warning: Cannot set pad file to read-only\n"); - } - - // Initialize state file with offset 32 (first 32 bytes reserved for checksum encryption) - FILE* state_file = fopen(state_path, "wb"); - if (state_file) { - uint64_t reserved_bytes = 32; - fwrite(&reserved_bytes, sizeof(uint64_t), 1, state_file); - fclose(state_file); - } else { - printf("Error: Failed to create state file\n"); - unlink(pad_path); - return 1; - } - - double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); - printf("Generated pad: %s (%.2f GB)\n", pad_path, size_gb); - printf("Pad checksum: %s\n", chksum_hex); - printf("State file: %s\n", state_path); - printf("Pad file set to read-only\n"); - printf("Use 'Add entropy' in Pads menu to enhance randomness.\n"); - - // Pause before returning to menu to let user see the success message - print_centered_header("Pad Generation Complete", 1); - - return 0; -} - -// In-place pad entropy addition using Chacha20 or direct XOR -int add_entropy_to_pad(const char* pad_chksum, const unsigned char* entropy_data, - size_t entropy_size, int display_progress) { - if (!pad_chksum || !entropy_data || entropy_size < 512) { - printf("Error: Invalid entropy data or insufficient entropy\n"); - return 1; - } - - // Get pad file path - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Check if pad exists and get size - struct stat pad_stat; - if (stat(pad_path, &pad_stat) != 0) { - printf("Error: Pad file not found: %s\n", pad_path); - return 1; - } - - uint64_t pad_size = pad_stat.st_size; - - // Determine entropy addition method based on entropy size vs pad size - if (entropy_size >= pad_size) { - // Use direct XOR when entropy >= pad size - return add_entropy_direct_xor(pad_chksum, entropy_data, entropy_size, pad_size, display_progress); - } else { - // Use ChaCha20 when entropy < pad size - return add_entropy_chacha20(pad_chksum, entropy_data, entropy_size, pad_size, display_progress); - } -} - -// Direct XOR entropy addition for large entropy sources -int add_entropy_direct_xor(const char* pad_chksum, const unsigned char* entropy_data, - size_t entropy_size, uint64_t pad_size, int display_progress) { - // Get pad file path - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Open pad file for read/write - FILE* pad_file = fopen(pad_path, "r+b"); - if (!pad_file) { - printf("Error: Cannot open pad file for modification: %s\n", pad_path); - printf("Note: Pad files are read-only. Temporarily changing permissions...\n"); - - // Try to make writable temporarily - if (chmod(pad_path, S_IRUSR | S_IWUSR) != 0) { - printf("Error: Cannot change pad file permissions\n"); - return 1; - } - - pad_file = fopen(pad_path, "r+b"); - if (!pad_file) { - printf("Error: Still cannot open pad file for modification\n"); - // Restore read-only - chmod(pad_path, S_IRUSR); - return 1; - } - } - - if (display_progress) { - printf("Adding entropy to pad using direct XOR...\n"); - printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size); - printf("Entropy size: %zu bytes\n", entropy_size); - } - - // Process pad in chunks - unsigned char buffer[64 * 1024]; // 64KB chunks - size_t entropy_offset = 0; - uint64_t offset = 0; - time_t start_time = time(NULL); - - while (offset < pad_size) { - size_t chunk_size = sizeof(buffer); - if (pad_size - offset < chunk_size) { - chunk_size = pad_size - offset; - } - - // Read current pad data - if (fread(buffer, 1, chunk_size, pad_file) != chunk_size) { - printf("Error: Cannot read pad data at offset %lu\n", offset); - fclose(pad_file); - chmod(pad_path, S_IRUSR); // Restore read-only - return 1; - } - - // XOR with entropy data (wrap around if entropy smaller than pad) - for (size_t i = 0; i < chunk_size; i++) { - buffer[i] ^= entropy_data[entropy_offset % entropy_size]; - entropy_offset++; - } - - // Seek back and write modified data - if (fseek(pad_file, offset, SEEK_SET) != 0) { - printf("Error: Cannot seek to offset %lu\n", offset); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - return 1; - } - - if (fwrite(buffer, 1, chunk_size, pad_file) != chunk_size) { - printf("Error: Cannot write modified pad data\n"); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - return 1; - } - - offset += chunk_size; - - // Show progress for large pads - if (display_progress && offset % (64 * 1024 * 1024) == 0) { // Every 64MB - show_progress(offset, pad_size, start_time); - } - } - - fclose(pad_file); - - // Restore read-only permissions - if (chmod(pad_path, S_IRUSR) != 0) { - printf("Warning: Cannot restore pad file to read-only\n"); - } - - if (display_progress) { - show_progress(pad_size, pad_size, start_time); - printf("\nāœ“ Entropy successfully added to pad using direct XOR\n"); - printf("āœ“ Pad integrity maintained\n"); - printf("āœ“ %zu bytes of entropy distributed across entire pad\n", entropy_size); - printf("āœ“ Pad restored to read-only mode\n"); - - // Update checksum after entropy addition - printf("\nšŸ”„ Updating pad checksum...\n"); - char new_chksum[65]; - int checksum_result = update_pad_checksum_after_entropy(pad_chksum, new_chksum); - - if (checksum_result == 0) { - printf("āœ“ Pad checksum updated successfully\n"); - printf(" Old checksum: %.16s...\n", pad_chksum); - printf(" New checksum: %.16s...\n", new_chksum); - printf("āœ“ Pad files renamed to new checksum\n"); - - // Pause before returning to menu to let user see the success message - print_centered_header("Entropy Addition Complete", 1); - } else if (checksum_result == 2) { - printf("ℹ Checksum unchanged (unusual but not an error)\n"); - } else { - printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n"); - printf(" You may need to manually handle the checksum update\n"); - return 1; // Report error despite successful entropy addition - } - } - - return 0; -} - -// ChaCha20 entropy addition for smaller entropy sources -int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_data, - size_t entropy_size, uint64_t pad_size, int display_progress) { - // Derive Chacha20 key and nonce from entropy - unsigned char key[32], nonce[12]; - if (derive_chacha20_params(entropy_data, entropy_size, key, nonce) != 0) { - printf("Error: Failed to derive Chacha20 parameters from entropy\n"); - return 1; - } - - // Get pad file path - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Open pad file for read/write - FILE* pad_file = fopen(pad_path, "r+b"); - if (!pad_file) { - printf("Error: Cannot open pad file for modification: %s\n", pad_path); - printf("Note: Pad files are read-only. Temporarily changing permissions...\n"); - - // Try to make writable temporarily - if (chmod(pad_path, S_IRUSR | S_IWUSR) != 0) { - printf("Error: Cannot change pad file permissions\n"); - return 1; - } - - pad_file = fopen(pad_path, "r+b"); - if (!pad_file) { - printf("Error: Still cannot open pad file for modification\n"); - // Restore read-only - chmod(pad_path, S_IRUSR); - return 1; - } - } - - if (display_progress) { - printf("Adding entropy to pad using Chacha20...\n"); - printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size); - } - - // Process pad in chunks - unsigned char buffer[64 * 1024]; // 64KB chunks - unsigned char keystream[64 * 1024]; - uint64_t offset = 0; - uint32_t counter = 0; - time_t start_time = time(NULL); - - while (offset < pad_size) { - size_t chunk_size = sizeof(buffer); - if (pad_size - offset < chunk_size) { - chunk_size = pad_size - offset; - } - - // Read current pad data - if (fread(buffer, 1, chunk_size, pad_file) != chunk_size) { - printf("Error: Cannot read pad data at offset %lu\n", offset); - fclose(pad_file); - chmod(pad_path, S_IRUSR); // Restore read-only - return 1; - } - - // Generate keystream for this chunk - if (chacha20_encrypt(key, counter, nonce, buffer, keystream, chunk_size) != 0) { - printf("Error: Chacha20 keystream generation failed\n"); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - return 1; - } - - // XOR existing pad with keystream (adds entropy) - for (size_t i = 0; i < chunk_size; i++) { - buffer[i] ^= keystream[i]; - } - - // Seek back and write modified data - if (fseek(pad_file, offset, SEEK_SET) != 0) { - printf("Error: Cannot seek to offset %lu\n", offset); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - return 1; - } - - if (fwrite(buffer, 1, chunk_size, pad_file) != chunk_size) { - printf("Error: Cannot write modified pad data\n"); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - return 1; - } - - offset += chunk_size; - counter += (chunk_size + 63) / 64; // Round up for block count - - // Show progress for large pads - if (display_progress && offset % (64 * 1024 * 1024) == 0) { // Every 64MB - show_progress(offset, pad_size, start_time); - } - } - - fclose(pad_file); - - // Restore read-only permissions - if (chmod(pad_path, S_IRUSR) != 0) { - printf("Warning: Cannot restore pad file to read-only\n"); - } - - if (display_progress) { - show_progress(pad_size, pad_size, start_time); - printf("\nāœ“ Entropy successfully added to pad using Chacha20\n"); - printf("āœ“ Pad integrity maintained\n"); - printf("āœ“ %zu bytes of entropy distributed across entire pad\n", entropy_size); - printf("āœ“ Pad restored to read-only mode\n"); - - // Update checksum after entropy addition - printf("\nšŸ”„ Updating pad checksum...\n"); - char new_chksum[65]; - int checksum_result = update_pad_checksum_after_entropy(pad_chksum, new_chksum); - - if (checksum_result == 0) { - printf("āœ“ Pad checksum updated successfully\n"); - printf(" Old checksum: %.16s...\n", pad_chksum); - printf(" New checksum: %.16s...\n", new_chksum); - printf("āœ“ Pad files renamed to new checksum\n"); - - // Pause before returning to menu to let user see the success message - print_centered_header("Entropy Addition Complete", 1); - } else if (checksum_result == 2) { - printf("ℹ Checksum unchanged (unusual but not an error)\n"); - } else { - printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n"); - printf(" You may need to manually handle the checksum update\n"); - return 1; // Report error despite successful entropy addition - } - } - - return 0; -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// ENCRYPT AND DECRYPT -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - - -int encrypt_text(const char* pad_identifier, const char* input_text) { - char* pad_chksum = find_pad_by_prefix(pad_identifier); - if (!pad_chksum) { - return 1; - } - - char text_buffer[MAX_INPUT_SIZE]; - char chksum_hex[MAX_HASH_LENGTH]; - uint64_t current_offset; - - char pad_path[MAX_HASH_LENGTH + 20]; - char state_path[MAX_HASH_LENGTH + 20]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Check if pad file exists - if (access(pad_path, R_OK) != 0) { - printf("Error: Pad file %s not found\n", pad_path); - free(pad_chksum); - return 1; - } - - // Read current offset - if (read_state_offset(pad_chksum, ¤t_offset) != 0) { - printf("Error: Cannot read state file\n"); - free(pad_chksum); - return 1; - } - - // Ensure we never encrypt before offset 32 (reserved for checksum encryption) - if (current_offset < 32) { - printf("Warning: State offset below reserved area, adjusting to 32\n"); - current_offset = 32; - if (write_state_offset(pad_chksum, current_offset) != 0) { - printf("Warning: Failed to update state file\n"); - } - } - - // Calculate XOR checksum of pad file - if (calculate_checksum(pad_path, chksum_hex) != 0) { - printf("Error: Cannot calculate pad checksum\n"); - free(pad_chksum); - return 1; - } - - // Get input text - either from parameter or user input - if (input_text != NULL) { - // Use provided text - strncpy(text_buffer, input_text, sizeof(text_buffer) - 1); - text_buffer[sizeof(text_buffer) - 1] = '\0'; - } else { - // Get input text from user (interactive mode) - if (is_interactive_mode) { - printf("\nText input options:\n"); - printf(" 1. Type text directly\n"); - printf(" 2. Use text editor\n"); - printf("Enter choice (1-2): "); - } - - char input_choice[10] = "1"; // Default to direct input in non-interactive mode - if (is_interactive_mode) { - if (!fgets(input_choice, sizeof(input_choice), stdin)) { - printf("Error: Failed to read input\n"); - free(pad_chksum); - return 1; - } - } - - if (is_interactive_mode && atoi(input_choice) == 2) { - // Use text editor - if (launch_text_editor(NULL, text_buffer, sizeof(text_buffer)) != 0) { - if (is_interactive_mode) { - printf("Falling back to direct text input.\n"); - printf("Enter text to encrypt: "); - } - fflush(stdout); - - if (fgets(text_buffer, sizeof(text_buffer), stdin) == NULL) { - printf("Error: Failed to read input\n"); - free(pad_chksum); - return 1; - } - - // Remove newline if present - size_t len = strlen(text_buffer); - if (len > 0 && text_buffer[len - 1] == '\n') { - text_buffer[len - 1] = '\0'; - } - } - } else { - // Direct text input - if (is_interactive_mode) { - printf("Enter text to encrypt: "); - fflush(stdout); - } - - if (fgets(text_buffer, sizeof(text_buffer), stdin) == NULL) { - printf("Error: Failed to read input\n"); - free(pad_chksum); - return 1; - } - - // Remove newline if present - size_t len = strlen(text_buffer); - if (len > 0 && text_buffer[len - 1] == '\n') { - text_buffer[len - 1] = '\0'; - } - } - } - - size_t input_len = strlen(text_buffer); - if (input_len == 0) { - printf("Error: No input provided\n"); - free(pad_chksum); - 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"); - free(pad_chksum); - return 1; - } - - if (current_offset + input_len > (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); - free(pad_chksum); - return 1; - } - - // Read pad data at current offset - FILE* pad_file = fopen(pad_path, "rb"); - if (!pad_file) { - printf("Error: Cannot open pad file\n"); - free(pad_chksum); - return 1; - } - - if (fseek(pad_file, current_offset, SEEK_SET) != 0) { - printf("Error: Cannot seek to offset in pad file\n"); - fclose(pad_file); - free(pad_chksum); - return 1; - } - - unsigned char* pad_data = malloc(input_len); - if (fread(pad_data, 1, input_len, pad_file) != input_len) { - printf("Error: Cannot read pad data\n"); - free(pad_data); - fclose(pad_file); - 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) { - printf("Error: Encryption operation failed\n"); - free(pad_data); - free(ciphertext); - free(pad_chksum); - return 1; - } - - // Update state offset - if (write_state_offset(pad_chksum, current_offset + input_len) != 0) { - printf("Warning: Failed to update state file\n"); - } - - // Use universal ASCII armor generator - char* ascii_output; - if (generate_ascii_armor(chksum_hex, current_offset, ciphertext, input_len, &ascii_output) != 0) { - printf("Error: Failed to generate ASCII armor\n"); - free(pad_data); - free(ciphertext); - free(pad_chksum); - return 1; - } - - // Output with appropriate formatting - clean format for piping, spaced format for interactive - int is_interactive = (input_text == NULL); // Interactive if no input_text provided - - if (is_interactive) { - printf("\n\n\n%s\n\n", ascii_output); - } else { - printf("%s\n", ascii_output); // Add newline for proper piping with tee - } - - // Cleanup - free(pad_data); - free(ciphertext); - free(ascii_output); - free(pad_chksum); - - return 0; -} - -int decrypt_text(const char* pad_identifier, const char* encrypted_message) { - // Use universal decrypt function with mode based on global interactive mode detection - (void)pad_identifier; // Suppress unused parameter warning - chksum comes from message - decrypt_mode_t mode = is_interactive_mode ? DECRYPT_MODE_INTERACTIVE : DECRYPT_MODE_SILENT; - return universal_decrypt(encrypted_message, NULL, mode); -} - -int encrypt_file(const char* pad_identifier, const char* input_file, const char* output_file, int ascii_armor) { - char* pad_chksum = find_pad_by_prefix(pad_identifier); - if (!pad_chksum) { - return 1; - } - - char chksum_hex[MAX_HASH_LENGTH]; - uint64_t current_offset; - - char pad_path[MAX_HASH_LENGTH + 20]; - char state_path[MAX_HASH_LENGTH + 20]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Check if input file exists and get its size - struct stat input_stat; - if (stat(input_file, &input_stat) != 0) { - printf("Error: Input file %s not found\n", input_file); - free(pad_chksum); - return 1; - } - - uint64_t file_size = input_stat.st_size; - if (file_size == 0) { - printf("Error: Input file is empty\n"); - free(pad_chksum); - return 1; - } - - // Check if pad file exists - if (access(pad_path, R_OK) != 0) { - printf("Error: Pad file %s not found\n", pad_path); - free(pad_chksum); - return 1; - } - - // Read current offset - if (read_state_offset(pad_chksum, ¤t_offset) != 0) { - printf("Error: Cannot read state file\n"); - free(pad_chksum); - return 1; - } - - // Ensure we never encrypt before offset 32 - if (current_offset < 32) { - printf("Warning: State offset below reserved area, adjusting to 32\n"); - current_offset = 32; - if (write_state_offset(pad_chksum, current_offset) != 0) { - printf("Warning: Failed to update state file\n"); - } - } - - // Calculate XOR checksum of pad file - if (calculate_checksum(pad_path, chksum_hex) != 0) { - printf("Error: Cannot calculate pad checksum\n"); - free(pad_chksum); - 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"); - free(pad_chksum); - return 1; - } - - if (current_offset + file_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); - free(pad_chksum); - return 1; - } - - // Generate output filename if not specified, using files directory - char default_output[512]; - if (output_file == NULL) { - char temp_output[512]; - if (ascii_armor) { - snprintf(temp_output, sizeof(temp_output), "%s.otp.asc", input_file); - } else { - snprintf(temp_output, sizeof(temp_output), "%s.otp", input_file); - } - - // Apply files directory default path - get_default_file_path(temp_output, default_output, sizeof(default_output)); - output_file = default_output; - } - - // Open input file - FILE* input_fp = fopen(input_file, "rb"); - if (!input_fp) { - printf("Error: Cannot open input file %s\n", input_file); - free(pad_chksum); - return 1; - } - - // Open pad file - FILE* pad_file = fopen(pad_path, "rb"); - if (!pad_file) { - printf("Error: Cannot open pad file\n"); - fclose(input_fp); - free(pad_chksum); - return 1; - } - - if (fseek(pad_file, current_offset, SEEK_SET) != 0) { - printf("Error: Cannot seek to offset in pad file\n"); - fclose(input_fp); - fclose(pad_file); - free(pad_chksum); - 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; - time_t start_time = time(NULL); - - 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); - } - } - - if (file_size > 10 * 1024 * 1024) { - show_progress(file_size, file_size, start_time); - printf("\n"); - } - - fclose(input_fp); - fclose(pad_file); - - // Write output file - if (ascii_armor) { - // ASCII armored format - same as message format - FILE* output_fp = fopen(output_file, "w"); - if (!output_fp) { - printf("Error: Cannot create output file %s\n", output_file); - free(encrypted_data); - free(pad_chksum); - return 1; - } - - // Use universal ASCII armor generator - char* ascii_output; - if (generate_ascii_armor(chksum_hex, current_offset, encrypted_data, file_size, &ascii_output) != 0) { - printf("Error: Failed to generate ASCII armor\n"); - fclose(output_fp); - free(encrypted_data); - free(pad_chksum); - return 1; - } - - // Write the ASCII armored output to file - fprintf(output_fp, "%s", ascii_output); - - fclose(output_fp); - free(ascii_output); - } else { - // Binary format - 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_chksum); - return 1; - } - - // Write binary header - // Magic: "OTP\0" - fwrite("OTP\0", 1, 4, output_fp); - - // Version: 2 bytes - uint16_t version = 1; - fwrite(&version, sizeof(uint16_t), 1, output_fp); - - // Pad checksum: 32 bytes (binary) - unsigned char pad_chksum_bin[32]; - for (int i = 0; i < 32; i++) { - sscanf(chksum_hex + i*2, "%2hhx", &pad_chksum_bin[i]); - } - fwrite(pad_chksum_bin, 1, 32, output_fp); - - // Pad offset: 8 bytes - fwrite(¤t_offset, sizeof(uint64_t), 1, output_fp); - - - // File mode: 4 bytes - uint32_t file_mode = input_stat.st_mode; - fwrite(&file_mode, sizeof(uint32_t), 1, output_fp); - - // File size: 8 bytes - fwrite(&file_size, sizeof(uint64_t), 1, output_fp); - - // Encrypted data - fwrite(encrypted_data, 1, file_size, output_fp); - - fclose(output_fp); - } - - // Update state offset - if (write_state_offset(pad_chksum, current_offset + file_size) != 0) { - printf("Warning: Failed to update state file\n"); - } - - printf("File encrypted successfully: %s\n", output_file); - if (ascii_armor) { - printf("Format: ASCII armored (.otp.asc)\n"); - } else { - printf("Format: Binary (.otp)\n"); - } - - // Pause before returning to menu to let user see the success message - print_centered_header("File Encryption Complete", 1); - - // Cleanup - free(encrypted_data); - free(pad_chksum); - - return 0; -} - -int decrypt_file(const char* input_file, const char* output_file) { - // Check if input file exists - if (access(input_file, R_OK) != 0) { - printf("Error: Input file %s not found\n", input_file); - return 1; - } - - FILE* input_fp = fopen(input_file, "rb"); - if (!input_fp) { - printf("Error: Cannot open input file %s\n", input_file); - return 1; - } - - // Read first few bytes to determine format - char magic[4]; - if (fread(magic, 1, 4, input_fp) != 4) { - printf("Error: Cannot read file header\n"); - fclose(input_fp); - return 1; - } - - fseek(input_fp, 0, SEEK_SET); // Reset to beginning - - if (memcmp(magic, "OTP\0", 4) == 0) { - // Binary format - return decrypt_binary_file(input_fp, output_file); - } else { - // Assume ASCII armored format, read entire file as text - fclose(input_fp); - return decrypt_ascii_file(input_file, output_file); - } -} - -int decrypt_binary_file(FILE* input_fp, const char* output_file) { - // Read binary header - char magic[4]; - uint16_t version; - unsigned char pad_chksum_bin[32]; - uint64_t pad_offset; - uint32_t file_mode; - uint64_t 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) { - printf("Error: Cannot read binary header\n"); - fclose(input_fp); - return 1; - } - - if (memcmp(magic, "OTP\0", 4) != 0) { - printf("Error: Invalid binary format\n"); - fclose(input_fp); - return 1; - } - - // Convert binary checksum to hex - char pad_chksum_hex[65]; - for (int i = 0; i < 32; i++) { - sprintf(pad_chksum_hex + i*2, "%02x", pad_chksum_bin[i]); - } - pad_chksum_hex[64] = '\0'; - - printf("Decrypting binary file...\n"); - printf("File size: %lu bytes\n", file_size); - - // Check if we have the required pad - char pad_path[MAX_HASH_LENGTH + 20]; - char state_path[MAX_HASH_LENGTH + 20]; - get_pad_path(pad_chksum_hex, pad_path, state_path); - - if (access(pad_path, R_OK) != 0) { - printf("Error: Required pad not found: %s\n", pad_chksum_hex); - printf("Available pads:\n"); - char* selected = select_pad_interactive("Available pads:", "Available pads (press Enter to continue)", PAD_FILTER_ALL, 0); - if (selected) { - free(selected); - } - fclose(input_fp); - return 1; - } - - // Determine output filename - char default_output[512]; - if (output_file == NULL) { - snprintf(default_output, sizeof(default_output), "decrypted.bin"); - 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) { - printf("Error: Cannot read encrypted data\n"); - free(encrypted_data); - fclose(input_fp); - return 1; - } - fclose(input_fp); - - // Open pad file and decrypt - FILE* pad_file = fopen(pad_path, "rb"); - if (!pad_file) { - printf("Error: Cannot open pad file\n"); - free(encrypted_data); - return 1; - } - - if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { - printf("Error: Cannot seek to offset in pad file\n"); - free(encrypted_data); - fclose(pad_file); - return 1; - } - - unsigned char* pad_data = malloc(file_size); - if (fread(pad_data, 1, file_size, pad_file) != file_size) { - printf("Error: Cannot read pad data\n"); - free(encrypted_data); - free(pad_data); - fclose(pad_file); - return 1; - } - fclose(pad_file); - - // Use universal XOR operation for decryption - if (universal_xor_operation(encrypted_data, file_size, pad_data, encrypted_data) != 0) { - printf("Error: Decryption operation failed\n"); - free(encrypted_data); - free(pad_data); - return 1; - } - - // Write decrypted file - 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) { - printf("Error: Cannot write decrypted data\n"); - free(encrypted_data); - free(pad_data); - fclose(output_fp); - return 1; - } - fclose(output_fp); - - // Restore file permissions - if (chmod(output_file, file_mode) != 0) { - printf("Warning: Cannot restore file permissions\n"); - } - - printf("File decrypted successfully: %s\n", output_file); - printf("Restored permissions and metadata\n"); - - // Pause before returning to menu to let user see the success message - print_centered_header("File Decryption Complete", 1); - - // Cleanup - free(encrypted_data); - free(pad_data); - - return 0; -} - -int decrypt_ascii_file(const char* input_file, const char* output_file) { - // Use universal decrypt function with file-to-file mode - return universal_decrypt(input_file, output_file, DECRYPT_MODE_FILE_TO_FILE); -} - -int read_state_offset(const char* pad_chksum, uint64_t* offset) { - char state_filename[1024]; - snprintf(state_filename, sizeof(state_filename), "%s/%s.state", current_pads_dir, pad_chksum); - - FILE* state_file = fopen(state_filename, "rb"); - if (!state_file) { - *offset = 0; - return 0; - } - - if (fread(offset, sizeof(uint64_t), 1, state_file) != 1) { - fclose(state_file); - *offset = 0; - return 0; - } - - fclose(state_file); - return 0; -} - -int write_state_offset(const char* pad_chksum, uint64_t offset) { - char state_filename[1024]; - snprintf(state_filename, sizeof(state_filename), "%s/%s.state", current_pads_dir, pad_chksum); - - FILE* state_file = fopen(state_filename, "wb"); - if (!state_file) { - return 1; - } - - if (fwrite(&offset, sizeof(uint64_t), 1, state_file) != 1) { - fclose(state_file); - return 1; - } - - fclose(state_file); - return 0; -} - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// DIRECTORY MANAGEMENT FUNCTIONS -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -int ensure_pads_directory(void) { - struct stat st = {0}; - if (stat(current_pads_dir, &st) == -1) { - if (mkdir(current_pads_dir, 0755) != 0) { - return 1; - } - } - return 0; -} - -const char* get_files_directory(void) { - struct stat st = {0}; - if (stat(FILES_DIR, &st) == 0 && S_ISDIR(st.st_mode)) { - return FILES_DIR; - } - return "."; // Fall back to current directory -} - -void get_default_file_path(const char* filename, char* result_path, size_t result_size) { - const char* files_dir = get_files_directory(); - - // If filename already has a path (contains '/'), use it as-is - if (strchr(filename, '/') != NULL) { - strncpy(result_path, filename, result_size - 1); - result_path[result_size - 1] = '\0'; - return; - } - - // Otherwise, prepend the files directory - snprintf(result_path, result_size, "%s/%s", files_dir, filename); -} - -void get_directory_display(const char* file_path, char* result, size_t result_size) { - // Extract directory path from full file path - char dir_path[512]; - char* last_slash = strrchr(file_path, '/'); - - if (last_slash) { - size_t dir_len = last_slash - file_path; - if (dir_len >= sizeof(dir_path)) { - dir_len = sizeof(dir_path) - 1; - } - strncpy(dir_path, file_path, dir_len); - dir_path[dir_len] = '\0'; - } else { - // No directory separator, assume current directory - strcpy(dir_path, "."); - } - - // USB Drive Detection and Smart Shortening - char* home_dir = getenv("HOME"); - - // Check for USB/removable media mount patterns - if (strstr(dir_path, "/media/") || strstr(dir_path, "/run/media/") || strstr(dir_path, "/mnt/")) { - // Extract USB label/name - char* media_start = NULL; - if (strstr(dir_path, "/media/")) { - media_start = strstr(dir_path, "/media/"); - } else if (strstr(dir_path, "/run/media/")) { - media_start = strstr(dir_path, "/run/media/"); - } else if (strstr(dir_path, "/mnt/")) { - media_start = strstr(dir_path, "/mnt/"); - } - - if (media_start) { - // Find the USB label part - char* path_after_media = strchr(media_start + 1, '/'); - if (path_after_media) { - path_after_media++; // Skip the slash - - // For /media/user/LABEL pattern, skip the username to get to the drive label - if (strstr(media_start, "/media/")) { - char* next_slash = strchr(path_after_media, '/'); - if (next_slash) { - path_after_media = next_slash + 1; - } - } - // For /run/media/user/LABEL pattern, skip the username - else if (strstr(media_start, "/run/media/")) { - char* next_slash = strchr(path_after_media, '/'); - if (next_slash) { - path_after_media = next_slash + 1; - } - } - - // Extract just the USB label (up to next slash or end) - char* label_end = strchr(path_after_media, '/'); - char usb_label[32]; - if (label_end) { - size_t label_len = label_end - path_after_media; - if (label_len > sizeof(usb_label) - 1) label_len = sizeof(usb_label) - 1; - strncpy(usb_label, path_after_media, label_len); - usb_label[label_len] = '\0'; - } else { - // USB label is the last part - strncpy(usb_label, path_after_media, sizeof(usb_label) - 1); - usb_label[sizeof(usb_label) - 1] = '\0'; - } - - // Format with USB: prefix, limiting total length to fit in result - snprintf(result, result_size, "USB:%s", usb_label); - // Truncate if too long - if (strlen(result) > 11) { - result[11] = '\0'; - } - return; - } - } - } - - // Home directory shortening - if (home_dir && strncmp(dir_path, home_dir, strlen(home_dir)) == 0) { - if (dir_path[strlen(home_dir)] == '/' || dir_path[strlen(home_dir)] == '\0') { - // Replace home directory with ~ - char temp[512]; - snprintf(temp, sizeof(temp), "~%s", dir_path + strlen(home_dir)); - - // If result is too long, truncate intelligently - if (strlen(temp) > 11) { - // Show ~/...end_part - char* last_part = strrchr(temp, '/'); - if (last_part && strlen(last_part) < 8) { - snprintf(result, result_size, "~...%s", last_part); - } else { - strncpy(result, temp, 11); - result[11] = '\0'; - } - } else { - strncpy(result, temp, result_size - 1); - result[result_size - 1] = '\0'; - } - return; - } - } - - // Current working directory - if (strcmp(dir_path, ".") == 0 || strcmp(dir_path, current_pads_dir) == 0) { - strncpy(result, "pads", result_size - 1); - result[result_size - 1] = '\0'; - return; - } - - // System/other paths - smart truncation with ellipsis - if (strlen(dir_path) > 11) { - // Try to show the most meaningful part - char* last_part = strrchr(dir_path, '/'); - if (last_part && strlen(last_part) < 9) { - // Show .../last_part - snprintf(result, result_size, "...%s", last_part); - } else { - // Show first part with ellipsis - strncpy(result, dir_path, 8); - strncpy(result + 8, "...", result_size - 8 - 1); - result[result_size - 1] = '\0'; - } - } else { - // Short enough, use as-is - strncpy(result, dir_path, result_size - 1); - result[result_size - 1] = '\0'; - } -} - -void get_pad_path(const char* chksum, char* pad_path, char* state_path) { - snprintf(pad_path, 1024, "%s/%s.pad", current_pads_dir, chksum); - snprintf(state_path, 1024, "%s/%s.state", current_pads_dir, chksum); -} - -// Stdin detection functions implementation -int has_stdin_data(void) { - // Check if stdin is a pipe/redirect (not a terminal) - if (!isatty(STDIN_FILENO)) { - return 1; - } - return 0; -} - -char* read_stdin_text(void) { - size_t capacity = 4096; - size_t length = 0; - char* buffer = malloc(capacity); - - if (!buffer) { - return NULL; - } - - char chunk[1024]; - while (fgets(chunk, sizeof(chunk), stdin)) { - size_t chunk_len = strlen(chunk); - - // Ensure we have enough capacity - while (length + chunk_len >= capacity) { - capacity *= 2; - char* new_buffer = realloc(buffer, capacity); - if (!new_buffer) { - free(buffer); - return NULL; - } - buffer = new_buffer; - } - - strcpy(buffer + length, chunk); - length += chunk_len; - } - - // Remove trailing newline if present - if (length > 0 && buffer[length - 1] == '\n') { - buffer[length - 1] = '\0'; - length--; - } - - // If empty, free and return NULL - if (length == 0) { - free(buffer); - return NULL; - } - - return buffer; -} - -int pipe_mode(int argc, char* argv[], const char* piped_text) { - (void)argc; // Suppress unused parameter warning - (void)argv; // Suppress unused parameter warning - - // Check if we have a default pad configured - char* default_pad = get_default_pad_path(); - if (default_pad) { - // Verify the default pad exists and extract checksum - if (access(default_pad, R_OK) == 0) { - // Extract checksum from pad filename - char* filename = strrchr(default_pad, '/'); - if (!filename) filename = default_pad; - else filename++; // Skip the '/' - - // Extract checksum (remove .pad extension) - if (strlen(filename) >= 68 && strstr(filename, ".pad")) { - char pad_checksum[65]; - strncpy(pad_checksum, filename, 64); - pad_checksum[64] = '\0'; - - free(default_pad); - - // Encrypt using the default pad (silent mode) - return encrypt_text(pad_checksum, piped_text); - } - } - - fprintf(stderr, "Error: Default pad not found or invalid: %s\n", default_pad); - free(default_pad); - return 1; - } - - fprintf(stderr, "Error: No default pad configured for pipe mode\n"); - fprintf(stderr, "Configure a default pad in ~/.otp/otp.conf\n"); - return 1; -} - -// Preferences management functions implementation -int load_preferences(void) { - char* home_dir = getenv("HOME"); - if (!home_dir) { - return 1; // No home directory - } - - char preferences_dir[1024]; - char preferences_file[2048]; // Increased buffer size to accommodate longer paths - snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir); - snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir); - - FILE* file = fopen(preferences_file, "r"); - if (!file) { - // No preferences file exists - create it and set first pad as default - - // Create .otp directory if it doesn't exist - struct stat st = {0}; - if (stat(preferences_dir, &st) == -1) { - if (mkdir(preferences_dir, 0755) != 0) { - return 1; - } - } - - // Find the first available pad to set as default - DIR* dir = opendir(current_pads_dir); - if (dir) { - struct dirent* entry; - char first_pad_path[1024]; - int found_pad = 0; - - while ((entry = readdir(dir)) != NULL && !found_pad) { - if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { - // Found a pad file - construct full absolute path - if (current_pads_dir[0] == '/') { - // Already absolute path - int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name); - if (ret >= (int)sizeof(first_pad_path)) { - // Path was truncated, skip this entry - continue; - } - } else { - // Relative path - make it absolute - char current_dir[512]; - if (getcwd(current_dir, sizeof(current_dir))) { - int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s/%s", current_dir, current_pads_dir, entry->d_name); - if (ret >= (int)sizeof(first_pad_path)) { - // Path was truncated, skip this entry - continue; - } - } else { - // Fallback to relative path - int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name); - if (ret >= (int)sizeof(first_pad_path)) { - // Path was truncated, skip this entry - continue; - } - } - } - strncpy(default_pad_path, first_pad_path, sizeof(default_pad_path) - 1); - default_pad_path[sizeof(default_pad_path) - 1] = '\0'; - found_pad = 1; - } - } - closedir(dir); - - // Create the preferences file with the default pad - if (found_pad) { - save_preferences(); - } - } - - return 0; // Successfully initialized - } - - char line[1024]; - while (fgets(line, sizeof(line), file)) { - // Remove newline - line[strcspn(line, "\n")] = 0; - - // Skip empty lines and comments - if (strlen(line) == 0 || line[0] == '#') { - continue; - } - - // Parse key=value pairs - char* equals = strchr(line, '='); - if (equals) { - *equals = '\0'; - char* key = line; - char* value = equals + 1; - - // Trim whitespace - while (*key == ' ' || *key == '\t') key++; - while (*value == ' ' || *value == '\t') value++; - - if (strcmp(key, "default_pad") == 0) { - strncpy(default_pad_path, value, sizeof(default_pad_path) - 1); - default_pad_path[sizeof(default_pad_path) - 1] = '\0'; - } - } - } - - fclose(file); - return 0; -} - -int save_preferences(void) { - char* home_dir = getenv("HOME"); - if (!home_dir) { - return 1; - } - - char preferences_dir[1024]; - char preferences_file[2048]; // Increased buffer size to accommodate longer paths - snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir); - snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir); - - // Create .otp directory if it doesn't exist - struct stat st = {0}; - if (stat(preferences_dir, &st) == -1) { - if (mkdir(preferences_dir, 0755) != 0) { - return 1; - } - } - - FILE* file = fopen(preferences_file, "w"); - if (!file) { - return 1; - } - - fprintf(file, "# OTP Preferences File\n"); - fprintf(file, "# This file is automatically generated and updated by the OTP program\n\n"); - - if (strlen(default_pad_path) > 0) { - fprintf(file, "default_pad=%s\n", default_pad_path); - } - - fclose(file); - return 0; -} - -char* get_preference(const char* key) { - if (strcmp(key, "default_pad") == 0) { - if (strlen(default_pad_path) > 0) { - return strdup(default_pad_path); - } - } - return NULL; -} - -int set_preference(const char* key, const char* value) { - if (strcmp(key, "default_pad") == 0) { - if (value) { - strncpy(default_pad_path, value, sizeof(default_pad_path) - 1); - default_pad_path[sizeof(default_pad_path) - 1] = '\0'; - } else { - default_pad_path[0] = '\0'; - } - return save_preferences(); - } - return 1; -} - -char* get_default_pad_path(void) { - if (strlen(default_pad_path) > 0) { - return strdup(default_pad_path); - } - return NULL; -} - -int set_default_pad_path(const char* pad_path) { - if (!pad_path) { - return set_preference("default_pad", NULL); - } - - // Ensure we store the full absolute path - char absolute_path[1024]; - if (pad_path[0] == '/') { - // Already absolute path - strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1); - absolute_path[sizeof(absolute_path) - 1] = '\0'; - } else { - // Relative path - make it absolute - char current_dir[512]; - if (getcwd(current_dir, sizeof(current_dir))) { - snprintf(absolute_path, sizeof(absolute_path), "%s/%s", current_dir, pad_path); - } else { - // Fallback to using the path as-is if getcwd fails - strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1); - absolute_path[sizeof(absolute_path) - 1] = '\0'; - } - } - - return set_preference("default_pad", absolute_path); -} - -// OTP thumb drive detection function implementation -int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) { - const char* mount_dirs[] = {"/media", "/run/media", "/mnt", NULL}; - - for (int mount_idx = 0; mount_dirs[mount_idx] != NULL; mount_idx++) { - DIR* mount_dir = opendir(mount_dirs[mount_idx]); - if (!mount_dir) continue; - - struct dirent* mount_entry; - while ((mount_entry = readdir(mount_dir)) != NULL) { - if (mount_entry->d_name[0] == '.') continue; - - char mount_path[1024]; // Increased buffer size - snprintf(mount_path, sizeof(mount_path), "%s/%s", mount_dirs[mount_idx], mount_entry->d_name); - - // For /media, we need to go one level deeper (user directories) - if (strcmp(mount_dirs[mount_idx], "/media") == 0) { - // This is /media/[username] - look inside for drives - DIR* user_dir = opendir(mount_path); - if (!user_dir) continue; - - struct dirent* user_entry; - while ((user_entry = readdir(user_dir)) != NULL) { - if (user_entry->d_name[0] == '.') continue; - - // Check if drive name starts with "OTP" - if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue; - - char user_mount_path[2048]; // Increased buffer size - // Verify buffer has enough space before concatenation - size_t mount_len = strlen(mount_path); - size_t entry_len = strlen(user_entry->d_name); - if (mount_len + entry_len + 2 < sizeof(user_mount_path)) { - snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name); - - // Check if this is a readable directory - DIR* drive_dir = opendir(user_mount_path); - if (drive_dir) { - closedir(drive_dir); - strncpy(otp_drive_path, user_mount_path, path_size - 1); - otp_drive_path[path_size - 1] = '\0'; - closedir(user_dir); - closedir(mount_dir); - return 1; // Found OTP drive - } - } - } - closedir(user_dir); - } else if (strcmp(mount_dirs[mount_idx], "/run/media") == 0) { - // For /run/media, we need to go one level deeper (skip username) - DIR* user_dir = opendir(mount_path); - if (!user_dir) continue; - - struct dirent* user_entry; - while ((user_entry = readdir(user_dir)) != NULL) { - if (user_entry->d_name[0] == '.') continue; - - // Check if drive name starts with "OTP" - if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue; - - char user_mount_path[2048]; // Increased buffer size - snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name); - - // Check if this is a readable directory - DIR* drive_dir = opendir(user_mount_path); - if (drive_dir) { - closedir(drive_dir); - strncpy(otp_drive_path, user_mount_path, path_size - 1); - otp_drive_path[path_size - 1] = '\0'; - closedir(user_dir); - closedir(mount_dir); - return 1; // Found OTP drive - } - } - closedir(user_dir); - } else { - // Direct mount point (like /mnt/OTP_DRIVE) - // Check if drive name starts with "OTP" - if (strncmp(mount_entry->d_name, "OTP", 3) == 0) { - DIR* drive_dir = opendir(mount_path); - if (drive_dir) { - closedir(drive_dir); - strncpy(otp_drive_path, mount_path, path_size - 1); - otp_drive_path[path_size - 1] = '\0'; - closedir(mount_dir); - return 1; // Found OTP drive - } - } - } - } - closedir(mount_dir); - } - - return 0; // No OTP drive found -} - - - - -// Custom base64 encode function - -char* custom_base64_encode(const unsigned char* input, int length) { - int output_length = 4 * ((length + 2) / 3); - char* encoded = malloc(output_length + 1); - if (!encoded) return NULL; - - int i, j; - for (i = 0, j = 0; i < length;) { - uint32_t octet_a = i < length ? input[i++] : 0; - uint32_t octet_b = i < length ? input[i++] : 0; - uint32_t octet_c = i < length ? input[i++] : 0; - - uint32_t triple = (octet_a << 16) + (octet_b << 8) + octet_c; - - encoded[j++] = base64_chars[(triple >> 18) & 63]; - encoded[j++] = base64_chars[(triple >> 12) & 63]; - encoded[j++] = base64_chars[(triple >> 6) & 63]; - encoded[j++] = base64_chars[triple & 63]; - } - - // Add padding - for (int pad = 0; pad < (3 - length % 3) % 3; pad++) { - encoded[output_length - 1 - pad] = '='; - } - - encoded[output_length] = '\0'; - return encoded; -} - -// Custom base64 decode function -unsigned char* custom_base64_decode(const char* input, int* output_length) { - int input_length = strlen(input); - if (input_length % 4 != 0) return NULL; - - *output_length = input_length / 4 * 3; - if (input[input_length - 1] == '=') (*output_length)--; - if (input[input_length - 2] == '=') (*output_length)--; - - unsigned char* decoded = malloc(*output_length); - if (!decoded) return NULL; - - int i, j; - for (i = 0, j = 0; i < input_length;) { - int sextet_a = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]]; - int sextet_b = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]]; - int sextet_c = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]]; - int sextet_d = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]]; - - if (sextet_a == -1 || sextet_b == -1 || sextet_c == -1 || sextet_d == -1) { - free(decoded); - return NULL; - } - - uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - - if (j < *output_length) decoded[j++] = (triple >> 16) & 255; - if (j < *output_length) decoded[j++] = (triple >> 8) & 255; - if (j < *output_length) decoded[j++] = triple & 255; - } - - return decoded; -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// ADD ENTROPY -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// TRUERNG DEVICE DETECTION AND COMMUNICATION -// Ported from true_rng/main.c for entropy collection -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -// Read USB device info from sysfs (ported from TrueRNG reference) -int read_usb_device_info(const char* port_name, char* vid, char* pid) { - char path[512]; - FILE *fp; - - // Try to read idVendor first (works for both ttyUSB and ttyACM devices) - snprintf(path, sizeof(path), "/sys/class/tty/%s/device/../idVendor", port_name); - fp = fopen(path, "r"); - if (fp) { - if (fgets(vid, 8, fp) != NULL) { - // Remove newline if present - int len = strlen(vid); - if (len > 0 && vid[len-1] == '\n') { - vid[len-1] = '\0'; - } - } else { - fclose(fp); - return 0; - } - fclose(fp); - } else { - return 0; - } - - // Try to read idProduct - snprintf(path, sizeof(path), "/sys/class/tty/%s/device/../idProduct", port_name); - fp = fopen(path, "r"); - if (fp) { - if (fgets(pid, 8, fp) != NULL) { - // Remove newline if present - int len = strlen(pid); - if (len > 0 && pid[len-1] == '\n') { - pid[len-1] = '\0'; - } - } else { - fclose(fp); - return 0; - } - fclose(fp); - return 1; - } else { - return 0; - } -} - -// Detect all available hardware RNG devices (TrueRNG and SwiftRNG) -int detect_all_hardware_rng_devices(hardware_rng_device_t* devices, int max_devices, int* num_devices_found) { - DIR *dir; - struct dirent *entry; - char vid[8], pid[8]; - int device_count = 0; - - *num_devices_found = 0; - - dir = opendir("/dev"); - if (dir == NULL) { - return 1; // Error opening /dev directory - } - - while ((entry = readdir(dir)) != NULL && device_count < max_devices) { - // Look for ttyUSB* or ttyACM* devices - if (strncmp(entry->d_name, "ttyUSB", 6) == 0 || - strncmp(entry->d_name, "ttyACM", 6) == 0) { - - if (read_usb_device_info(entry->d_name, vid, pid)) { - // Convert to uppercase for comparison - for (int i = 0; vid[i]; i++) vid[i] = toupper(vid[i]); - for (int i = 0; pid[i]; i++) pid[i] = toupper(pid[i]); - - truerng_device_type_t device_type = 0; - - // Check for TrueRNGproV2 / SwiftRNGproV2 - if (strcmp(vid, TRUERNGPROV2_VID) == 0 && strcmp(pid, TRUERNGPROV2_PID) == 0) { - device_type = TRUERNG_PRO_V2; - } - // Check for TrueRNGpro / SwiftRNGpro - else if (strcmp(vid, TRUERNGPRO_VID) == 0 && strcmp(pid, TRUERNGPRO_PID) == 0) { - device_type = TRUERNG_PRO; - } - // Check for TrueRNG / SwiftRNG - else if (strcmp(vid, TRUERNG_VID) == 0 && strcmp(pid, TRUERNG_PID) == 0) { - device_type = TRUERNG_ORIGINAL; - } - - if (device_type != 0) { - // Found a valid device - store it - snprintf(devices[device_count].port_path, sizeof(devices[device_count].port_path), - "/dev/%s", entry->d_name); - devices[device_count].device_type = device_type; - strncpy(devices[device_count].friendly_name, - get_truerng_device_name(device_type), - sizeof(devices[device_count].friendly_name) - 1); - devices[device_count].friendly_name[sizeof(devices[device_count].friendly_name) - 1] = '\0'; - devices[device_count].is_working = 0; // Will be tested later - - device_count++; - } - } - } - } - - closedir(dir); - *num_devices_found = device_count; - return 0; // Success -} - -// Test a hardware RNG device to verify it's working -int test_hardware_rng_device(const hardware_rng_device_t* device) { - int serial_fd = -1; - int test_result = 0; - - // Setup serial port - serial_fd = setup_truerng_serial_port(device->port_path); - if (serial_fd < 0) { - return 0; // Device not working - cannot open serial port - } - - // Try to read a small amount of data to test functionality - unsigned char test_buffer[64]; - size_t bytes_read = 0; - - // Set a short timeout for testing - struct termios tty; - if (tcgetattr(serial_fd, &tty) == 0) { - tty.c_cc[VMIN] = 0; - tty.c_cc[VTIME] = 1; // 0.1 second timeout - tcsetattr(serial_fd, TCSANOW, &tty); - } - - // Try to read test data - ssize_t result = read(serial_fd, test_buffer, sizeof(test_buffer)); - if (result > 0) { - bytes_read = result; - } - - close(serial_fd); - - // Device is working if we got at least some data - if (bytes_read > 0) { - test_result = 1; - } - - return test_result; -} - -// Interactive device selection menu -int select_hardware_rng_device_interactive(hardware_rng_device_t* devices, int num_devices, hardware_rng_device_t* selected_device) { - if (num_devices == 0) { - printf("No hardware RNG devices found.\n"); - return 1; // No devices available - } - - if (num_devices == 1) { - // Only one device - use it automatically - *selected_device = devices[0]; - printf("Found 1 hardware RNG device: %s at %s\n", - selected_device->friendly_name, selected_device->port_path); - return 0; - } - - // Multiple devices - show menu - printf("\nMultiple hardware RNG devices found:\n"); - printf("Testing each device for functionality...\n\n"); - - int working_devices = 0; - - for (int i = 0; i < num_devices; i++) { - printf("%d. %s at %s - ", i + 1, devices[i].friendly_name, devices[i].port_path); - - // Test the device - devices[i].is_working = test_hardware_rng_device(&devices[i]); - - if (devices[i].is_working) { - printf("āœ… WORKING\n"); - working_devices++; - } else { - printf("āŒ NOT WORKING\n"); - } - } - - if (working_devices == 0) { - printf("\nāŒ No working hardware RNG devices found.\n"); - printf("Please check device connections and permissions.\n"); - return 1; - } - - printf("\nSelect device to use (1-%d): ", num_devices); - - char input[10]; - if (!fgets(input, sizeof(input), stdin)) { - printf("Error reading input.\n"); - return 1; - } - - int choice = atoi(input); - if (choice < 1 || choice > num_devices) { - printf("Invalid selection.\n"); - return 1; - } - - int selected_index = choice - 1; - - if (!devices[selected_index].is_working) { - printf("āŒ Selected device is not working. Please choose a different device.\n"); - return 1; - } - - *selected_device = devices[selected_index]; - printf("Selected: %s at %s\n", selected_device->friendly_name, selected_device->port_path); - - return 0; // Success -} - -// Legacy function for backward compatibility - now uses the new multi-device detection -// Returns: 0=not found, 1=TrueRNGproV2, 2=TrueRNGpro, 3=TrueRNG -int find_truerng_port(char* port_path, size_t port_path_size, truerng_device_type_t* device_type) { - hardware_rng_device_t devices[10]; - int num_devices_found = 0; - - if (detect_all_hardware_rng_devices(devices, 10, &num_devices_found) != 0) { - return 0; // Error detecting devices - } - - if (num_devices_found == 0) { - return 0; // No devices found - } - - // For backward compatibility, return the first working device - for (int i = 0; i < num_devices_found; i++) { - if (test_hardware_rng_device(&devices[i])) { - // Found a working device - strncpy(port_path, devices[i].port_path, port_path_size - 1); - port_path[port_path_size - 1] = '\0'; - *device_type = devices[i].device_type; - return (devices[i].device_type == TRUERNG_PRO_V2) ? 1 : - (devices[i].device_type == TRUERNG_PRO) ? 2 : 3; - } - } - - return 0; // No working devices found -} - -// Setup serial port for TrueRNG communication (ported from TrueRNG reference) -int setup_truerng_serial_port(const char* port_path) { - int fd; - struct termios tty; - - fd = open(port_path, O_RDWR | O_NOCTTY); - if (fd < 0) { - return -1; - } - - // Get current port settings - if (tcgetattr(fd, &tty) != 0) { - close(fd); - return -1; - } - - // Set baud rate (TrueRNG devices use 9600) - cfsetospeed(&tty, B9600); - cfsetispeed(&tty, B9600); - - // 8N1 mode - tty.c_cflag &= ~PARENB; // No parity - tty.c_cflag &= ~CSTOPB; // 1 stop bit - tty.c_cflag &= ~CSIZE; // Clear size bits - tty.c_cflag |= CS8; // 8 data bits - tty.c_cflag &= ~CRTSCTS; // No hardware flow control - tty.c_cflag |= CREAD | CLOCAL; // Enable reading and ignore modem controls - - // Raw input mode - tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); - tty.c_iflag &= ~(IXON | IXOFF | IXANY); - tty.c_oflag &= ~OPOST; - - // Set for blocking reads - wait for data indefinitely - tty.c_cc[VMIN] = 1; // Block until at least 1 character is received - tty.c_cc[VTIME] = 0; // No timeout - - // Apply settings - if (tcsetattr(fd, TCSANOW, &tty) != 0) { - close(fd); - return -1; - } - - // Flush input buffer - tcflush(fd, TCIFLUSH); - - // Set DTR - int status; - ioctl(fd, TIOCMGET, &status); - status |= TIOCM_DTR; - ioctl(fd, TIOCMSET, &status); - - return fd; -} - -// Get friendly name for TrueRNG/SwiftRNG device type -const char* get_truerng_device_name(truerng_device_type_t device_type) { - switch (device_type) { - case TRUERNG_PRO_V2: return "TrueRNGproV2/SwiftRNGproV2"; - case TRUERNG_PRO: return "TrueRNGpro/SwiftRNGpro"; - case TRUERNG_ORIGINAL: return "TrueRNG/SwiftRNG"; - case SWIFT_RNG_PRO_V2: return "SwiftRNGproV2"; - case SWIFT_RNG_PRO: return "SwiftRNGpro"; - case SWIFT_RNG: return "SwiftRNG"; - default: return "Unknown"; - } -} - -// Test TrueRNG/SwiftRNG device speed and provide time estimates -int test_truerng_speed(void) { - char port_path[512]; - truerng_device_type_t device_type; - int serial_fd = -1; - - printf("\n"); - print_centered_header("TrueRNG/SwiftRNG Speed Test", 0); - - // Find TrueRNG device - if (!find_truerng_port(port_path, sizeof(port_path), &device_type)) { - printf("No TrueRNG/SwiftRNG device found.\n"); - printf("\nSupported devices:\n"); - printf(" - TrueRNG/SwiftRNG (PID: %s, VID: %s)\n", TRUERNG_VID, TRUERNG_PID); - printf(" - TrueRNGpro/SwiftRNGpro (PID: %s, VID: %s)\n", TRUERNGPRO_VID, TRUERNGPRO_PID); - printf(" - TrueRNGproV2/SwiftRNGproV2 (PID: %s, VID: %s)\n", TRUERNGPROV2_VID, TRUERNGPROV2_PID); - printf("\nPlease connect a TrueRNG or SwiftRNG device and try again.\n"); - return 1; - } - - printf("Found %s at %s\n", get_truerng_device_name(device_type), port_path); - printf("Running speed test...\n\n"); - - // Setup serial port - serial_fd = setup_truerng_serial_port(port_path); - if (serial_fd < 0) { - printf("Error: Cannot open TrueRNG device at %s\n", port_path); - printf("Check device permissions or run as root.\n"); - return 2; - } - - // Test with increasing sizes for comprehensive benchmarking - const size_t test_sizes[] = {1024, 1024*1024, 10*1024*1024}; // 1KB, 1MB, 10MB - const char* test_labels[] = {"1KB", "1MB", "10MB"}; - double speeds[3] = {0.0, 0.0, 0.0}; - - printf("Testing with different data sizes:\n"); - printf("%-8s %-12s %-12s %-12s\n", "Size", "Time", "Speed", "Status"); - printf("%-8s %-12s %-12s %-12s\n", "--------", "------------", "------------", "--------"); - - for (int i = 0; i < 3; i++) { - size_t test_bytes = test_sizes[i]; - unsigned char* test_buffer = malloc(test_bytes); - if (!test_buffer) { - printf("%-8s %-12s %-12s %-12s\n", test_labels[i], "N/A", "N/A", "MEM_ERR"); - continue; - } - - time_t test_start = time(NULL); - size_t test_collected = 0; - int test_failed = 0; - - while (test_collected < test_bytes && !test_failed) { - size_t bytes_needed = test_bytes - test_collected; - size_t read_size = (bytes_needed > 1024) ? 1024 : bytes_needed; - - ssize_t result = read(serial_fd, test_buffer + test_collected, read_size); - if (result <= 0) { - test_failed = 1; - break; - } - test_collected += result; - } - - if (test_failed) { - printf("%-8s %-12s %-12s %-12s\n", test_labels[i], "N/A", "N/A", "FAILED"); - free(test_buffer); - close(serial_fd); - return 3; - } - - double test_time = difftime(time(NULL), test_start); - if (test_time < 0.1) test_time = 0.1; // Minimum time to avoid division by zero - - double speed_kbps = (double)test_collected / test_time / 1024.0; - speeds[i] = speed_kbps; - - char time_str[16]; - if (test_time >= 1.0) { - snprintf(time_str, sizeof(time_str), "%.1fs", test_time); - } else { - snprintf(time_str, sizeof(time_str), "%.0fms", test_time * 1000.0); - } - - char speed_str[16]; - if (speed_kbps >= 1024.0) { - snprintf(speed_str, sizeof(speed_str), "%.1f MB/s", speed_kbps / 1024.0); - } else { - snprintf(speed_str, sizeof(speed_str), "%.0f KB/s", speed_kbps); - } - - printf("%-8s %-12s %-12s %-12s\n", test_labels[i], time_str, speed_str, "OK"); - - free(test_buffer); - } - - close(serial_fd); - - // Calculate average speed (weighted towards larger tests) - double avg_speed_kbps = (speeds[0] * 0.1 + speeds[1] * 0.3 + speeds[2] * 0.6); - - printf("\nšŸ“Š Performance Summary:\n"); - printf("Device: %s\n", get_truerng_device_name(device_type)); - printf("Average speed: %.0f KB/s (%.2f MB/s)\n", avg_speed_kbps, avg_speed_kbps / 1024.0); - - // Provide time estimates for common entropy collection amounts - printf("\nā±ļø Time Estimates for Entropy Collection:\n"); - - const size_t common_sizes[] = {1024, 2048, 4096, 1024*1024, 10*1024*1024, 100*1024*1024}; - const char* size_labels[] = {"1KB", "2KB", "4KB", "1MB", "10MB", "100MB"}; - - for (int i = 0; i < 6; i++) { - double est_time = (double)common_sizes[i] / (avg_speed_kbps * 1024.0); // Convert KB/s back to bytes/s - - char time_str[32]; - if (est_time < 1.0) { - snprintf(time_str, sizeof(time_str), "%.0fms", est_time * 1000.0); - } else if (est_time < 60.0) { - snprintf(time_str, sizeof(time_str), "%.1fs", est_time); - } else if (est_time < 3600.0) { - int mins = (int)(est_time / 60.0); - int secs = (int)(est_time) % 60; - snprintf(time_str, sizeof(time_str), "%dm %02ds", mins, secs); - } else { - int hours = (int)(est_time / 3600.0); - int mins = (int)(est_time / 60.0) % 60; - snprintf(time_str, sizeof(time_str), "%dh %02dm", hours, mins); - } - - printf(" %-6s → %s\n", size_labels[i], time_str); - } - - printf("\nāœ… Speed test completed successfully!\n"); - - // Pause before returning to menu - print_centered_header("Speed Test Complete", 1); - - return 0; -} - -// Collect entropy from TrueRNG device with equivalent quality to keyboard entropy -int collect_truerng_entropy(unsigned char* entropy_buffer, size_t target_bytes, - size_t* collected_bytes, int display_progress) { - char port_path[512]; - truerng_device_type_t device_type; - int serial_fd = -1; - - // Find TrueRNG device (legacy function for backward compatibility) - if (!find_truerng_port(port_path, sizeof(port_path), &device_type)) { - if (display_progress) { - printf("No TrueRNG/SwiftRNG device found.\n"); - printf("\nSupported devices:\n"); - printf(" - TrueRNG/SwiftRNG (PID: %s, VID: %s)\n", TRUERNG_VID, TRUERNG_PID); - printf(" - TrueRNGpro/SwiftRNGpro (PID: %s, VID: %s)\n", TRUERNGPRO_VID, TRUERNGPRO_PID); - printf(" - TrueRNGproV2/SwiftRNGproV2 (PID: %s, VID: %s)\n", TRUERNGPROV2_VID, TRUERNGPROV2_PID); - printf("\nPlease connect a TrueRNG or SwiftRNG device and try again.\n"); - } - return 1; // Device not found - } - - if (display_progress) { - printf("Found %s at %s\n", get_truerng_device_name(device_type), port_path); - printf("Collecting %zu bytes of entropy...\n", target_bytes); - } - - // Setup serial port - serial_fd = setup_truerng_serial_port(port_path); - if (serial_fd < 0) { - if (display_progress) { - printf("Error: Cannot open TrueRNG device at %s\n", port_path); - printf("Check device permissions or run as root.\n"); - } - return 2; // Serial port setup failed - } - - // Collect entropy data - size_t bytes_read = 0; - unsigned char buffer[1024]; // Read in 1KB chunks - time_t start_time = time(NULL); - - while (bytes_read < target_bytes) { - size_t chunk_size = sizeof(buffer); - if (target_bytes - bytes_read < chunk_size) { - chunk_size = target_bytes - bytes_read; - } - - size_t bytes_in_chunk = 0; - while (bytes_in_chunk < chunk_size) { - ssize_t result = read(serial_fd, buffer + bytes_in_chunk, chunk_size - bytes_in_chunk); - if (result < 0) { - if (display_progress) { - printf("Error: Failed to read from TrueRNG device\n"); - } - close(serial_fd); - return 3; // Read failed - } else if (result == 0) { - if (display_progress) { - printf("Error: No data received from TrueRNG device\n"); - } - close(serial_fd); - return 4; // No data - } - bytes_in_chunk += result; - } - - // Copy to entropy buffer - memcpy(entropy_buffer + bytes_read, buffer, chunk_size); - bytes_read += chunk_size; - - // Show progress for large collections - if (display_progress && bytes_read % (4 * 1024) == 0) { // Every 4KB - double percentage = (double)bytes_read / target_bytes * 100.0; - printf("Progress: %.1f%% (%zu/%zu bytes)\r", percentage, bytes_read, target_bytes); - fflush(stdout); - } - } - - close(serial_fd); - - if (display_progress) { - double collection_time = difftime(time(NULL), start_time); - printf("\nāœ“ TrueRNG entropy collection complete!\n"); - printf(" Collected: %zu bytes in %.1f seconds\n", bytes_read, collection_time); - printf(" Device: %s\n", get_truerng_device_name(device_type)); - if (collection_time > 0) { - printf(" Rate: %.1f KB/s\n", (double)bytes_read / collection_time / 1024.0); - } - } - - *collected_bytes = bytes_read; - return 0; // Success -} - -// Collect entropy from a specific TrueRNG/SwiftRNG device -// This function collects entropy from a user-selected device for pad enhancement -int collect_truerng_entropy_from_device(const hardware_rng_device_t* device, unsigned char* entropy_buffer, - size_t target_bytes, size_t* collected_bytes, int display_progress) { - int serial_fd = -1; - - if (display_progress) { - printf("Using %s at %s\n", device->friendly_name, device->port_path); - printf("Collecting %zu bytes of entropy...\n", target_bytes); - } - - // Setup serial port for the specific device - serial_fd = setup_truerng_serial_port(device->port_path); - if (serial_fd < 0) { - if (display_progress) { - printf("Error: Cannot open TrueRNG device at %s\n", device->port_path); - printf("Check device permissions or run as root.\n"); - } - return 2; // Serial port setup failed - } - - // Collect entropy data - size_t bytes_read = 0; - unsigned char buffer[1024]; // Read in 1KB chunks - time_t start_time = time(NULL); - - while (bytes_read < target_bytes) { - size_t chunk_size = sizeof(buffer); - if (target_bytes - bytes_read < chunk_size) { - chunk_size = target_bytes - bytes_read; - } - - size_t bytes_in_chunk = 0; - while (bytes_in_chunk < chunk_size) { - ssize_t result = read(serial_fd, buffer + bytes_in_chunk, chunk_size - bytes_in_chunk); - if (result < 0) { - if (display_progress) { - printf("Error: Failed to read from TrueRNG device\n"); - } - close(serial_fd); - return 3; // Read failed - } else if (result == 0) { - if (display_progress) { - printf("Error: No data received from TrueRNG device\n"); - } - close(serial_fd); - return 4; // No data - } - bytes_in_chunk += result; - } - - // Copy to entropy buffer - memcpy(entropy_buffer + bytes_read, buffer, chunk_size); - bytes_read += chunk_size; - - // Show progress for large collections - if (display_progress && bytes_read % (4 * 1024) == 0) { // Every 4KB - double percentage = (double)bytes_read / target_bytes * 100.0; - printf("Progress: %.1f%% (%zu/%zu bytes)\r", percentage, bytes_read, target_bytes); - fflush(stdout); - } - } - - close(serial_fd); - - if (display_progress) { - double collection_time = difftime(time(NULL), start_time); - printf("\nāœ“ TrueRNG entropy collection complete!\n"); - printf(" Collected: %zu bytes in %.1f seconds\n", bytes_read, collection_time); - printf(" Device: %s\n", device->friendly_name); - if (collection_time > 0) { - printf(" Rate: %.1f KB/s\n", (double)bytes_read / collection_time / 1024.0); - } - } - - *collected_bytes = bytes_read; - return 0; // Success -} - -// Helper function to format time in human-readable format -void format_time_remaining(double seconds, char* buffer, size_t buffer_size) { - if (seconds < 60) { - snprintf(buffer, buffer_size, "%.0fs", seconds); - } else if (seconds < 3600) { - int mins = (int)(seconds / 60); - int secs = (int)(seconds) % 60; - snprintf(buffer, buffer_size, "%dm %02ds", mins, secs); - } else { - int hours = (int)(seconds / 3600); - int mins = (int)(seconds / 60) % 60; - snprintf(buffer, buffer_size, "%dh %02dm", hours, mins); - } -} - -// Streaming TrueRNG entropy collection for full pad enhancement -// This function applies entropy directly to the pad in chunks to avoid memory issues -// entropy_mode: 0=direct XOR, 1=ChaCha20 distribution -int collect_truerng_entropy_streaming(const char* pad_chksum, size_t total_bytes, int display_progress, int entropy_mode) { - char port_path[512]; - truerng_device_type_t device_type; - int serial_fd = -1; - - // Find TrueRNG device - if (!find_truerng_port(port_path, sizeof(port_path), &device_type)) { - if (display_progress) { - printf("No TrueRNG/SwiftRNG device found.\n"); - printf("\nSupported devices:\n"); - printf(" - TrueRNG/SwiftRNG (PID: %s, VID: %s)\n", TRUERNG_VID, TRUERNG_PID); - printf(" - TrueRNGpro/SwiftRNGpro (PID: %s, VID: %s)\n", TRUERNGPRO_VID, TRUERNGPRO_PID); - printf(" - TrueRNGproV2/SwiftRNGproV2 (PID: %s, VID: %s)\n", TRUERNGPROV2_VID, TRUERNGPROV2_PID); - printf("\nPlease connect a TrueRNG or SwiftRNG device and try again.\n"); - } - return 1; // Device not found - } - - if (display_progress) { - printf("Found %s at %s\n", get_truerng_device_name(device_type), port_path); - printf("Streaming %zu bytes of entropy directly to pad...\n", total_bytes); - } - - // Setup serial port - serial_fd = setup_truerng_serial_port(port_path); - if (serial_fd < 0) { - if (display_progress) { - printf("Error: Cannot open TrueRNG device at %s\n", port_path); - printf("Check device permissions or run as root.\n"); - } - return 2; // Serial port setup failed - } - - // For large pads (>10MB), do a 1MB test to estimate completion time - double estimated_rate = 0.0; - int user_confirmed = 1; // Default to confirmed for small pads - - if (total_bytes > 10 * 1024 * 1024 && display_progress) { - printf("\nLarge pad detected (%.1f MB). Running 1MB speed test...\n", - (double)total_bytes / (1024.0 * 1024.0)); - - // Test with 1MB sample - size_t test_bytes = 1024 * 1024; // 1MB - unsigned char* test_buffer = malloc(test_bytes); - if (!test_buffer) { - printf("Error: Cannot allocate test buffer\n"); - close(serial_fd); - return 3; - } - - time_t test_start = time(NULL); - size_t test_collected = 0; - - while (test_collected < test_bytes) { - size_t bytes_needed = test_bytes - test_collected; - size_t read_size = (bytes_needed > 1024) ? 1024 : bytes_needed; - - ssize_t result = read(serial_fd, test_buffer + test_collected, read_size); - if (result <= 0) { - printf("Error: TrueRNG test failed\n"); - free(test_buffer); - close(serial_fd); - return 4; - } - test_collected += result; - - // Show test progress - double test_progress = (double)test_collected / test_bytes * 100.0; - printf("\rSpeed test: %.1f%% complete", test_progress); - fflush(stdout); - } - - double test_time = difftime(time(NULL), test_start); - estimated_rate = (double)test_bytes / test_time; // bytes per second - - // Calculate estimated total time - double estimated_total_time = (double)total_bytes / estimated_rate; - - char time_str[64]; - format_time_remaining(estimated_total_time, time_str, sizeof(time_str)); - - printf("\nSpeed test complete: %.1f KB/s\n", estimated_rate / 1024.0); - printf("Estimated completion time: %s\n", time_str); - printf("\nProceed with full pad enhancement? (y/N): "); - fflush(stdout); - - char response[10]; - if (fgets(response, sizeof(response), stdin) == NULL || - (response[0] != 'y' && response[0] != 'Y')) { - printf("Operation cancelled by user.\n"); - free(test_buffer); - close(serial_fd); - return 5; // User cancelled - } - - user_confirmed = 1; - free(test_buffer); - - // Reset serial port for main operation - close(serial_fd); - serial_fd = setup_truerng_serial_port(port_path); - if (serial_fd < 0) { - printf("Error: Cannot reopen TrueRNG device\n"); - return 6; - } - } - - if (!user_confirmed) { - close(serial_fd); - return 5; // User cancelled - } - - // Get pad paths - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Open pad file for read/write with temporary permissions - if (chmod(pad_path, S_IRUSR | S_IWUSR) != 0) { - if (display_progress) { - printf("Error: Cannot change pad file permissions\n"); - } - close(serial_fd); - return 3; - } - - FILE* pad_file = fopen(pad_path, "r+b"); - if (!pad_file) { - if (display_progress) { - printf("Error: Cannot open pad file for modification: %s\n", pad_path); - } - chmod(pad_path, S_IRUSR); // Restore read-only - close(serial_fd); - return 4; - } - - // Process pad in chunks - const size_t chunk_size = 1024 * 1024; // 1MB chunks - unsigned char* entropy_chunk = malloc(chunk_size); - unsigned char* pad_chunk = malloc(chunk_size); - unsigned char* mixed_chunk = malloc(chunk_size); - - if (!entropy_chunk || !pad_chunk || !mixed_chunk) { - if (display_progress) { - printf("Error: Cannot allocate chunk buffers\n"); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 5; - } - - size_t bytes_processed = 0; - time_t start_time = time(NULL); - - while (bytes_processed < total_bytes) { - size_t current_chunk_size = chunk_size; - if (total_bytes - bytes_processed < chunk_size) { - current_chunk_size = total_bytes - bytes_processed; - } - - // Collect entropy from TrueRNG for this chunk - size_t entropy_bytes_in_chunk = 0; - while (entropy_bytes_in_chunk < current_chunk_size) { - size_t bytes_needed = current_chunk_size - entropy_bytes_in_chunk; - size_t read_size = (bytes_needed > 1024) ? 1024 : bytes_needed; - - ssize_t result = read(serial_fd, entropy_chunk + entropy_bytes_in_chunk, read_size); - if (result < 0) { - if (display_progress) { - printf("Error: Failed to read from TrueRNG device\n"); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 6; - } else if (result == 0) { - if (display_progress) { - printf("Error: No data received from TrueRNG device\n"); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 7; - } - entropy_bytes_in_chunk += result; - } - - // Read current pad data for this chunk - if (fseek(pad_file, bytes_processed, SEEK_SET) != 0) { - if (display_progress) { - printf("Error: Cannot seek to offset %zu in pad file\n", bytes_processed); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 8; - } - - if (fread(pad_chunk, 1, current_chunk_size, pad_file) != current_chunk_size) { - if (display_progress) { - printf("Error: Cannot read pad data at offset %zu\n", bytes_processed); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 9; - } - - // Apply entropy based on selected mode - if (entropy_mode == 0) { - // Direct XOR mode - use entropy directly - for (size_t i = 0; i < current_chunk_size; i++) { - mixed_chunk[i] = pad_chunk[i] ^ entropy_chunk[i % current_chunk_size]; - } - } else { - // ChaCha20 mode - derive key and nonce from entropy chunk - unsigned char key[32], nonce[12]; - if (derive_chacha20_params(entropy_chunk, current_chunk_size, key, nonce) != 0) { - if (display_progress) { - printf("Error: Failed to derive Chacha20 parameters from entropy\n"); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 10; - } - - // Generate keystream and XOR with pad data - uint32_t counter = bytes_processed / 64; // Chacha20 block counter - if (chacha20_encrypt(key, counter, nonce, pad_chunk, mixed_chunk, current_chunk_size) != 0) { - if (display_progress) { - printf("Error: Chacha20 keystream generation failed\n"); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 11; - } - - // XOR existing pad with keystream (adds entropy) - for (size_t i = 0; i < current_chunk_size; i++) { - mixed_chunk[i] = pad_chunk[i] ^ mixed_chunk[i]; - } - } - - // Write modified data back to pad - if (fseek(pad_file, bytes_processed, SEEK_SET) != 0) { - if (display_progress) { - printf("Error: Cannot seek to write offset %zu\n", bytes_processed); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 12; - } - - if (fwrite(mixed_chunk, 1, current_chunk_size, pad_file) != current_chunk_size) { - if (display_progress) { - printf("Error: Cannot write modified pad data\n"); - } - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - chmod(pad_path, S_IRUSR); - close(serial_fd); - return 13; - } - - bytes_processed += current_chunk_size; - - // Show progress and entropy samples for visual verification - if (display_progress) { - // Update progress more frequently for better user experience - always show cumulative amount - if (bytes_processed % (4 * 1024 * 1024) == 0 || bytes_processed == total_bytes) { // Every 4MB or at completion - double percentage = (double)bytes_processed / total_bytes * 100.0; - double elapsed = difftime(time(NULL), start_time); - double rate = 0.0; - if (elapsed > 0) { - rate = (double)bytes_processed / elapsed / (1024.0 * 1024.0); - } - - // Calculate estimated time remaining - char eta_str[64] = ""; - if (rate > 0.0 && bytes_processed < total_bytes) { - double remaining_bytes = (double)(total_bytes - bytes_processed); - double eta_seconds = remaining_bytes / (rate * 1024.0 * 1024.0); // Convert rate back to bytes/sec - format_time_remaining(eta_seconds, eta_str, sizeof(eta_str)); - } - - // Clear previous line and show progress bar with cumulative TrueRNG data generated - printf("\r\033[K"); // Clear line - printf("["); - int bar_width = 30; - int filled = (int)(percentage / 100.0 * bar_width); - for (int i = 0; i < filled; i++) printf("ā–ˆ"); - for (int i = filled; i < bar_width; i++) printf("ā–‘"); - - if (strlen(eta_str) > 0) { - printf("] %.1f%% - TrueRNG: %zu MB / %zu MB (%.1f MB/s) ETA: %s", - percentage, bytes_processed / (1024*1024), total_bytes / (1024*1024), rate, eta_str); - } else { - printf("] %.1f%% - TrueRNG: %zu MB / %zu MB (%.1f MB/s)", - percentage, bytes_processed / (1024*1024), total_bytes / (1024*1024), rate); - } - fflush(stdout); - } - - // Show entropy samples every 64MB for visual verification of randomness - if (bytes_processed % (64 * 1024 * 1024) == 0 && bytes_processed > 0) { - printf("\nšŸ”¬ TrueRNG entropy sample: "); - // Display first 16 bytes of current entropy chunk as hex - size_t sample_size = (current_chunk_size < 16) ? current_chunk_size : 16; - for (size_t i = 0; i < sample_size; i++) { - printf("%02x", entropy_chunk[i]); - if (i == 7) printf(" "); // Space in middle for readability - } - printf("\n"); - } - } - } - - // Cleanup - free(entropy_chunk); - free(pad_chunk); - free(mixed_chunk); - fclose(pad_file); - close(serial_fd); - - // Restore read-only permissions - if (chmod(pad_path, S_IRUSR) != 0) { - if (display_progress) { - printf("Warning: Cannot restore pad file to read-only\n"); - } - } - - if (display_progress) { - double collection_time = difftime(time(NULL), start_time); - printf("\nāœ“ TrueRNG streaming entropy collection complete!\n"); - printf(" Enhanced: %zu bytes in %.1f seconds\n", bytes_processed, collection_time); - printf(" Device: %s\n", get_truerng_device_name(device_type)); - if (collection_time > 0) { - printf(" Rate: %.1f MB/s\n", (double)bytes_processed / collection_time / (1024.0*1024.0)); - } - printf("āœ“ Pad integrity maintained\n"); - printf("āœ“ Entropy distributed across entire pad\n"); - printf("āœ“ Pad restored to read-only mode\n"); - - // Pause before returning to menu to let user see the success message - print_centered_header("TrueRNG Enhancement Complete", 1); - } - - return 0; // Success -} - -// Collect manual entropy from any printable character input -int collect_dice_entropy(unsigned char* entropy_buffer, size_t target_bytes, - size_t* collected_bytes, int display_progress) { - if (display_progress) { - print_centered_header("Manual Entropy Collection", 0); - printf("Enter any text, numbers, or symbols for entropy.\n"); - printf("Target: %zu bytes (%zu characters needed)\n", target_bytes, target_bytes); - printf("Press Enter after each line, or 'done' when finished.\n\n"); - } - - size_t bytes_written = 0; - - char input[256]; - - while (bytes_written < target_bytes) { - if (display_progress) { - double percentage = (double)bytes_written / target_bytes * 100.0; - printf("Progress: %.1f%% (%zu/%zu bytes) - Enter text: ", - percentage, bytes_written, target_bytes); - fflush(stdout); - } - - if (!fgets(input, sizeof(input), stdin)) { - if (display_progress) { - printf("Error: Failed to read input\n"); - } - return 1; - } - - // Remove newline - input[strcspn(input, "\n")] = 0; - - // Check for done command - if (strcmp(input, "done") == 0 && bytes_written >= target_bytes / 2) { - break; // Allow early exit if we have at least half the target - } - - // Process each printable character as 8 bits of entropy - for (size_t i = 0; input[i] && bytes_written < target_bytes; i++) { - char c = input[i]; - if (c >= 32 && c <= 126) { // Printable ASCII characters - entropy_buffer[bytes_written++] = (unsigned char)c; - } - } - } - - if (display_progress) { - printf("\nāœ“ Manual entropy collection complete!\n"); - printf(" Collected: %zu bytes from text input\n", bytes_written); - printf(" Entropy quality: 8 bits per character\n"); - } - - *collected_bytes = bytes_written; - return 0; // Success -} - -// Collect entropy from binary file -int collect_file_entropy(unsigned char* entropy_buffer, size_t target_bytes, - size_t* collected_bytes, int display_progress) { - if (display_progress) { - print_centered_header("File Entropy Collection", 0); - printf("Load entropy from binary file (.bin format)\n"); - printf("Target: %zu bytes\n", target_bytes); - } - - printf("Enter path to binary entropy file: "); - fflush(stdout); - - char file_path[512]; - if (!fgets(file_path, sizeof(file_path), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - // Remove newline - file_path[strcspn(file_path, "\n")] = 0; - - // Check if file exists and get size - struct stat file_stat; - if (stat(file_path, &file_stat) != 0) { - printf("Error: File '%s' not found\n", file_path); - return 1; - } - - if (!S_ISREG(file_stat.st_mode)) { - printf("Error: '%s' is not a regular file\n", file_path); - return 1; - } - - size_t file_size = file_stat.st_size; - if (file_size == 0) { - printf("Error: File is empty\n"); - return 1; - } - - if (file_size < target_bytes) { - printf("Warning: File size (%zu bytes) is smaller than target (%zu bytes)\n", - file_size, target_bytes); - printf("Will read available data and pad with zeros if necessary.\n"); - } - - // Open file for reading - FILE* entropy_file = fopen(file_path, "rb"); - if (!entropy_file) { - printf("Error: Cannot open file '%s' for reading\n", file_path); - return 1; - } - - if (display_progress) { - printf("Reading entropy from file...\n"); - } - - // Read entropy data - size_t bytes_to_read = (file_size < target_bytes) ? file_size : target_bytes; - size_t bytes_read = fread(entropy_buffer, 1, bytes_to_read, entropy_file); - - if (bytes_read != bytes_to_read) { - printf("Error: Failed to read %zu bytes from file (read %zu)\n", - bytes_to_read, bytes_read); - fclose(entropy_file); - return 1; - } - - fclose(entropy_file); - - // Pad with zeros if file was smaller than target - if (bytes_read < target_bytes) { - memset(entropy_buffer + bytes_read, 0, target_bytes - bytes_read); - *collected_bytes = target_bytes; // We padded to target size - } else { - *collected_bytes = bytes_read; - } - - if (display_progress) { - printf("āœ“ File entropy collection complete!\n"); - printf(" File: %s\n", file_path); - printf(" Read: %zu bytes\n", bytes_read); - printf(" Total: %zu bytes (padded to target if necessary)\n", *collected_bytes); - } - - return 0; // Success -} - -// Collect entropy by source type with unified interface -int collect_entropy_by_source(entropy_source_t source, unsigned char* entropy_buffer, - size_t target_bytes, size_t* collected_bytes, int display_progress) { - switch (source) { - case ENTROPY_SOURCE_KEYBOARD: - return collect_entropy_with_feedback(entropy_buffer, target_bytes, collected_bytes, 1); - - case ENTROPY_SOURCE_TRUERNG: - return collect_truerng_entropy(entropy_buffer, target_bytes, collected_bytes, display_progress); - - case ENTROPY_SOURCE_DICE: - return collect_dice_entropy(entropy_buffer, target_bytes, collected_bytes, display_progress); - - case ENTROPY_SOURCE_FILE: - return collect_file_entropy(entropy_buffer, target_bytes, collected_bytes, display_progress); - - default: - if (display_progress) { - printf("Error: Unknown entropy source\n"); - } - return 1; - } -} - -double get_precise_time(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec + ts.tv_nsec / 1000000000.0; -} - -void draw_progress_bar(double percentage, int width) { - int filled = (int)(percentage / 100.0 * width); - if (filled > width) filled = width; - - printf("["); - for (int i = 0; i < filled; i++) { - printf("ā–ˆ"); - } - for (int i = filled; i < width; i++) { - printf("ā–‘"); - } - printf("]"); -} - -void draw_quality_bar(double quality, int width, const char* label) { - int filled = (int)(quality / 100.0 * width); - if (filled > width) filled = width; - - // Color coding based on quality - const char* color; - if (quality >= 80) color = "\033[32m"; // Green - else if (quality >= 60) color = "\033[33m"; // Yellow - else color = "\033[31m"; // Red - - printf("%s", color); - draw_progress_bar(quality, width); - printf("\033[0m %-10s", label); // Reset color -} - -double calculate_timing_quality(const entropy_collection_state_t* state) { - // Analyze timing variance between keypresses - if (state->collected_bytes < 32) return 0.0; // Need minimum data - - // Simplified timing quality based on collection rate and variation - double elapsed = get_precise_time() - state->collection_start_time; - if (elapsed < 0.1) return 0.0; - - double rate = state->collected_bytes / elapsed; - - // Optimal rate is around 50-200 bytes/second (moderate typing with good timing variance) - if (rate >= 50 && rate <= 200) return 90.0; - if (rate >= 20 && rate <= 500) return 70.0; - if (rate >= 10 && rate <= 1000) return 50.0; - return 30.0; -} - -double calculate_variety_quality(const entropy_collection_state_t* state) { - // Analyze key variety and distribution - if (state->collected_bytes < 16) return 0.0; - - // Calculate entropy from key histogram - double entropy = 0.0; - size_t total_keys = 0; - - // Count total keypresses - for (int i = 0; i < 256; i++) { - total_keys += state->key_histogram[i]; - } - - if (total_keys == 0) return 0.0; - - // Calculate Shannon entropy - for (int i = 0; i < 256; i++) { - if (state->key_histogram[i] > 0) { - double p = (double)state->key_histogram[i] / total_keys; - entropy -= p * log2(p); - } - } - - // Convert entropy to quality score (0-100) - double max_entropy = log2(256); // Perfect entropy for 8-bit keyspace - double normalized_entropy = entropy / max_entropy; - - // Scale based on unique keys as well - double unique_key_factor = (double)state->unique_keys / 50.0; // 50+ unique keys is excellent - if (unique_key_factor > 1.0) unique_key_factor = 1.0; - - return (normalized_entropy * 70.0 + unique_key_factor * 30.0); -} - -unsigned char calculate_overall_quality(const entropy_collection_state_t* state) { - double timing = calculate_timing_quality(state); - double variety = calculate_variety_quality(state); - - // Simple collection progress bonus - double progress_bonus = (double)state->collected_bytes / state->target_bytes * 20.0; - if (progress_bonus > 20.0) progress_bonus = 20.0; - - // Weighted average - double overall = (timing * 0.4 + variety * 0.4 + progress_bonus); - if (overall > 100.0) overall = 100.0; - - return (unsigned char)overall; -} - -void display_entropy_progress(const entropy_collection_state_t* state) { - // Calculate percentages - double progress = (double)state->collected_bytes / state->target_bytes * 100.0; - if (progress > 100.0) progress = 100.0; - - double quality = state->quality_score; - double timing_quality = calculate_timing_quality(state); - double variety_quality = calculate_variety_quality(state); - - // Clear previous output and redraw - printf("\033[2K\r"); // Clear line - printf("\033[A\033[2K\r"); // Move up and clear - printf("\033[A\033[2K\r"); // Move up and clear - printf("\033[A\033[2K\r"); // Move up and clear - printf("\033[A\033[2K\r"); // Move up and clear - printf("\033[A\033[2K\r"); // Move up and clear - - // Header - printf("Adding Entropy to Pad - Target: %zu bytes\n\n", state->target_bytes); - - // Main progress bar - printf("Progress: "); - draw_progress_bar(progress, 50); - printf(" %.1f%% (%zu/%zu bytes)\n", progress, state->collected_bytes, state->target_bytes); - - // Quality indicators - printf("Quality: "); - draw_quality_bar(quality, 50, "OVERALL"); - printf("\n"); - - printf("Timing: "); - draw_quality_bar(timing_quality, 50, "VARIED"); - printf("\n"); - - printf("Keys: "); - draw_quality_bar(variety_quality, 50, "DIVERSE"); - printf("\n"); - - // Instructions - if (state->collected_bytes >= 1024 && state->auto_complete_enabled) { - printf("\nPress ESC to finish (minimum reached) or continue typing..."); - } else if (state->collected_bytes < 1024) { - printf("\nType random keys... (%zu more bytes needed)", 1024 - state->collected_bytes); - } else { - printf("\nType random keys or press ESC when satisfied..."); - } - - fflush(stdout); -} - -// Chacha20 key derivation from collected entropy -int derive_chacha20_params(const unsigned char* entropy_data, size_t entropy_size, - unsigned char key[32], unsigned char nonce[12]) { - if (!entropy_data || entropy_size < 512 || !key || !nonce) { - return 1; // Error: insufficient entropy or null pointers - } - - // Phase 1: Generate base key from entropy using enhanced XOR checksum method - unsigned char enhanced_checksum[44]; // 32 key + 12 nonce - memset(enhanced_checksum, 0, 44); - - // Mix entropy data similar to calculate_checksum but for 44 bytes - for (size_t i = 0; i < entropy_size; i++) { - unsigned char bucket = i % 44; - enhanced_checksum[bucket] ^= entropy_data[i] ^ - ((i >> 8) & 0xFF) ^ - ((i >> 16) & 0xFF) ^ - ((i >> 24) & 0xFF); - } - - // Phase 2: Add system entropy for additional randomness - unsigned char system_entropy[32]; - FILE* urandom = fopen("/dev/urandom", "rb"); - if (!urandom) { - return 2; // Error: cannot access system entropy - } - - if (fread(system_entropy, 1, 32, urandom) != 32) { - fclose(urandom); - return 2; // Error: insufficient system entropy - } - fclose(urandom); - - // Mix system entropy into derived key - for (int i = 0; i < 32; i++) { - enhanced_checksum[i] ^= system_entropy[i]; - } - - // Extract key and nonce - memcpy(key, enhanced_checksum, 32); - memcpy(nonce, enhanced_checksum + 32, 12); - - return 0; // Success -} - -// Check if a pad is unused (0% usage) -int is_pad_unused(const char* pad_chksum) { - uint64_t used_bytes; - if (read_state_offset(pad_chksum, &used_bytes) != 0) { - return 0; // Error reading state, assume used - } - return (used_bytes <= 32); // Only reserved bytes used (32 bytes for checksum encryption) -} - -// Safely rename pad files (pad and state) from old to new checksum -int rename_pad_files_safely(const char* old_chksum, const char* new_chksum) { - char old_pad_path[1024], new_pad_path[1024]; - char old_state_path[1024], new_state_path[1024]; - - // Construct file paths - snprintf(old_pad_path, sizeof(old_pad_path), "%s/%s.pad", current_pads_dir, old_chksum); - snprintf(new_pad_path, sizeof(new_pad_path), "%s/%s.pad", current_pads_dir, new_chksum); - snprintf(old_state_path, sizeof(old_state_path), "%s/%s.state", current_pads_dir, old_chksum); - snprintf(new_state_path, sizeof(new_state_path), "%s/%s.state", current_pads_dir, new_chksum); - - // Check if new files would conflict with existing files - if (access(new_pad_path, F_OK) == 0) { - printf("Error: New pad file already exists: %s\n", new_pad_path); - return 1; // Conflict - } - - // Rename pad file - if (rename(old_pad_path, new_pad_path) != 0) { - printf("Error: Failed to rename pad file from %s to %s\n", old_pad_path, new_pad_path); - return 2; // Pad rename failed - } - - // Rename state file (if it exists) - if (access(old_state_path, F_OK) == 0) { - if (rename(old_state_path, new_state_path) != 0) { - printf("Warning: Failed to rename state file, but pad file was renamed successfully\n"); - // Try to rollback pad file rename - rename(new_pad_path, old_pad_path); - return 3; // State rename failed - } - } - - return 0; // Success -} - -// Update pad checksum after entropy addition -int update_pad_checksum_after_entropy(const char* old_chksum, char* new_chksum) { - char pad_path[1024]; - snprintf(pad_path, sizeof(pad_path), "%s/%s.pad", current_pads_dir, old_chksum); - - // Calculate new checksum of the modified pad - if (calculate_checksum(pad_path, new_chksum) != 0) { - printf("Error: Cannot calculate new pad checksum\n"); - return 1; - } - - // Check if checksum actually changed - if (strcmp(old_chksum, new_chksum) == 0) { - printf("Warning: Pad checksum unchanged after entropy addition\n"); - return 2; // Checksum didn't change (unusual but not fatal) - } - - // Rename pad files to use new checksum - if (rename_pad_files_safely(old_chksum, new_chksum) != 0) { - return 3; // Rename failed - } - - // Update default pad preference if this was the default pad - char* current_default = get_default_pad_path(); - if (current_default) { - // Check if the old pad was the default - if (strstr(current_default, old_chksum)) { - // Update to new checksum - char new_default_path[1024]; - snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, new_chksum); - - if (set_default_pad_path(new_default_path) != 0) { - printf("Warning: Failed to update default pad preference\n"); - } else { - printf("Updated default pad to new checksum: %.16s...\n", new_chksum); - } - } - free(current_default); - } - - return 0; // Success -} - -// Enhanced entropy collection with visual feedback -int collect_entropy_with_feedback(unsigned char* entropy_buffer, size_t target_bytes, - size_t* collected_bytes, int allow_early_exit) { - struct termios original_termios; - entropy_collection_state_t state = {0}; - - // Initialize state - state.target_bytes = target_bytes; - state.auto_complete_enabled = allow_early_exit; - state.collection_start_time = get_precise_time(); - - // Setup raw terminal - if (setup_raw_terminal(&original_termios) != 0) { - printf("Error: Cannot setup terminal for entropy collection\n"); - return 1; - } - - // Clear screen area for display - printf("\n\n\n\n\n\n"); - - unsigned char entropy_block[16]; - struct timespec timestamp; - uint32_t sequence_counter = 0; - char key; - unsigned char seen_keys[256] = {0}; - - *collected_bytes = 0; - - while (state.collected_bytes < target_bytes) { - // Update display - state.quality_score = calculate_overall_quality(&state); - display_entropy_progress(&state); - - // Non-blocking read - if (read(STDIN_FILENO, &key, 1) == 1) { - // Handle ESC key for early exit - if (key == 27 && allow_early_exit && state.collected_bytes >= 1024) { - break; // Early exit allowed - } - - // Record keypress timing - double current_time = get_precise_time(); - state.last_keypress_time = current_time; - - // Update key histogram - state.key_histogram[(unsigned char)key]++; - - // Get high precision timestamp - clock_gettime(CLOCK_MONOTONIC, ×tamp); - - // Create enhanced entropy block: [key][timestamp][sequence][quality_bits] - entropy_block[0] = key; - memcpy(&entropy_block[1], ×tamp.tv_sec, 8); - memcpy(&entropy_block[9], ×tamp.tv_nsec, 4); - memcpy(&entropy_block[13], &sequence_counter, 2); - entropy_block[15] = (unsigned char)(current_time * 1000) & 0xFF; // Sub-millisecond timing - - // Add to entropy buffer - if (state.collected_bytes + 16 <= MAX_ENTROPY_BUFFER) { - memcpy(entropy_buffer + state.collected_bytes, entropy_block, 16); - state.collected_bytes += 16; - } - - sequence_counter++; - - // Track unique keys - if (!seen_keys[(unsigned char)key]) { - seen_keys[(unsigned char)key] = 1; - state.unique_keys++; - } - } else { - // No key available, just sleep and wait for keystrokes - usleep(10000); // 10ms delay - wait for keystrokes, don't add timing entropy - } - - // Auto-complete at target if enabled - if (state.collected_bytes >= target_bytes) { - break; - } - } - - // Final display update - state.quality_score = calculate_overall_quality(&state); - display_entropy_progress(&state); - - // Summary - double collection_time = get_precise_time() - state.collection_start_time; - printf("\n\nāœ“ Entropy collection complete!\n"); - printf(" Collected: %zu bytes in %.1f seconds\n", state.collected_bytes, collection_time); - printf(" Quality: %d%% (Excellent: 80%%+, Good: 60%%+)\n", state.quality_score); - printf(" Unique keys: %zu\n", state.unique_keys); - - // Restore terminal - restore_terminal(&original_termios); - - *collected_bytes = state.collected_bytes; - return 0; -} - -// Keyboard entropy functions -int setup_raw_terminal(struct termios* original_termios) { - struct termios new_termios; - - if (tcgetattr(STDIN_FILENO, original_termios) != 0) { - return 1; - } - - new_termios = *original_termios; - new_termios.c_lflag &= ~(ICANON | ECHO); - new_termios.c_cc[VMIN] = 0; - new_termios.c_cc[VTIME] = 0; - - if (tcsetattr(STDIN_FILENO, TCSANOW, &new_termios) != 0) { - return 1; - } - - // Set stdin to non-blocking - int flags = fcntl(STDIN_FILENO, F_GETFL); - if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) { - tcsetattr(STDIN_FILENO, TCSANOW, original_termios); - return 1; - } - - return 0; -} - -void restore_terminal(struct termios* original_termios) { - tcsetattr(STDIN_FILENO, TCSANOW, original_termios); - - // Reset stdin to blocking - int flags = fcntl(STDIN_FILENO, F_GETFL); - fcntl(STDIN_FILENO, F_SETFL, flags & ~O_NONBLOCK); -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// UNIVERSAL CORE FUNCTIONS FOR CODE CONSOLIDATION -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -// Universal XOR operation - handles both encryption and decryption -// Since XOR is symmetric, this single function replaces all 6 duplicate XOR loops -// MOVED TO src/crypto.c - commented out here -/* -int universal_xor_operation(const unsigned char* data, size_t data_len, - const unsigned char* pad_data, unsigned char* result) { - if (!data || !pad_data || !result) { - return 1; // Error: null pointer - } - - for (size_t i = 0; i < data_len; i++) { - result[i] = data[i] ^ pad_data[i]; - } - - return 0; // Success -} -*/ - -// Universal ASCII message parser - consolidates 4 duplicate parsing implementations -// Extracts checksum, offset, and base64 data from ASCII armored messages -int parse_ascii_message(const char* message, char* chksum, uint64_t* offset, char* base64_data) { - if (!message || !chksum || !offset || !base64_data) { - return 1; // Error: null pointer - } - - char *message_copy = strdup(message); - if (!message_copy) { - return 1; // Memory allocation failed - } - - char *line_ptr = strtok(message_copy, "\n"); - int found_begin = 0; - int in_data_section = 0; - int found_chksum = 0, found_offset = 0; - - // Initialize output - chksum[0] = '\0'; - *offset = 0; - base64_data[0] = '\0'; - - while (line_ptr != NULL) { - if (strcmp(line_ptr, "-----BEGIN OTP MESSAGE-----") == 0) { - found_begin = 1; - } - else if (strcmp(line_ptr, "-----END OTP MESSAGE-----") == 0) { - break; - } - else if (found_begin) { - if (strncmp(line_ptr, "Pad-ChkSum: ", 12) == 0) { - strncpy(chksum, line_ptr + 12, 64); - chksum[64] = '\0'; - found_chksum = 1; - } - else if (strncmp(line_ptr, "Pad-Offset: ", 12) == 0) { - *offset = strtoull(line_ptr + 12, NULL, 10); - found_offset = 1; - } - else if (strlen(line_ptr) == 0) { - in_data_section = 1; - } - else if (in_data_section) { - strncat(base64_data, line_ptr, MAX_INPUT_SIZE * 2 - strlen(base64_data) - 1); - } - else if (strncmp(line_ptr, "Version:", 8) != 0 && strncmp(line_ptr, "Pad-", 4) != 0) { - // This might be base64 data without a blank line separator - strncat(base64_data, line_ptr, MAX_INPUT_SIZE * 2 - strlen(base64_data) - 1); - } - } - line_ptr = strtok(NULL, "\n"); - } - - free(message_copy); - - if (!found_begin || !found_chksum || !found_offset) { - return 2; // Error: incomplete message format - } - - return 0; // Success -} - -// Universal pad data loader - consolidates pad loading and validation logic -// Loads pad data at specified offset and validates pad availability -int load_pad_data(const char* pad_chksum, uint64_t offset, size_t length, unsigned char** pad_data) { - if (!pad_chksum || !pad_data) { - return 1; // Error: null pointer - } - - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Check if pad file exists - if (access(pad_path, R_OK) != 0) { - return 2; // Error: pad file not found - } - - // Check pad file size - struct stat pad_stat; - if (stat(pad_path, &pad_stat) != 0) { - return 3; // Error: cannot get pad file size - } - - if (offset + length > (uint64_t)pad_stat.st_size) { - return 4; // Error: not enough pad space - } - - // Allocate memory for pad data - *pad_data = malloc(length); - if (!*pad_data) { - return 5; // Error: memory allocation failed - } - - // Open and read pad file - FILE* pad_file = fopen(pad_path, "rb"); - if (!pad_file) { - free(*pad_data); - *pad_data = NULL; - return 6; // Error: cannot open pad file - } - - if (fseek(pad_file, offset, SEEK_SET) != 0) { - fclose(pad_file); - free(*pad_data); - *pad_data = NULL; - return 7; // Error: cannot seek to offset - } - - if (fread(*pad_data, 1, length, pad_file) != length) { - fclose(pad_file); - free(*pad_data); - *pad_data = NULL; - return 8; // Error: cannot read pad data - } - - fclose(pad_file); - return 0; // Success -} - -// Universal ASCII armor generator - consolidates duplicate ASCII armor generation -// Creates ASCII armored output format used by both text and file encryption -int generate_ascii_armor(const char* chksum, uint64_t offset, const unsigned char* encrypted_data, - size_t data_length, char** ascii_output) { - if (!chksum || !encrypted_data || !ascii_output) { - return 1; // Error: null pointer - } - - // Encode data as base64 - char* base64_data = custom_base64_encode(encrypted_data, data_length); - if (!base64_data) { - return 2; // Error: base64 encoding failed - } - - // Calculate required buffer size - size_t base64_len = strlen(base64_data); - size_t header_size = 200; // Approximate size for headers - size_t total_size = header_size + base64_len + (base64_len / 64) + 100; // +newlines +footer - - *ascii_output = malloc(total_size); - if (!*ascii_output) { - free(base64_data); - return 3; // Error: memory allocation failed - } - - // Build ASCII armor - strcpy(*ascii_output, "-----BEGIN OTP MESSAGE-----\n"); - - char temp_line[256]; - snprintf(temp_line, sizeof(temp_line), "Version: v0.3.16\n"); - strcat(*ascii_output, temp_line); - - snprintf(temp_line, sizeof(temp_line), "Pad-ChkSum: %s\n", chksum); - strcat(*ascii_output, temp_line); - - snprintf(temp_line, sizeof(temp_line), "Pad-Offset: %lu\n", offset); - strcat(*ascii_output, temp_line); - - strcat(*ascii_output, "\n"); - - // Add base64 data in 64-character lines - int b64_len = strlen(base64_data); - for (int i = 0; i < b64_len; i += 64) { - char line[70]; - snprintf(line, sizeof(line), "%.64s\n", base64_data + i); - strcat(*ascii_output, line); - } - - strcat(*ascii_output, "-----END OTP MESSAGE-----\n"); - - free(base64_data); - return 0; // Success -} - -// Universal pad integrity validator - consolidates pad validation logic -// Verifies pad checksum matches expected value for security -int validate_pad_integrity(const char* pad_path, const char* expected_chksum) { - if (!pad_path || !expected_chksum) { - return 1; // Error: null pointer - } - - char current_chksum[MAX_HASH_LENGTH]; - if (calculate_checksum(pad_path, current_chksum) != 0) { - return 2; // Error: cannot calculate checksum - } - - if (strcmp(expected_chksum, current_chksum) != 0) { - return 3; // Error: checksum mismatch - } - - return 0; // Success - pad integrity verified -} - -// Universal decrypt function - consolidates all decrypt operations -// input_data: encrypted message text or file path -// output_target: output file path (NULL for stdout/interactive) -// mode: determines behavior and output format -int universal_decrypt(const char* input_data, const char* output_target, decrypt_mode_t mode) { - char stored_chksum[MAX_HASH_LENGTH]; - uint64_t pad_offset; - char base64_data[MAX_INPUT_SIZE * 8] = {0}; - unsigned char* ciphertext = NULL; - int ciphertext_len; - - // Handle input based on mode - if (mode == DECRYPT_MODE_FILE_TO_TEXT || mode == DECRYPT_MODE_FILE_TO_FILE) { - // File input - read the entire file - FILE* input_fp = fopen(input_data, "r"); - if (!input_fp) { - printf("Error: Cannot open input file %s\n", input_data); - return 1; - } - - fseek(input_fp, 0, SEEK_END); - long file_size = ftell(input_fp); - fseek(input_fp, 0, SEEK_SET); - - char* file_content = malloc(file_size + 1); - if (!file_content) { - printf("Error: Memory allocation failed\n"); - fclose(input_fp); - return 1; - } - - size_t bytes_read = fread(file_content, 1, file_size, input_fp); - file_content[bytes_read] = '\0'; - fclose(input_fp); - - // Parse ASCII message from file content - if (parse_ascii_message(file_content, stored_chksum, &pad_offset, base64_data) != 0) { - printf("Error: Invalid ASCII armored format in file\n"); - free(file_content); - return 1; - } - free(file_content); - - if (mode == DECRYPT_MODE_FILE_TO_TEXT) { - printf("Decrypting ASCII armored file...\n"); - } - // Note: DECRYPT_MODE_FILE_TO_FILE should be completely silent for piping - } else { - // Text input (interactive or piped) - const char* message_text; - char full_message[MAX_INPUT_SIZE * 4] = {0}; - - if (input_data != NULL) { - message_text = input_data; - } else { - // Interactive mode - read from stdin - if (mode == DECRYPT_MODE_INTERACTIVE) { - printf("Enter encrypted message (paste the full ASCII armor block):\n"); - } - - char line[MAX_LINE_LENGTH]; - while (fgets(line, sizeof(line), stdin)) { - strncat(full_message, line, sizeof(full_message) - strlen(full_message) - 1); - if (strstr(line, "-----END OTP MESSAGE-----")) { - break; - } - } - message_text = full_message; - } - - // Parse ASCII message from text - if (parse_ascii_message(message_text, stored_chksum, &pad_offset, base64_data) != 0) { - if (mode == DECRYPT_MODE_SILENT) { - fprintf(stderr, "Error: Invalid message format - missing BEGIN header\n"); - } else { - printf("Error: Invalid message format - missing BEGIN header\n"); - } - return 1; - } - } - - // Get pad path and check existence - char pad_path[MAX_HASH_LENGTH + 20]; - char state_path[MAX_HASH_LENGTH + 20]; - get_pad_path(stored_chksum, pad_path, state_path); - - if (access(pad_path, R_OK) != 0) { - if (mode == DECRYPT_MODE_SILENT) { - fprintf(stderr, "Error: Required pad not found: %s\n", stored_chksum); - } else { - printf("Error: Required pad not found: %s\n", stored_chksum); - if (mode == DECRYPT_MODE_INTERACTIVE || mode == DECRYPT_MODE_FILE_TO_TEXT) { - printf("Available pads:\n"); - char* selected = select_pad_interactive("Available pads:", "Available pads (press Enter to continue)", PAD_FILTER_ALL, 0); - if (selected) { - free(selected); - } - } - } - return 1; - } - - // Validate pad integrity - int integrity_result = validate_pad_integrity(pad_path, stored_chksum); - if (integrity_result == 3) { - if (mode == DECRYPT_MODE_SILENT) { - fprintf(stderr, "Error: Pad integrity check failed!\n"); - return 1; - } else if (mode == DECRYPT_MODE_INTERACTIVE) { - printf("Warning: Pad integrity check failed!\n"); - printf("Expected: %s\n", stored_chksum); - printf("Continue anyway? (y/N): "); - fflush(stdout); - - char response[10]; - if (fgets(response, sizeof(response), stdin) == NULL || - (response[0] != 'y' && response[0] != 'Y')) { - printf("Decryption aborted.\n"); - return 1; - } - } - } else if (integrity_result != 0) { - if (mode == DECRYPT_MODE_SILENT) { - fprintf(stderr, "Error: Cannot verify pad integrity\n"); - } else { - printf("Error: Cannot verify pad integrity\n"); - } - return 1; - } else { - if (mode == DECRYPT_MODE_INTERACTIVE || mode == DECRYPT_MODE_FILE_TO_TEXT) { - printf("Pad integrity: VERIFIED\n"); - } - } - - // Decode base64 ciphertext - ciphertext = custom_base64_decode(base64_data, &ciphertext_len); - if (!ciphertext) { - if (mode == DECRYPT_MODE_SILENT) { - fprintf(stderr, "Error: Invalid base64 data\n"); - } else { - printf("Error: Invalid base64 data\n"); - } - return 1; - } - - // Load pad data using universal function - unsigned char* pad_data; - if (load_pad_data(stored_chksum, pad_offset, ciphertext_len, &pad_data) != 0) { - if (mode == DECRYPT_MODE_SILENT) { - fprintf(stderr, "Error: Cannot load pad data\n"); - } else { - printf("Error: Cannot load pad data\n"); - } - free(ciphertext); - return 1; - } - - // Decrypt using universal XOR operation - if (universal_xor_operation(ciphertext, ciphertext_len, pad_data, ciphertext) != 0) { - if (mode == DECRYPT_MODE_SILENT) { - fprintf(stderr, "Error: Decryption operation failed\n"); - } else { - printf("Error: Decryption operation failed\n"); - } - free(ciphertext); - free(pad_data); - return 1; - } - - // Output based on mode - if (mode == DECRYPT_MODE_FILE_TO_FILE) { - // Write to output file - const char* output_file = output_target; - - // Generate default output filename if not provided - char default_output[512]; - if (output_file == NULL) { - strncpy(default_output, input_data, sizeof(default_output) - 1); - default_output[sizeof(default_output) - 1] = '\0'; - - char* ext = strstr(default_output, ".otp.asc"); - if (ext) { - *ext = '\0'; - } else { - strncat(default_output, ".decrypted", sizeof(default_output) - strlen(default_output) - 1); - } - output_file = default_output; - } - - FILE* output_fp = fopen(output_file, "wb"); - if (!output_fp) { - printf("Error: Cannot create output file %s\n", output_file); - free(ciphertext); - free(pad_data); - return 1; - } - - if (fwrite(ciphertext, 1, ciphertext_len, output_fp) != (size_t)ciphertext_len) { - printf("Error: Cannot write decrypted data\n"); - free(ciphertext); - free(pad_data); - fclose(output_fp); - return 1; - } - fclose(output_fp); - - // Only show success messages in non-silent modes - if (mode != DECRYPT_MODE_FILE_TO_FILE) { - printf("File decrypted successfully: %s\n", output_file); - printf("Note: ASCII format does not preserve original filename/permissions\n"); - } - } else { - // Text output to stdout - need to allocate space for null terminator - char* decrypted_text = malloc(ciphertext_len + 1); - if (!decrypted_text) { - printf("Error: Memory allocation failed for output\n"); - free(ciphertext); - free(pad_data); - return 1; - } - - memcpy(decrypted_text, ciphertext, ciphertext_len); - decrypted_text[ciphertext_len] = '\0'; - - if (mode == DECRYPT_MODE_SILENT) { - // Silent mode - just output the text - printf("%s\n", decrypted_text); - fflush(stdout); - } else { - // Interactive mode - with label - printf("Decrypted: %s\n", decrypted_text); - } - - free(decrypted_text); - } - - // Cleanup - free(ciphertext); - free(pad_data); - - return 0; -} - -// Enhanced input function with editable pre-filled text -int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size) { - // Find the last directory separator - char* last_slash = strrchr(default_path, '/'); - char directory[1024] = ""; - char filename[512] = ""; - - if (last_slash) { - // Extract directory path - size_t dir_len = last_slash - default_path + 1; // Include the trailing slash - if (dir_len < sizeof(directory)) { - strncpy(directory, default_path, dir_len); - directory[dir_len] = '\0'; - } - - // Extract filename - strncpy(filename, last_slash + 1, sizeof(filename) - 1); - filename[sizeof(filename) - 1] = '\0'; - } else { - // No directory separator, treat as filename only - strncpy(filename, default_path, sizeof(filename) - 1); - filename[sizeof(filename) - 1] = '\0'; - } - - // Setup terminal for raw input - struct termios orig_termios; - if (tcgetattr(STDIN_FILENO, &orig_termios) != 0) { - // Fallback to simple input if terminal control fails - printf("\n%s\n%s: ", prompt, directory); - fflush(stdout); - - char input_buffer[512]; - if (!fgets(input_buffer, sizeof(input_buffer), stdin)) { - return 1; - } - input_buffer[strcspn(input_buffer, "\n")] = 0; - - if (strlen(input_buffer) == 0) { - strncpy(result, default_path, result_size - 1); - } else { - if (strlen(directory) > 0) { - snprintf(result, result_size, "%s%s", directory, input_buffer); - } else { - strncpy(result, input_buffer, result_size - 1); - } - } - result[result_size - 1] = '\0'; - return 0; - } - - // Set up raw terminal mode - struct termios raw_termios = orig_termios; - raw_termios.c_lflag &= ~(ECHO | ICANON); - raw_termios.c_cc[VMIN] = 1; - raw_termios.c_cc[VTIME] = 0; - - if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_termios) != 0) { - // Fallback if terminal setup fails - printf("\n%s\n%s: ", prompt, directory); - fflush(stdout); - - char input_buffer[512]; - if (!fgets(input_buffer, sizeof(input_buffer), stdin)) { - return 1; - } - input_buffer[strcspn(input_buffer, "\n")] = 0; - - if (strlen(input_buffer) == 0) { - strncpy(result, default_path, result_size - 1); - } else { - if (strlen(directory) > 0) { - snprintf(result, result_size, "%s%s", directory, input_buffer); - } else { - strncpy(result, input_buffer, result_size - 1); - } - } - result[result_size - 1] = '\0'; - return 0; - } - - // Display prompt and directory - printf("\n%s\n%s", prompt, directory); - fflush(stdout); - - // Initialize editing buffer with default filename - char edit_buffer[512]; - strncpy(edit_buffer, filename, sizeof(edit_buffer) - 1); - edit_buffer[sizeof(edit_buffer) - 1] = '\0'; - int cursor_pos = strlen(edit_buffer); - int buffer_len = cursor_pos; - - // Display initial filename - printf("%s", edit_buffer); - fflush(stdout); - - // Main editing loop - int c; - while ((c = getchar()) != EOF) { - if (c == '\n' || c == '\r') { - // Enter key - accept input - break; - } else if (c == 127 || c == 8) { - // Backspace/Delete - if (cursor_pos > 0) { - // Move everything after cursor one position left - memmove(&edit_buffer[cursor_pos - 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1); - cursor_pos--; - buffer_len--; - - // Redraw line: move cursor to start of filename area, clear to end, redraw buffer - printf("\r%s\033[K%s", directory, edit_buffer); - - // Position cursor correctly - if (cursor_pos < buffer_len) { - printf("\033[%dD", buffer_len - cursor_pos); - } - fflush(stdout); - } - } else if (c == 27) { - // Escape sequence (arrow keys, etc.) - int c1 = getchar(); - int c2 = getchar(); - if (c1 == '[') { - if (c2 == 'C' && cursor_pos < buffer_len) { - // Right arrow - cursor_pos++; - printf("\033[1C"); - fflush(stdout); - } else if (c2 == 'D' && cursor_pos > 0) { - // Left arrow - cursor_pos--; - printf("\033[1D"); - fflush(stdout); - } else if (c2 == 'H') { - // Home key - printf("\033[%dD", cursor_pos); - cursor_pos = 0; - fflush(stdout); - } else if (c2 == 'F') { - // End key - printf("\033[%dC", buffer_len - cursor_pos); - cursor_pos = buffer_len; - fflush(stdout); - } - } - } else if (c >= 32 && c <= 126) { - // Printable character - if (buffer_len < (int)sizeof(edit_buffer) - 1) { - // Move everything after cursor one position right - memmove(&edit_buffer[cursor_pos + 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1); - edit_buffer[cursor_pos] = c; - cursor_pos++; - buffer_len++; - - // Redraw from cursor position - printf("%c", c); - if (cursor_pos < buffer_len) { - // Print remaining characters and move cursor back - printf("%s\033[%dD", &edit_buffer[cursor_pos], buffer_len - cursor_pos); - } - fflush(stdout); - } - } - // Ignore other control characters - } - - // Restore terminal - tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); - printf("\n"); - - // Construct final result - if (buffer_len == 0) { - // Empty input, use default - strncpy(result, default_path, result_size - 1); - } else { - // Combine directory with edited filename - edit_buffer[buffer_len] = '\0'; - if (strlen(directory) > 0) { - snprintf(result, result_size, "%s%s", directory, edit_buffer); - } else { - strncpy(result, edit_buffer, result_size - 1); - } - } - result[result_size - 1] = '\0'; - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// EDITOR AND FILE MANAGER -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -char* get_preferred_editor(void) { - // Check EDITOR environment variable first - char* editor = getenv("EDITOR"); - if (editor && strlen(editor) > 0) { - // Verify the editor exists - char command[512]; - snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", editor); - if (system(command) == 0) { - return strdup(editor); - } - } - - // Check VISUAL environment variable - editor = getenv("VISUAL"); - if (editor && strlen(editor) > 0) { - char command[512]; - snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", editor); - if (system(command) == 0) { - return strdup(editor); - } - } - - // Try common editors in order of preference - const char* common_editors[] = {"vim", "vi", "nano", "emacs", "gedit", NULL}; - for (int i = 0; common_editors[i] != NULL; i++) { - char command[512]; - snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", common_editors[i]); - if (system(command) == 0) { - return strdup(common_editors[i]); - } - } - - return NULL; // No editor found -} - -char* get_preferred_file_manager(void) { - // Try file managers in order of preference - const char* file_managers[] = {"ranger", "fzf", "nnn", "lf", NULL}; - - for (int i = 0; file_managers[i] != NULL; i++) { - char command[512]; - snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", file_managers[i]); - if (system(command) == 0) { - return strdup(file_managers[i]); - } - } - - return NULL; // No file manager found -} - -int launch_text_editor(const char* initial_content, char* result_buffer, size_t buffer_size) { - char* editor = get_preferred_editor(); - if (!editor) { - printf("Error: No text editor found. Set EDITOR environment variable or install vim/nano.\n"); - return 1; - } - - // Create temporary file - char temp_filename[64]; - snprintf(temp_filename, sizeof(temp_filename), "/tmp/otp_edit_%ld.tmp", time(NULL)); - - // Write initial content to temp file if provided - if (initial_content && strlen(initial_content) > 0) { - FILE* temp_file = fopen(temp_filename, "w"); - if (temp_file) { - fputs(initial_content, temp_file); - fclose(temp_file); - } - } else { - // Create empty temp file - FILE* temp_file = fopen(temp_filename, "w"); - if (temp_file) { - fclose(temp_file); - } - } - - // Launch editor - printf("Opening %s for text editing...\n", editor); - char command[512]; - snprintf(command, sizeof(command), "%s %s", editor, temp_filename); - - int result = system(command); - - if (result == 0) { - // Read the edited content back - FILE* temp_file = fopen(temp_filename, "r"); - if (temp_file) { - size_t bytes_read = fread(result_buffer, 1, buffer_size - 1, temp_file); - result_buffer[bytes_read] = '\0'; - - // Remove trailing newline if present - if (bytes_read > 0 && result_buffer[bytes_read - 1] == '\n') { - result_buffer[bytes_read - 1] = '\0'; - } - - fclose(temp_file); - } else { - printf("Error: Cannot read edited content\n"); - free(editor); - unlink(temp_filename); - return 1; - } - } else { - printf("Editor exited with error or was cancelled\n"); - free(editor); - unlink(temp_filename); - return 1; - } - - // Clean up - unlink(temp_filename); - free(editor); - - return 0; -} - -int launch_file_manager(const char* start_directory, char* selected_file, size_t buffer_size) { - char* fm = get_preferred_file_manager(); - if (!fm) { - printf("No file manager found. Please install ranger, fzf, nnn, or lf.\n"); - printf("Falling back to manual file path entry.\n"); - return 1; // Fall back to manual entry - } - - char temp_filename[64]; - snprintf(temp_filename, sizeof(temp_filename), "/tmp/otp_file_%ld.tmp", time(NULL)); - - char command[512]; - int result = 1; - - printf("Opening %s for file selection...\n", fm); - - if (strcmp(fm, "ranger") == 0) { - snprintf(command, sizeof(command), "cd '%s' && ranger --choosefile=%s", - start_directory ? start_directory : ".", temp_filename); - } else if (strcmp(fm, "fzf") == 0) { - snprintf(command, sizeof(command), "cd '%s' && find . -type f | fzf > %s", - start_directory ? start_directory : ".", temp_filename); - } else if (strcmp(fm, "nnn") == 0) { - snprintf(command, sizeof(command), "cd '%s' && nnn -p %s", - start_directory ? start_directory : ".", temp_filename); - } else if (strcmp(fm, "lf") == 0) { - snprintf(command, sizeof(command), "cd '%s' && lf -selection-path=%s", - start_directory ? start_directory : ".", temp_filename); - } - - result = system(command); - - if (result == 0 || result == 256) { // Some file managers return 256 on success - // Read selected file from temp file - FILE* temp_file = fopen(temp_filename, "r"); - if (temp_file) { - if (fgets(selected_file, buffer_size, temp_file)) { - // Remove trailing newline - selected_file[strcspn(selected_file, "\n\r")] = 0; - - // For relative paths from fzf, make absolute if needed - if (selected_file[0] == '.' && selected_file[1] == '/') { - char current_dir[512]; - if (getcwd(current_dir, sizeof(current_dir))) { - char abs_path[1024]; - snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, selected_file + 2); - strncpy(selected_file, abs_path, buffer_size - 1); - selected_file[buffer_size - 1] = '\0'; - } - } - - fclose(temp_file); - unlink(temp_filename); - free(fm); - return 0; // Success - } - fclose(temp_file); - } - } - - // Clean up and indicate failure - unlink(temp_filename); - free(fm); - return 1; // Fall back to manual entry -} - -int handle_text_encrypt(void) { - printf("\n"); - print_centered_header("Text Encrypt", 0); - - // Launch text editor directly - char text_buffer[MAX_INPUT_SIZE]; - if (launch_text_editor(NULL, text_buffer, sizeof(text_buffer)) != 0) { - printf("Error: Could not launch text editor\n"); - return 1; - } - - if (strlen(text_buffer) == 0) { - printf("No text entered - canceling encryption\n"); - return 1; - } - - // Use unified pad selection - char* selected_pad = select_pad_interactive("Select Pad for Text Encryption", - "Select pad (by prefix)", - PAD_FILTER_ALL, 1); - if (!selected_pad) { - printf("Text encryption cancelled.\n"); - return 1; - } - - int result = encrypt_text(selected_pad, text_buffer); - free(selected_pad); - return result; -} - -int handle_file_encrypt(void) { - printf("\n"); - print_centered_header("File Encrypt", 0); - - // Launch file manager directly - char input_file[512]; - if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) { - printf("Error: Could not launch file manager\n"); - return 1; - } - - // Check if file exists - if (access(input_file, R_OK) != 0) { - printf("Error: File '%s' not found or cannot be read\n", input_file); - return 1; - } - - // Use unified pad selection - char* selected_pad = select_pad_interactive("Select Pad for File Encryption", - "Select pad (by prefix)", - PAD_FILTER_ALL, 1); - if (!selected_pad) { - printf("File encryption cancelled.\n"); - return 1; - } - - // Ask for output format - printf("\nSelect output format:\n"); - printf("1. Binary (.otp) - preserves file permissions\n"); - printf("2. ASCII (.otp.asc) - text-safe format\n"); - printf("Enter choice (1-2): "); - - char format_input[10]; - if (!fgets(format_input, sizeof(format_input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - int ascii_armor = (atoi(format_input) == 2) ? 1 : 0; - - // Generate default output filename - char default_output[1024]; // Increased buffer size to prevent truncation warnings - if (ascii_armor) { - snprintf(default_output, sizeof(default_output), "%s.otp.asc", input_file); - } else { - snprintf(default_output, sizeof(default_output), "%s.otp", input_file); - } - - // Use enhanced input function for output filename - char output_file[512]; - if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { - printf("Error: Failed to read input\n"); - return 1; - } - - const char* output_filename = output_file; - - int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor); - free(selected_pad); - return result; -} - - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// PADS -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -int calculate_checksum(const char* filename, char* checksum_hex) { - FILE* file = fopen(filename, "rb"); - if (!file) { - return 1; - } - - unsigned char checksum[32]; - unsigned char buffer[64 * 1024]; // 64KB buffer for large files - size_t bytes_read; - - // Initialize checksum - memset(checksum, 0, 32); - size_t total_bytes = 0; - - // Calculate XOR checksum of entire file - while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { - // Process this chunk with XOR checksum - for (size_t i = 0; i < bytes_read; i++) { - unsigned char bucket = (total_bytes + i) % 32; - checksum[bucket] ^= buffer[i] ^ (((total_bytes + i) >> 8) & 0xFF) ^ - (((total_bytes + i) >> 16) & 0xFF) ^ (((total_bytes + i) >> 24) & 0xFF); - } - total_bytes += bytes_read; - } - - fclose(file); - - // Now encrypt the checksum with the first 32 bytes of the pad - fseek(file = fopen(filename, "rb"), 0, SEEK_SET); - unsigned char pad_key[32]; - if (fread(pad_key, 1, 32, file) != 32) { - fclose(file); - return 1; - } - fclose(file); - - // XOR encrypt the checksum with pad data to create unique identifier - unsigned char encrypted_checksum[32]; - for (int i = 0; i < 32; i++) { - encrypted_checksum[i] = checksum[i] ^ pad_key[i]; - } - - // Convert to hex string (64 characters) - for (int i = 0; i < 32; i++) { - sprintf(checksum_hex + (i * 2), "%02x", encrypted_checksum[i]); - } - checksum_hex[64] = '\0'; - - return 0; -} - -// Calculate checksum with progress display for large files -int calculate_checksum_with_progress(const char* filename, char* checksum_hex, int display_progress, uint64_t file_size) { - FILE* file = fopen(filename, "rb"); - if (!file) { - return 1; - } - - unsigned char checksum[32]; - unsigned char buffer[64 * 1024]; // 64KB buffer for large files - size_t bytes_read; - - // Initialize checksum - memset(checksum, 0, 32); - size_t total_bytes = 0; - time_t start_time = time(NULL); - - // Calculate XOR checksum of entire file with progress - while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { - // Process this chunk with XOR checksum - for (size_t i = 0; i < bytes_read; i++) { - unsigned char bucket = (total_bytes + i) % 32; - checksum[bucket] ^= buffer[i] ^ (((total_bytes + i) >> 8) & 0xFF) ^ - (((total_bytes + i) >> 16) & 0xFF) ^ (((total_bytes + i) >> 24) & 0xFF); - } - total_bytes += bytes_read; - - // Show progress for large files (every 64MB or if display_progress is enabled) - if (display_progress && file_size > 10 * 1024 * 1024 && total_bytes % (64 * 1024 * 1024) == 0) { - show_progress(total_bytes, file_size, start_time); - } - } - - // Final progress update - if (display_progress && file_size > 10 * 1024 * 1024) { - show_progress(file_size, file_size, start_time); - printf("\n"); - } - - fclose(file); - - // Now encrypt the checksum with the first 32 bytes of the pad - fseek(file = fopen(filename, "rb"), 0, SEEK_SET); - unsigned char pad_key[32]; - if (fread(pad_key, 1, 32, file) != 32) { - fclose(file); - return 1; - } - fclose(file); - - // XOR encrypt the checksum with pad data to create unique identifier - unsigned char encrypted_checksum[32]; - for (int i = 0; i < 32; i++) { - encrypted_checksum[i] = checksum[i] ^ pad_key[i]; - } - - // Convert to hex string (64 characters) - for (int i = 0; i < 32; i++) { - sprintf(checksum_hex + (i * 2), "%02x", encrypted_checksum[i]); - } - checksum_hex[64] = '\0'; - - return 0; -} - -// Unified pad selection function - extracts the best UI from handle_pads_menu() -char* select_pad_interactive(const char* title, const char* prompt, pad_filter_type_t filter_type, int allow_cancel) { - // Get list of pads from current directory - DIR* dir = opendir(current_pads_dir); - if (!dir) { - printf("Error: Cannot open pads directory %s\n", current_pads_dir); - return NULL; - } - - // Structure to store pad information - struct PadInfo { - char chksum[65]; - char size_str[32]; - char used_str[32]; - double percentage; - char location[256]; - }; - - struct PadInfo pads[100]; // Support up to 100 pads - int pad_count = 0; - - // Collect all pad information - struct dirent* entry; - while ((entry = readdir(dir)) != NULL && pad_count < 100) { - if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { - strncpy(pads[pad_count].chksum, entry->d_name, 64); - pads[pad_count].chksum[64] = '\0'; - - // Get pad file size and usage info - char full_path[1024]; - snprintf(full_path, sizeof(full_path), "%s/%s", current_pads_dir, entry->d_name); - struct stat st; - if (stat(full_path, &st) == 0) { - // Get used bytes from state - uint64_t used_bytes; - read_state_offset(pads[pad_count].chksum, &used_bytes); - - // Apply filter - if (filter_type == PAD_FILTER_UNUSED_ONLY && used_bytes > 32) { - continue; // Skip used pads when filtering for unused only - } - - // Format total size - if (st.st_size < 1024) { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size); - } else if (st.st_size < 1024 * 1024) { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1024.0); - } else if (st.st_size < 1024 * 1024 * 1024) { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1024.0 * 1024.0)); - } else { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1024.0 * 1024.0 * 1024.0)); - } - - // Format used size - if (used_bytes < 1024) { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes); - } else if (used_bytes < 1024 * 1024) { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1024.0); - } else if (used_bytes < 1024 * 1024 * 1024) { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1024.0 * 1024.0)); - } else { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1024.0 * 1024.0 * 1024.0)); - } - - // Calculate percentage - pads[pad_count].percentage = (double)used_bytes / st.st_size * 100.0; - - // Set location info using directory display - get_directory_display(full_path, pads[pad_count].location, sizeof(pads[pad_count].location)); - - pad_count++; - } - } - } - closedir(dir); - - if (pad_count == 0) { - printf("\n%s\n", title); - if (filter_type == PAD_FILTER_UNUSED_ONLY) { - printf("No unused pads found.\n"); - printf("Entropy can only be added to pads with 0%% usage (only reserved bytes used).\n"); - } else { - printf("No pads found.\n"); - } - return NULL; - } - - // Calculate minimal unique prefixes for each pad - char prefixes[100][65]; - int prefix_lengths[100]; - - for (int i = 0; i < pad_count; i++) { - prefix_lengths[i] = 1; - - // Find minimal unique prefix - while (prefix_lengths[i] <= 64) { - int unique = 1; - - // Check if current prefix is unique among all other pads - for (int j = 0; j < pad_count; j++) { - if (i != j && strncmp(pads[i].chksum, pads[j].chksum, prefix_lengths[i]) == 0) { - unique = 0; - break; - } - } - - if (unique) { - break; - } - prefix_lengths[i]++; - } - - // Store the minimal prefix - strncpy(prefixes[i], pads[i].chksum, prefix_lengths[i]); - prefixes[i][prefix_lengths[i]] = '\0'; - } - - // Display title and pads table - printf("\n%s\n", title); - printf("%-8s %-2s %-12s %-12s %-12s %-8s\n", "ChkSum", "D", "Dir", "Size", "Used", "% Used"); - printf("%-8s %-2s %-12s %-12s %-12s %-8s\n", "--------", "--", "------------", "----------", "----------", "------"); - - // Get current default pad path for comparison - char* current_default = get_default_pad_path(); - char default_pad_checksum[65] = ""; - - if (current_default) { - // Extract checksum from default pad path - char* filename = strrchr(current_default, '/'); - if (!filename) filename = current_default; - else filename++; // Skip the '/' - - // Extract checksum (remove .pad extension) - if (strlen(filename) >= 68 && strstr(filename, ".pad")) { - strncpy(default_pad_checksum, filename, 64); - default_pad_checksum[64] = '\0'; - } - free(current_default); - } - - for (int i = 0; i < pad_count; i++) { - // Check if this is the default pad - int is_default = (strlen(default_pad_checksum) > 0 && - strncmp(pads[i].chksum, default_pad_checksum, 64) == 0); - - // Display first 8 characters of checksum with prefix underlined - char checksum_8char[9]; - strncpy(checksum_8char, pads[i].chksum, 8); - checksum_8char[8] = '\0'; - - printf("\033[4m%.*s\033[0m%s %-2s %-12s %-12s %-12s %.1f%%\n", - prefix_lengths[i], checksum_8char, // Underlined prefix - checksum_8char + prefix_lengths[i], // Rest of 8-char checksum - is_default ? "*" : "", // Default indicator - pads[i].location, - pads[i].size_str, - pads[i].used_str, - pads[i].percentage); - } - - // Display prompt - printf("\n%s", prompt); - if (allow_cancel) { - printf(" (or 'x' to cancel)"); - } - printf(": "); - - char input[MAX_HASH_LENGTH]; - if (!fgets(input, sizeof(input), stdin)) { - printf("Error: Failed to read input\n"); - return NULL; - } - input[strcspn(input, "\n")] = 0; - - // Handle empty input - select default pad if available - if (strlen(input) == 0) { - // Get current default pad path - char* current_default = get_default_pad_path(); - if (current_default) { - // Extract checksum from default pad path - char* filename = strrchr(current_default, '/'); - if (!filename) filename = current_default; - else filename++; // Skip the '/' - - // Extract checksum (remove .pad extension) - if (strlen(filename) >= 68 && strstr(filename, ".pad")) { - char default_checksum[65]; - strncpy(default_checksum, filename, 64); - default_checksum[64] = '\0'; - - // Verify this default pad is in our current list - for (int i = 0; i < pad_count; i++) { - if (strncmp(pads[i].chksum, default_checksum, 64) == 0) { - free(current_default); - printf("Selected default pad: %.16s...\n\n", default_checksum); - return strdup(default_checksum); - } - } - } - free(current_default); - } - // No default pad or default pad not in current list - printf("No default pad available or default pad not in current list\n"); - return NULL; - } - - // Handle cancel - if (allow_cancel && (toupper(input[0]) == 'X' && strlen(input) == 1)) { - return NULL; - } - - // Find matching pad by prefix only - int selected_pad = -1; - int match_count = 0; - - // Try prefix matching only - for (int i = 0; i < pad_count; i++) { - if (strncmp(input, pads[i].chksum, strlen(input)) == 0) { - if (match_count == 0) { - selected_pad = i; - } - match_count++; - } - } - - if (match_count == 0) { - printf("No pad found matching '%s'\n", input); - return NULL; - } else if (match_count > 1) { - printf("Ambiguous prefix. Multiple matches found.\n"); - return NULL; - } - - // Return selected pad checksum (caller must free) - return strdup(pads[selected_pad].chksum); -} - -int handle_pads_menu(void) { - printf("\n"); - print_centered_header("Pad Management", 0); - - // Get list of pads from current directory - DIR* dir = opendir(current_pads_dir); - if (!dir) { - printf("Error: Cannot open pads directory %s\n", current_pads_dir); - return 1; - } - - // Structure to store pad information - struct PadInfo { - char chksum[65]; - char size_str[32]; - char used_str[32]; - double percentage; - char location[256]; // Store location info - }; - - struct PadInfo pads[100]; // Support up to 100 pads - int pad_count = 0; - - // Collect all pad information - struct dirent* entry; - while ((entry = readdir(dir)) != NULL && pad_count < 100) { - if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { - strncpy(pads[pad_count].chksum, entry->d_name, 64); - pads[pad_count].chksum[64] = '\0'; - - // Get pad file size and usage info - char full_path[1024]; // Increased buffer size - snprintf(full_path, sizeof(full_path), "%s/%s", current_pads_dir, entry->d_name); - struct stat st; - if (stat(full_path, &st) == 0) { - // Get used bytes from state - uint64_t used_bytes; - read_state_offset(pads[pad_count].chksum, &used_bytes); - - // Format total size - if (st.st_size < 1024) { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size); - } else if (st.st_size < 1024 * 1024) { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1024.0); - } else if (st.st_size < 1024 * 1024 * 1024) { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1024.0 * 1024.0)); - } else { - snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1024.0 * 1024.0 * 1024.0)); - } - - // Format used size - if (used_bytes < 1024) { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes); - } else if (used_bytes < 1024 * 1024) { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1024.0); - } else if (used_bytes < 1024 * 1024 * 1024) { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1024.0 * 1024.0)); - } else { - snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1024.0 * 1024.0 * 1024.0)); - } - - // Calculate percentage - pads[pad_count].percentage = (double)used_bytes / st.st_size * 100.0; - - // Set location info using directory display - get_directory_display(full_path, pads[pad_count].location, sizeof(pads[pad_count].location)); - - pad_count++; - } - } - } - closedir(dir); - - if (pad_count == 0) { - printf("No pads found.\n"); - printf("\nOptions:\n"); - printf(" \033[4mG\033[0menerate new pad\n"); - printf(" E\033[4mx\033[0mit\n"); - printf("\nSelect option: "); - - char input[10]; - if (fgets(input, sizeof(input), stdin)) { - char choice = toupper(input[0]); - if (choice == 'G') { - int result = handle_generate_menu(); - if (result == 0) { - // After successful pad generation, return to pads menu - return handle_pads_menu(); - } - return result; - } - } - return 0; - } - - // Calculate minimal unique prefixes for each pad - char prefixes[100][65]; // Store the minimal prefix for each pad - int prefix_lengths[100]; // Length of minimal prefix for each pad - - for (int i = 0; i < pad_count; i++) { - prefix_lengths[i] = 1; - - // Find minimal unique prefix - while (prefix_lengths[i] <= 64) { - int unique = 1; - - // Check if current prefix is unique among all other pads - for (int j = 0; j < pad_count; j++) { - if (i != j && strncmp(pads[i].chksum, pads[j].chksum, prefix_lengths[i]) == 0) { - unique = 0; - break; - } - } - - if (unique) { - break; - } - prefix_lengths[i]++; - } - - // Store the minimal prefix - strncpy(prefixes[i], pads[i].chksum, prefix_lengths[i]); - prefixes[i][prefix_lengths[i]] = '\0'; - } - - // Display pads with minimal prefixes underlined and default indicator - printf("\nAvailable pads:\n"); - printf("%-8s %-2s %-12s %-12s %-12s %-8s\n", "ChkSum", "D", "Dir", "Size", "Used", "% Used"); - printf("%-8s %-2s %-12s %-12s %-12s %-8s\n", "--------", "--", "------------", "----------", "----------", "------"); - - // Get current default pad path for comparison - char* current_default = get_default_pad_path(); - char default_pad_checksum[65] = ""; - - if (current_default) { - // Extract checksum from default pad path - char* filename = strrchr(current_default, '/'); - if (!filename) filename = current_default; - else filename++; // Skip the '/' - - // Extract checksum (remove .pad extension) - if (strlen(filename) >= 68 && strstr(filename, ".pad")) { - strncpy(default_pad_checksum, filename, 64); - default_pad_checksum[64] = '\0'; - } - free(current_default); - } - - for (int i = 0; i < pad_count; i++) { - // Check if this is the default pad - int is_default = (strlen(default_pad_checksum) > 0 && - strncmp(pads[i].chksum, default_pad_checksum, 64) == 0); - - // Display first 8 characters of checksum with prefix underlined - char checksum_8char[9]; - strncpy(checksum_8char, pads[i].chksum, 8); - checksum_8char[8] = '\0'; - - printf("\033[4m%.*s\033[0m%s %-2s %-12s %-12s %-12s %.1f%%\n", - prefix_lengths[i], checksum_8char, // Underlined prefix - checksum_8char + prefix_lengths[i], // Rest of 8-char checksum - is_default ? "*" : "", // Default indicator - pads[i].location, // Use the stored location info - pads[i].size_str, - pads[i].used_str, - pads[i].percentage); - } - - printf("\nActions:\n"); - printf(" \033[4mG\033[0menerate new pad\n"); - printf(" \033[4mA\033[0mdd entropy to pad\n"); - printf(" \033[4mV\033[0merify pad integrity\n"); - printf(" \033[4mD\033[0melete pad\n"); - printf(" \033[4mS\033[0met default pad\n"); - printf(" E\033[4mx\033[0mit\n"); - printf("\nSelect action: "); - - char input[MAX_HASH_LENGTH]; - if (!fgets(input, sizeof(input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - input[strcspn(input, "\n")] = 0; - - // Handle actions - if (toupper(input[0]) == 'G') { - int result = handle_generate_menu(); - if (result == 0) { - // After successful pad generation, return to pads menu - return handle_pads_menu(); - } - return result; - } else if (toupper(input[0]) == 'A') { - // Add entropy to pad - use unified function with unused pads filter - char* selected_pad = select_pad_interactive("Select Unused Pad for Entropy Addition", - "Select unused pad (by prefix)", - PAD_FILTER_UNUSED_ONLY, 1); - if (!selected_pad) { - printf("Entropy addition cancelled.\n"); - return handle_pads_menu(); - } - - // Add entropy to the selected unused pad - int result = handle_add_entropy_to_pad(selected_pad); - free(selected_pad); - if (result == 0) { - // Return to pads menu after successful entropy addition - return handle_pads_menu(); - } - return result; - } else if (toupper(input[0]) == 'V') { - // Verify pad integrity - use unified function - char* selected_pad = select_pad_interactive("Select Pad for Verification", - "Select pad to verify (by prefix)", - PAD_FILTER_ALL, 1); - if (!selected_pad) { - printf("Pad verification cancelled.\n"); - return handle_pads_menu(); - } - - // Verify the selected pad - handle_verify_pad(selected_pad); - free(selected_pad); - return handle_pads_menu(); // Always return to pads menu after verification - } else if (toupper(input[0]) == 'D') { - // Delete pad - use unified function - char* selected_pad = select_pad_interactive("Select Pad for Deletion", - "Select pad to delete (by prefix)", - PAD_FILTER_ALL, 1); - if (!selected_pad) { - printf("Pad deletion cancelled.\n"); - return handle_pads_menu(); - } - - // Delete the selected pad - handle_delete_pad(selected_pad); - free(selected_pad); - return handle_pads_menu(); // Always return to pads menu after deletion attempt - } else if (toupper(input[0]) == 'S') { - // Set default pad - use unified function - char* selected_pad = select_pad_interactive("Select Default Pad", - "Select pad to set as default (by prefix)", - PAD_FILTER_ALL, 1); - if (!selected_pad) { - printf("Default pad selection cancelled.\n"); - return handle_pads_menu(); - } - - // Construct the full absolute pad path and set as default - char new_default_path[1024]; - if (current_pads_dir[0] == '/') { - // Already absolute path - int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, selected_pad); - if (ret >= (int)sizeof(new_default_path)) { - printf("Error: Path too long for default pad setting\n"); - free(selected_pad); - return handle_pads_menu(); - } - } else { - // Relative path - make it absolute - char current_dir[512]; - if (getcwd(current_dir, sizeof(current_dir))) { - int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s/%s.pad", current_dir, current_pads_dir, selected_pad); - if (ret >= (int)sizeof(new_default_path)) { - // Path was truncated, fall back to relative path - int ret2 = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, selected_pad); - if (ret2 >= (int)sizeof(new_default_path)) { - printf("Error: Path too long for default pad setting\n"); - free(selected_pad); - return handle_pads_menu(); - } - } - } else { - // Fallback to relative path - int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, selected_pad); - if (ret >= (int)sizeof(new_default_path)) { - printf("Error: Path too long for default pad setting\n"); - free(selected_pad); - return handle_pads_menu(); - } - } - } - - if (set_default_pad_path(new_default_path) == 0) { - printf("Default pad set to: %.16s...\n", selected_pad); - printf("Full path: %s\n", new_default_path); - } else { - printf("Error: Failed to update default pad preference\n"); - } - - free(selected_pad); - return handle_pads_menu(); - } else if (toupper(input[0]) == 'X') { - return 0; // Exit to main menu - } else { - printf("Invalid action. Please select G, A, S, or X.\n"); - return handle_pads_menu(); - } -} - -int handle_add_entropy_to_pad(const char* pad_chksum) { - char header_text[128]; - snprintf(header_text, sizeof(header_text), "Add Entropy to Pad: %.16s...", pad_chksum); - printf("\n"); - print_centered_header(header_text, 0); - - // Present entropy source selection menu with consistent formatting - printf("Select entropy source:\n"); - printf(" \033[4mK\033[0meyboard entropy - Random typing for entropy collection\n"); - printf(" \033[4mD\033[0mice/Coins/Cards - Manual input for high-quality entropy\n"); - printf(" \033[4mH\033[0mardware RNG - Hardware random number generators\n"); - printf(" \033[4mF\033[0mile - Load entropy from binary file\n"); - printf(" \033[4mT\033[0mest RNG Speed - Test TrueRNG/SwiftRNG device performance\n"); - printf(" E\033[4mx\033[0mit\n"); - printf("\nSelect option: "); - - char source_input[10]; - if (!fgets(source_input, sizeof(source_input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - char choice = toupper(source_input[0]); - entropy_source_t entropy_source; - - switch (choice) { - case 'K': - entropy_source = ENTROPY_SOURCE_KEYBOARD; - break; - case 'D': - entropy_source = ENTROPY_SOURCE_DICE; - break; - case 'H': - // Hardware RNG selected - will handle after target_bytes selection - entropy_source = ENTROPY_SOURCE_TRUERNG; - break; - case 'F': - entropy_source = ENTROPY_SOURCE_FILE; - break; - case 'T': - // Test RNG speed - this doesn't collect entropy, just tests the device - return test_truerng_speed(); - case 'X': - return 0; // Exit - default: - printf("Invalid choice. Please select K, D, H, F, T, or X.\n"); - return 1; - } - - size_t target_bytes; - - // For TrueRNG, automatically use the full pad size - if (entropy_source == ENTROPY_SOURCE_TRUERNG) { - // Get the pad file size - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - struct stat pad_stat; - if (stat(pad_path, &pad_stat) != 0) { - printf("Error: Cannot get pad file size\n"); - return 1; - } - - target_bytes = (size_t)pad_stat.st_size; - printf("\nTrueRNG selected - will enhance entire pad with hardware entropy\n"); - printf("Pad size: %.2f GB (%zu bytes)\n", - (double)target_bytes / (1024.0 * 1024.0 * 1024.0), target_bytes); - } else { - // For other entropy sources, show the selection menu - printf("\nEntropy collection options:\n"); - printf(" 1. Recommended (2048 bytes) - Optimal security\n"); - printf(" 2. Minimum (1024 bytes) - Good security\n"); - printf(" 3. Maximum (4096 bytes) - Maximum security\n"); - printf(" 4. Custom amount\n"); - printf("Enter choice (1-4): "); - - char amount_input[10]; - if (!fgets(amount_input, sizeof(amount_input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - target_bytes = 2048; // Default - int amount_choice = atoi(amount_input); - - switch (amount_choice) { - case 1: - target_bytes = 2048; - break; - case 2: - target_bytes = 1024; - break; - case 3: - target_bytes = 4096; - break; - case 4: - printf("Enter custom amount (512-8192 bytes): "); - char custom_input[32]; - if (!fgets(custom_input, sizeof(custom_input), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - size_t custom_amount = (size_t)atoi(custom_input); - if (custom_amount < 512 || custom_amount > 8192) { - printf("Error: Invalid amount. Must be between 512 and 8192 bytes.\n"); - return 1; - } - target_bytes = custom_amount; - break; - default: - target_bytes = 2048; // Default to recommended - break; - } - } - - // For TrueRNG, detect all devices and present selection menu - hardware_rng_device_t selected_device; - if (entropy_source == ENTROPY_SOURCE_TRUERNG) { - hardware_rng_device_t devices[10]; - int num_devices_found = 0; - - if (detect_all_hardware_rng_devices(devices, 10, &num_devices_found) != 0) { - printf("Error: Failed to detect hardware RNG devices\n"); - return 1; - } - - if (num_devices_found == 0) { - printf("No hardware RNG devices found.\n"); - printf("\nSupported devices:\n"); - printf(" - TrueRNG/SwiftRNG (PID: %s, VID: %s)\n", TRUERNG_VID, TRUERNG_PID); - printf(" - TrueRNGpro/SwiftRNGpro (PID: %s, VID: %s)\n", TRUERNGPRO_VID, TRUERNGPRO_PID); - printf(" - TrueRNGproV2/SwiftRNGproV2 (PID: %s, VID: %s)\n", TRUERNGPROV2_VID, TRUERNGPROV2_PID); - printf("\nPlease connect a TrueRNG or SwiftRNG device and try again.\n"); - return 1; - } - - // Present device selection menu - if (select_hardware_rng_device_interactive(devices, num_devices_found, &selected_device) != 0) { - printf("Device selection cancelled or failed.\n"); - return 1; - } - } - - // For TrueRNG with large pads, use streaming approach - if (entropy_source == ENTROPY_SOURCE_TRUERNG && target_bytes > MAX_ENTROPY_BUFFER) { - printf("\nUsing streaming approach for large pad enhancement...\n"); - - // Prompt user for entropy distribution mode - printf("Choose entropy distribution method:\n"); - printf("0) Direct XOR (recommended for high entropy)\n"); - printf("1) ChaCha20 distribution (recommended for low entropy)\n"); - printf("Enter choice (0 or 1): "); - fflush(stdout); - - char mode_input[10]; - if (fgets(mode_input, sizeof(mode_input), stdin) == NULL) { - printf("Error reading input\n"); - return 1; - } - - int entropy_mode = atoi(mode_input); - if (entropy_mode != 0 && entropy_mode != 1) { - printf("Invalid choice. Using ChaCha20 distribution (1).\n"); - entropy_mode = 1; - } - - // Use streaming collection with selected device - int result = collect_truerng_entropy_streaming_from_device(&selected_device, pad_chksum, target_bytes, 1, entropy_mode); - - if (result != 0) { - printf("Error: TrueRNG streaming entropy collection failed\n"); - return 1; - } - - // Update checksum after entropy addition - printf("\nšŸ”„ Updating pad checksum...\n"); - char new_chksum[65]; - int checksum_result = update_pad_checksum_after_entropy(pad_chksum, new_chksum); - - if (checksum_result == 0) { - printf("āœ“ Pad checksum updated successfully\n"); - printf(" Old checksum: %.16s...\n", pad_chksum); - printf(" New checksum: %.16s...\n", new_chksum); - printf("āœ“ Pad files renamed to new checksum\n"); - } else if (checksum_result == 2) { - printf("ℹ Checksum unchanged (unusual but not an error)\n"); - } else { - printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n"); - printf(" You may need to manually handle the checksum update\n"); - return 1; - } - - printf("\nšŸŽ‰ SUCCESS! Your entire pad now has enhanced randomness!\n"); - - // Use enhanced pause mechanism instead of simple getchar - print_centered_header("Pad Enhancement Complete", 1); - - return 0; - } - - // For other entropy sources or smaller amounts, use traditional approach - printf("\nCollecting %zu bytes of entropy from selected source...\n", target_bytes); - - // Allocate entropy buffer - unsigned char* entropy_buffer = malloc(MAX_ENTROPY_BUFFER); - if (!entropy_buffer) { - printf("Error: Cannot allocate entropy buffer\n"); - return 1; - } - - // Collect entropy using unified interface - size_t collected_bytes = 0; - int result = collect_entropy_by_source(entropy_source, entropy_buffer, target_bytes, &collected_bytes, 1); - - if (result != 0) { - printf("Error: Entropy collection failed\n"); - free(entropy_buffer); - return 1; - } - - if (collected_bytes < 512) { - printf("Error: Insufficient entropy collected (%zu bytes)\n", collected_bytes); - free(entropy_buffer); - return 1; - } - - printf("\nProcessing entropy and modifying pad...\n"); - - // Add entropy to pad - result = add_entropy_to_pad(pad_chksum, entropy_buffer, collected_bytes, 1); - - // Clear entropy buffer for security - memset(entropy_buffer, 0, MAX_ENTROPY_BUFFER); - free(entropy_buffer); - - if (result != 0) { - printf("Error: Failed to add entropy to pad\n"); - return 1; - } - - printf("\nšŸŽ‰ SUCCESS! Your pad now has enhanced randomness!\n"); - - // Use enhanced pause mechanism instead of simple getchar - print_centered_header("Entropy Enhancement Complete", 1); - - return 0; -} - -int handle_verify_pad(const char* pad_chksum) { - char header_text[128]; - snprintf(header_text, sizeof(header_text), "Verify Pad Integrity: %.16s...", pad_chksum); - printf("\n"); - print_centered_header(header_text, 0); - - // Construct pad file path - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Check if pad file exists - if (access(pad_path, R_OK) != 0) { - printf("āŒ ERROR: Pad file not found: %s\n", pad_path); - return 1; - } - - printf("Calculating pad checksum...\n"); - - // Calculate actual checksum of the pad file - char calculated_checksum[65]; - if (calculate_checksum(pad_path, calculated_checksum) != 0) { - printf("āŒ ERROR: Failed to calculate pad checksum\n"); - return 1; - } - - // Compare calculated checksum with filename (expected checksum) - if (strcmp(pad_chksum, calculated_checksum) == 0) { - printf("āœ… SUCCESS: Pad integrity verified!\n"); - printf(" Expected: %.16s...\n", pad_chksum); - printf(" Actual: %.16s...\n", calculated_checksum); - printf(" Status: MATCH - Pad is intact and valid\n"); - - // Get additional pad info - struct stat pad_stat; - if (stat(pad_path, &pad_stat) == 0) { - uint64_t used_bytes; - read_state_offset(pad_chksum, &used_bytes); - - double size_gb = (double)pad_stat.st_size / (1024.0 * 1024.0 * 1024.0); - double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0); - double usage_percent = (double)used_bytes / pad_stat.st_size * 100.0; - - printf(" Size: %.2f GB (%lu bytes)\n", size_gb, pad_stat.st_size); - printf(" Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes); - printf(" Usage: %.1f%%\n", usage_percent); - } - - printf("\nāœ… This pad is safe to use for encryption.\n"); - - // Pause before returning to menu to let user see the verification results - print_centered_header("Pad Verification Complete", 1); - return 0; - } else { - printf("āŒ FAILURE: Pad integrity check failed!\n"); - printf(" Expected: %.16s...\n", pad_chksum); - printf(" Actual: %.16s...\n", calculated_checksum); - printf(" Status: MISMATCH - Pad may be corrupted!\n"); - printf("\nāš ļø WARNING: This pad should NOT be used for encryption.\n"); - printf(" The pad file may have been modified or corrupted.\n"); - printf(" Consider regenerating this pad or using a different one.\n"); - return 1; - } -} - -int handle_delete_pad(const char* pad_chksum) { - char header_text[128]; - snprintf(header_text, sizeof(header_text), "Delete Pad: %.16s...", pad_chksum); - printf("\n"); - print_centered_header(header_text, 0); - - // Construct pad and state file paths - char pad_path[1024]; - char state_path[1024]; - get_pad_path(pad_chksum, pad_path, state_path); - - // Check if pad file exists - if (access(pad_path, F_OK) != 0) { - printf("āŒ ERROR: Pad file not found: %s\n", pad_path); - return 1; - } - - // Get pad information for display - struct stat pad_stat; - if (stat(pad_path, &pad_stat) == 0) { - uint64_t used_bytes; - read_state_offset(pad_chksum, &used_bytes); - - double size_gb = (double)pad_stat.st_size / (1024.0 * 1024.0 * 1024.0); - double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0); - double usage_percent = (double)used_bytes / pad_stat.st_size * 100.0; - - printf("Pad Information:\n"); - printf(" Checksum: %s\n", pad_chksum); - printf(" Size: %.2f GB (%lu bytes)\n", size_gb, pad_stat.st_size); - printf(" Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes); - printf(" Usage: %.1f%%\n", usage_percent); - printf(" Path: %s\n", pad_path); - } - - // Check if this is the default pad - char* current_default = get_default_pad_path(); - int is_default_pad = 0; - if (current_default) { - // Check if the pad to be deleted is the current default - if (strstr(current_default, pad_chksum)) { - is_default_pad = 1; - printf(" Status: āš ļø This is your DEFAULT pad\n"); - } - free(current_default); - } - - // Warning and confirmation - printf("\nāš ļø WARNING: This action cannot be undone!\n"); - if (is_default_pad) { - printf("āš ļø Deleting the default pad will require setting a new default.\n"); - } - printf("\nAre you absolutely sure you want to delete this pad? (y/N): "); - - char response[10]; - if (!fgets(response, sizeof(response), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - // Require explicit 'y' or 'Y' to proceed - if (response[0] != 'y' && response[0] != 'Y') { - printf("Pad deletion cancelled.\n"); - return 0; // User cancelled - not an error - } - - // Double confirmation for extra safety - printf("\nFinal confirmation - type 'DELETE' to proceed: "); - char final_response[20]; - if (!fgets(final_response, sizeof(final_response), stdin)) { - printf("Error: Failed to read input\n"); - return 1; - } - - // Remove newline - final_response[strcspn(final_response, "\n")] = 0; - - if (strcmp(final_response, "DELETE") != 0) { - printf("Confirmation text did not match. Pad deletion cancelled.\n"); - return 0; // User didn't confirm - not an error - } - - // Proceed with deletion - printf("\nDeleting pad files...\n"); - - // Delete pad file - if (unlink(pad_path) != 0) { - printf("āŒ ERROR: Failed to delete pad file: %s\n", pad_path); - perror("unlink"); - return 1; - } else { - printf("āœ… Deleted pad file: %s\n", pad_path); - } - - // Delete state file (if it exists) - if (access(state_path, F_OK) == 0) { - if (unlink(state_path) != 0) { - printf("āš ļø WARNING: Failed to delete state file: %s\n", state_path); - perror("unlink"); - // Continue - pad file was deleted successfully - } else { - printf("āœ… Deleted state file: %s\n", state_path); - } - } else { - printf("ā„¹ļø No state file found (this is normal)\n"); - } - - // Handle default pad update if necessary - if (is_default_pad) { - printf("\nšŸ”„ Updating default pad preference...\n"); - - // Clear the current default pad - if (set_preference("default_pad", NULL) == 0) { - printf("āœ… Default pad preference cleared\n"); - printf("ā„¹ļø You can set a new default pad from the pad management menu\n"); - } else { - printf("āš ļø WARNING: Failed to clear default pad preference\n"); - printf(" You may need to manually update your configuration\n"); - } - } - - printf("\nāœ… SUCCESS: Pad deleted successfully!\n"); - printf(" Checksum: %.16s...\n", pad_chksum); - printf(" Both pad and state files have been removed\n"); - - // Pause before returning to menu to let user see the success message - print_centered_header("Pad Deletion Complete", 1); - - return 0; -} - - -void print_usage(const char* program_name) { - printf("OTP Cipher - One Time Pad Implementation v0.3.16\n"); - printf("Built for testing entropy system\n"); - printf("Usage:\n"); - printf(" %s - Interactive mode\n", program_name); - printf(" %s generate|-g - Generate new pad\n", program_name); - printf(" %s encrypt|-e [pad_checksum_prefix] [text] - Encrypt text\n", program_name); - printf(" %s decrypt|-d [encrypted_message] - Decrypt message\n", program_name); - printf(" %s -f [-a] [-o ] - Encrypt file\n", program_name); - printf(" %s list|-l - List available pads\n", program_name); - printf("\nFile Operations:\n"); - printf(" -f - Encrypt file (binary .otp format)\n"); - printf(" -f -a - Encrypt file (ASCII .otp.asc format)\n"); - printf(" -o - Specify output filename\n"); - printf("\nShort flags:\n"); - printf(" -g generate -e encrypt -d decrypt -l list -f file\n"); - printf("\nExamples:\n"); - printf(" %s -e 1a2b3c \"Hello world\" - Encrypt inline text\n", program_name); - printf(" %s -f document.pdf 1a2b - Encrypt file (binary)\n", program_name); - printf(" %s -f document.pdf 1a2b -a - Encrypt file (ASCII)\n", program_name); - printf(" %s -f document.pdf 1a2b -o secret.otp - Encrypt with custom output\n", program_name); - printf(" %s -d \"-----BEGIN OTP MESSAGE-----...\" - Decrypt message/file\n", program_name); - printf(" %s -d encrypted.otp.asc - Decrypt ASCII file\n", program_name); - printf(" %s -g 1GB - Generate 1GB pad\n", program_name); - printf(" %s -l - List pads\n", program_name); - printf("\nSize examples: 1GB, 5TB, 512MB, 2048 (bytes)\n"); - printf("Pad selection: Full chksum or prefix\n"); -} diff --git a/otp-arm64 b/otp-arm64 deleted file mode 100755 index 4552667..0000000 Binary files a/otp-arm64 and /dev/null differ diff --git a/otp-x86_64 b/otp-x86_64 deleted file mode 100755 index ea6e14a..0000000 Binary files a/otp-x86_64 and /dev/null differ diff --git a/src/pads.c b/src/pads.c index 9dd1722..cece9c9 100644 --- a/src/pads.c +++ b/src/pads.c @@ -22,11 +22,13 @@ // Extracted pad management functions from otp.c int show_pad_info(const char* chksum) { - char pad_filename[MAX_HASH_LENGTH + 10]; - char state_filename[MAX_HASH_LENGTH + 10]; + char pad_filename[1024]; + char state_filename[1024]; - snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum); - snprintf(state_filename, sizeof(state_filename), "%s.state", chksum); + // Use full paths with pads directory + const char* pads_dir = get_current_pads_dir(); + snprintf(pad_filename, sizeof(pad_filename), "%s/%s.pad", pads_dir, chksum); + snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, chksum); struct stat st; if (stat(pad_filename, &st) != 0) { @@ -899,12 +901,14 @@ int update_pad_checksum_after_entropy(const char* old_chksum, char* new_chksum) // Verify pad integrity by checking its checksum int handle_verify_pad(const char* chksum) { - char pad_filename[MAX_HASH_LENGTH + 10]; - char state_filename[MAX_HASH_LENGTH + 10]; + char pad_filename[1024]; + char state_filename[1024]; char calculated_chksum[MAX_HASH_LENGTH]; - snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum); - snprintf(state_filename, sizeof(state_filename), "%s.state", chksum); + // Use full paths with pads directory + const char* pads_dir = get_current_pads_dir(); + snprintf(pad_filename, sizeof(pad_filename), "%s/%s.pad", pads_dir, chksum); + snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, chksum); struct stat st; if (stat(pad_filename, &st) != 0) { @@ -952,11 +956,13 @@ int handle_verify_pad(const char* chksum) { // Delete a pad and its associated state file int handle_delete_pad(const char* chksum) { - char pad_filename[MAX_HASH_LENGTH + 10]; - char state_filename[MAX_HASH_LENGTH + 10]; + char pad_filename[1024]; + char state_filename[1024]; - snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum); - snprintf(state_filename, sizeof(state_filename), "%s.state", chksum); + // Use full paths with pads directory + const char* pads_dir = get_current_pads_dir(); + snprintf(pad_filename, sizeof(pad_filename), "%s/%s.pad", pads_dir, chksum); + snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, chksum); // Check if pad exists if (access(pad_filename, F_OK) != 0) {