#define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE #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" #define MAX_ENTROPY_BUFFER 32768 // 32KB entropy buffer //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // GLOBAL VARIABLES //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// static char current_pads_dir[512] = DEFAULT_PADS_DIR; static char default_pad_path[1024] = ""; static int is_interactive_mode = 0; //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // MAIN //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { // Load preferences first 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=========================== Main Menu - OTP v0.3.5 ===========================\n\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=== Generate New Pad ===\n"); 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=== Encrypt Data ===\n"); 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=== Smart Decrypt ===\n"); 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); printf("=== Pad Information ===\n"); 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; } 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[64]; char pad_path[MAX_HASH_LENGTH + 20]; char state_path[MAX_HASH_LENGTH + 20]; char chksum_hex[MAX_HASH_LENGTH]; // Create temporary filename snprintf(temp_filename, sizeof(temp_filename), "temp_%ld.pad", 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 (calculate_checksum(temp_filename, chksum_hex) != 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); // Try rename first (works for same filesystem) if (rename(temp_filename, pad_path) != 0) { // If rename fails, try copy and delete (works across filesystems) FILE* temp_file = fopen(temp_filename, "rb"); FILE* dest_file = fopen(pad_path, "wb"); if (!temp_file || !dest_file) { printf("Error: Cannot copy pad file to pads directory\n"); if (temp_file) fclose(temp_file); if (dest_file) fclose(dest_file); unlink(temp_filename); return 1; } // Copy file in chunks unsigned char copy_buffer[64 * 1024]; size_t bytes_read; while ((bytes_read = fread(copy_buffer, 1, sizeof(copy_buffer), temp_file)) > 0) { if (fwrite(copy_buffer, 1, bytes_read, dest_file) != bytes_read) { printf("Error: Failed to copy pad file to pads directory\n"); fclose(temp_file); fclose(dest_file); unlink(temp_filename); unlink(pad_path); return 1; } } fclose(temp_file); fclose(dest_file); // Remove temporary file after successful copy unlink(temp_filename); } // 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"); return 0; } // In-place pad entropy addition using Chacha20 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; } // 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); // 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; // 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"); } 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"); } // 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"); // 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_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 //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// 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 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.5\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=== Text Encrypt ===\n"); // 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=== File Encrypt ===\n"); // 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; } // 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=== Pad Management ===\n"); // 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[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]) == '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(); } } 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'; } } int handle_add_entropy_to_pad(const char* pad_chksum) { printf("\n=== Add Entropy to Pad: %.16s... ===\n", pad_chksum); printf("This will enhance the randomness of your pad using keyboard entropy.\n"); printf("The entropy will be processed with Chacha20 and distributed throughout the entire pad.\n\n"); printf("Entropy 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 choice_input[10]; if (!fgets(choice_input, sizeof(choice_input), stdin)) { printf("Error: Failed to read input\n"); return 1; } size_t target_bytes = 2048; // Default int choice = atoi(choice_input); switch (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; } printf("\nCollecting %zu bytes of entropy...\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 with visual feedback size_t collected_bytes = 0; int result = collect_entropy_with_feedback(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"); printf("Press Enter to continue..."); getchar(); return 0; } void print_usage(const char* program_name) { printf("OTP Cipher - One Time Pad Implementation v0.3.5\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"); }