From 66c6e3eea529fe91a0d601431c693fe5eb3ab354 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Tue, 23 Sep 2025 10:39:17 -0400 Subject: [PATCH] Version v0.3.11 - Add delete option, and TUI improvements --- otp-arm64 | Bin 94296 -> 98456 bytes otp-x86_64 | Bin 105288 -> 113536 bytes otp.c | 770 ++++++++++++++++++++++++++++++++++++++++------------- otp.h | 12 +- 4 files changed, 593 insertions(+), 189 deletions(-) diff --git a/otp-arm64 b/otp-arm64 index 13dde59262da69992fd4397a2a63640b7333df2f..945855826c5809624ce2aeac0edc25ff6f128d2d 100755 GIT binary patch delta 29009 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 delta 26000 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 diff --git a/otp-x86_64 b/otp-x86_64 index 3209a901127b13681006cff4dd3000967a7e9f2b..9204611f14389469d96412bc5ae1080047b777e7 100755 GIT binary patch delta 29224 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 e@}=_ChY&B@CsF-XT~xYCCotx~F5TZ0f{ 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 aN%v9a-eQg}l>!ct=Hi%D(ttyx;r{^~!{8|Z diff --git a/otp.c b/otp.c index ba57073..e5953a6 100644 --- a/otp.c +++ b/otp.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,6 @@ static const int base64_decode_table[256] = { #define PROGRESS_UPDATE_INTERVAL (64 * 1024 * 1024) // 64MB intervals #define DEFAULT_PADS_DIR "pads" #define FILES_DIR "files" -#define MAX_ENTROPY_BUFFER (4 * 1024 * 1024) // 4MB entropy buffer for large operations //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -56,6 +56,60 @@ static char current_pads_dir[512] = DEFAULT_PADS_DIR; static char default_pad_path[1024] = ""; static int is_interactive_mode = 0; +// Terminal dimensions +static int terminal_width = 80; // Default fallback width +static int terminal_height = 24; // Default fallback height + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// TERMINAL UI FUNCTIONS +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Initialize terminal dimensions +void init_terminal_dimensions(void) { + struct winsize ws; + + // Try to get actual terminal size + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0 && ws.ws_row > 0) { + terminal_width = ws.ws_col; + terminal_height = ws.ws_row; + } + // If ioctl fails, keep the default values (80x24) +} + +// Print centered header with = padding +void print_centered_header(const char* text) { + if (!text) return; + + int text_len = strlen(text); + int available_width = terminal_width; + + // Ensure minimum spacing: at least 1 space on each side + int min_required = text_len + 4; // text + " " + text + " " (spaces around text) + + if (available_width < min_required) { + // Terminal too narrow - just print the text with minimal formatting + printf("=== %s ===\n", text); + return; + } + + // Calculate padding + int total_padding = available_width - text_len - 2; // -2 for spaces around text + int left_padding = total_padding / 2; + int right_padding = total_padding - left_padding; + + // Print the header + for (int i = 0; i < left_padding; i++) { + printf("="); + } + printf(" %s ", text); + for (int i = 0; i < right_padding; i++) { + printf("="); + } + printf("\n"); +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // MAIN @@ -63,7 +117,10 @@ static int is_interactive_mode = 0; //////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { - // Load preferences first + // Initialize terminal dimensions first + init_terminal_dimensions(); + + // Load preferences load_preferences(); // Detect interactive mode: only true when running with no arguments @@ -329,9 +386,9 @@ int interactive_mode(void) { void show_main_menu(void) { - - - printf("\n=========================== Main Menu - OTP v0.3.9 ===========================\n\n"); + printf("\n"); + print_centered_header("Main Menu - OTP v0.3.10"); + printf("\n"); printf(" \033[4mT\033[0mext encrypt\n"); //TEXT ENCRYPT printf(" \033[4mF\033[0mile encrypt\n"); //FILE ENCRYPT @@ -342,7 +399,8 @@ void show_main_menu(void) { } int handle_generate_menu(void) { - printf("\n=== Generate New Pad ===\n"); + printf("\n"); + print_centered_header("Generate New Pad"); printf("Enter pad size (examples: 1GB, 5TB, 512MB, 2048): "); char size_input[64]; @@ -367,7 +425,8 @@ int handle_generate_menu(void) { } int handle_encrypt_menu(void) { - printf("\n=== Encrypt Data ===\n"); + printf("\n"); + print_centered_header("Encrypt Data"); printf("Available pads:\n"); char* selected = select_pad_interactive("Available pads:", "Select pad (or press Enter to continue)", PAD_FILTER_ALL, 0); @@ -398,7 +457,7 @@ int handle_encrypt_menu(void) { if (choice == 1) { // Text encryption - use unified pad selection - char* selected_pad = select_pad_interactive("=== Select Pad for Text Encryption ===", + char* selected_pad = select_pad_interactive("Select Pad for Text Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { @@ -453,7 +512,7 @@ int handle_encrypt_menu(void) { } // Use unified pad selection - char* selected_pad = select_pad_interactive("=== Select Pad for File Encryption ===", + char* selected_pad = select_pad_interactive("Select Pad for File Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { @@ -508,7 +567,8 @@ int handle_encrypt_menu(void) { } int handle_decrypt_menu(void) { - printf("\n=== Smart Decrypt ===\n"); + printf("\n"); + print_centered_header("Smart Decrypt"); printf("Enter encrypted data (paste ASCII armor), file path, or press Enter to browse files:\n"); char input_line[MAX_LINE_LENGTH]; @@ -742,7 +802,7 @@ int show_pad_info(const char* chksum) { uint64_t used_bytes; read_state_offset(chksum, &used_bytes); - printf("=== Pad Information ===\n"); + print_centered_header("Pad Information"); printf("ChkSum: %s\n", chksum); printf("File: %s\n", pad_filename); @@ -1797,6 +1857,136 @@ void get_default_file_path(const char* filename, char* result_path, size_t resul snprintf(result_path, result_size, "%s/%s", files_dir, filename); } +void get_directory_display(const char* file_path, char* result, size_t result_size) { + // Extract directory path from full file path + char dir_path[512]; + char* last_slash = strrchr(file_path, '/'); + + if (last_slash) { + size_t dir_len = last_slash - file_path; + if (dir_len >= sizeof(dir_path)) { + dir_len = sizeof(dir_path) - 1; + } + strncpy(dir_path, file_path, dir_len); + dir_path[dir_len] = '\0'; + } else { + // No directory separator, assume current directory + strcpy(dir_path, "."); + } + + // USB Drive Detection and Smart Shortening + char* home_dir = getenv("HOME"); + + // Check for USB/removable media mount patterns + if (strstr(dir_path, "/media/") || strstr(dir_path, "/run/media/") || strstr(dir_path, "/mnt/")) { + // Extract USB label/name + char* media_start = NULL; + if (strstr(dir_path, "/media/")) { + media_start = strstr(dir_path, "/media/"); + } else if (strstr(dir_path, "/run/media/")) { + media_start = strstr(dir_path, "/run/media/"); + } else if (strstr(dir_path, "/mnt/")) { + media_start = strstr(dir_path, "/mnt/"); + } + + if (media_start) { + // Find the USB label part + char* path_after_media = strchr(media_start + 1, '/'); + if (path_after_media) { + path_after_media++; // Skip the slash + + // For /media/user/LABEL pattern, skip the username to get to the drive label + if (strstr(media_start, "/media/")) { + char* next_slash = strchr(path_after_media, '/'); + if (next_slash) { + path_after_media = next_slash + 1; + } + } + // For /run/media/user/LABEL pattern, skip the username + else if (strstr(media_start, "/run/media/")) { + char* next_slash = strchr(path_after_media, '/'); + if (next_slash) { + path_after_media = next_slash + 1; + } + } + + // Extract just the USB label (up to next slash or end) + char* label_end = strchr(path_after_media, '/'); + char usb_label[32]; + if (label_end) { + size_t label_len = label_end - path_after_media; + if (label_len > sizeof(usb_label) - 1) label_len = sizeof(usb_label) - 1; + strncpy(usb_label, path_after_media, label_len); + usb_label[label_len] = '\0'; + } else { + // USB label is the last part + strncpy(usb_label, path_after_media, sizeof(usb_label) - 1); + usb_label[sizeof(usb_label) - 1] = '\0'; + } + + // Format with USB: prefix, limiting total length to fit in result + snprintf(result, result_size, "USB:%s", usb_label); + // Truncate if too long + if (strlen(result) > 11) { + result[11] = '\0'; + } + return; + } + } + } + + // Home directory shortening + if (home_dir && strncmp(dir_path, home_dir, strlen(home_dir)) == 0) { + if (dir_path[strlen(home_dir)] == '/' || dir_path[strlen(home_dir)] == '\0') { + // Replace home directory with ~ + char temp[512]; + snprintf(temp, sizeof(temp), "~%s", dir_path + strlen(home_dir)); + + // If result is too long, truncate intelligently + if (strlen(temp) > 11) { + // Show ~/...end_part + char* last_part = strrchr(temp, '/'); + if (last_part && strlen(last_part) < 8) { + snprintf(result, result_size, "~...%s", last_part); + } else { + strncpy(result, temp, 11); + result[11] = '\0'; + } + } else { + strncpy(result, temp, result_size - 1); + result[result_size - 1] = '\0'; + } + return; + } + } + + // Current working directory + if (strcmp(dir_path, ".") == 0 || strcmp(dir_path, current_pads_dir) == 0) { + strncpy(result, "pads", result_size - 1); + result[result_size - 1] = '\0'; + return; + } + + // System/other paths - smart truncation with ellipsis + if (strlen(dir_path) > 11) { + // Try to show the most meaningful part + char* last_part = strrchr(dir_path, '/'); + if (last_part && strlen(last_part) < 9) { + // Show .../last_part + snprintf(result, result_size, "...%s", last_part); + } else { + // Show first part with ellipsis + strncpy(result, dir_path, 8); + strncpy(result + 8, "...", result_size - 8 - 1); + result[result_size - 1] = '\0'; + } + } else { + // Short enough, use as-is + strncpy(result, dir_path, result_size - 1); + result[result_size - 1] = '\0'; + } +} + void get_pad_path(const char* chksum, char* pad_path, char* state_path) { snprintf(pad_path, 1024, "%s/%s.pad", current_pads_dir, chksum); snprintf(state_path, 1024, "%s/%s.state", current_pads_dir, chksum); @@ -2186,36 +2376,9 @@ int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) { -// To be removed -// Format file size for display -// void format_size_string(uint64_t bytes, char* result, size_t result_size) { -// if (bytes == 0) { -// strncpy(result, "Unknown", result_size - 1); -// result[result_size - 1] = '\0'; -// return; -// } - -// if (bytes < 1024) { -// snprintf(result, result_size, "%luB", bytes); -// } else if (bytes < 1024 * 1024) { -// snprintf(result, result_size, "%.1fKB", (double)bytes / 1024.0); -// } else if (bytes < 1024 * 1024 * 1024) { -// snprintf(result, result_size, "%.1fMB", (double)bytes / (1024.0 * 1024.0)); -// } else if (bytes < 1024ULL * 1024 * 1024 * 1024) { -// snprintf(result, result_size, "%.2fGB", (double)bytes / (1024.0 * 1024.0 * 1024.0)); -// } else { -// snprintf(result, result_size, "%.2fTB", (double)bytes / (1024.0 * 1024.0 * 1024.0 * 1024.0)); -// } -// } - - - - // 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); @@ -2551,6 +2714,21 @@ int collect_truerng_entropy(unsigned char* entropy_buffer, size_t target_bytes, return 0; // Success } +// Helper function to format time in human-readable format +void format_time_remaining(double seconds, char* buffer, size_t buffer_size) { + if (seconds < 60) { + snprintf(buffer, buffer_size, "%.0fs", seconds); + } else if (seconds < 3600) { + int mins = (int)(seconds / 60); + int secs = (int)(seconds) % 60; + snprintf(buffer, buffer_size, "%dm %02ds", mins, secs); + } else { + int hours = (int)(seconds / 3600); + int mins = (int)(seconds / 60) % 60; + snprintf(buffer, buffer_size, "%dh %02dm", hours, mins); + } +} + // Streaming TrueRNG entropy collection for full pad enhancement // This function applies entropy directly to the pad in chunks to avoid memory issues int collect_truerng_entropy_streaming(const char* pad_chksum, size_t total_bytes, int display_progress) { @@ -2586,6 +2764,85 @@ int collect_truerng_entropy_streaming(const char* pad_chksum, size_t total_bytes return 2; // Serial port setup failed } + // For large pads (>10MB), do a 1MB test to estimate completion time + double estimated_rate = 0.0; + int user_confirmed = 1; // Default to confirmed for small pads + + if (total_bytes > 10 * 1024 * 1024 && display_progress) { + printf("\nLarge pad detected (%.1f MB). Running 1MB speed test...\n", + (double)total_bytes / (1024.0 * 1024.0)); + + // Test with 1MB sample + size_t test_bytes = 1024 * 1024; // 1MB + unsigned char* test_buffer = malloc(test_bytes); + if (!test_buffer) { + printf("Error: Cannot allocate test buffer\n"); + close(serial_fd); + return 3; + } + + time_t test_start = time(NULL); + size_t test_collected = 0; + + while (test_collected < test_bytes) { + size_t bytes_needed = test_bytes - test_collected; + size_t read_size = (bytes_needed > 1024) ? 1024 : bytes_needed; + + ssize_t result = read(serial_fd, test_buffer + test_collected, read_size); + if (result <= 0) { + printf("Error: TrueRNG test failed\n"); + free(test_buffer); + close(serial_fd); + return 4; + } + test_collected += result; + + // Show test progress + double test_progress = (double)test_collected / test_bytes * 100.0; + printf("\rSpeed test: %.1f%% complete", test_progress); + fflush(stdout); + } + + double test_time = difftime(time(NULL), test_start); + estimated_rate = (double)test_bytes / test_time; // bytes per second + + // Calculate estimated total time + double estimated_total_time = (double)total_bytes / estimated_rate; + + char time_str[64]; + format_time_remaining(estimated_total_time, time_str, sizeof(time_str)); + + printf("\nSpeed test complete: %.1f KB/s\n", estimated_rate / 1024.0); + printf("Estimated completion time: %s\n", time_str); + printf("\nProceed with full pad enhancement? (y/N): "); + fflush(stdout); + + char response[10]; + if (fgets(response, sizeof(response), stdin) == NULL || + (response[0] != 'y' && response[0] != 'Y')) { + printf("Operation cancelled by user.\n"); + free(test_buffer); + close(serial_fd); + return 5; // User cancelled + } + + user_confirmed = 1; + free(test_buffer); + + // Reset serial port for main operation + close(serial_fd); + serial_fd = setup_truerng_serial_port(port_path); + if (serial_fd < 0) { + printf("Error: Cannot reopen TrueRNG device\n"); + return 6; + } + } + + if (!user_confirmed) { + close(serial_fd); + return 5; // User cancelled + } + // Get pad paths char pad_path[1024]; char state_path[1024]; @@ -2762,12 +3019,54 @@ int collect_truerng_entropy_streaming(const char* pad_chksum, size_t total_bytes bytes_processed += current_chunk_size; - // Show progress for large pads - if (display_progress && bytes_processed % (64 * 1024 * 1024) == 0) { // Every 64MB - double percentage = (double)bytes_processed / total_bytes * 100.0; - printf("Progress: %.1f%% (%zu/%zu MB)\r", percentage, - bytes_processed / (1024*1024), total_bytes / (1024*1024)); - fflush(stdout); + // Show progress and entropy samples for visual verification + if (display_progress) { + // Update progress more frequently for better user experience - always show cumulative amount + if (bytes_processed % (4 * 1024 * 1024) == 0 || bytes_processed == total_bytes) { // Every 4MB or at completion + double percentage = (double)bytes_processed / total_bytes * 100.0; + double elapsed = difftime(time(NULL), start_time); + double rate = 0.0; + if (elapsed > 0) { + rate = (double)bytes_processed / elapsed / (1024.0 * 1024.0); + } + + // Calculate estimated time remaining + char eta_str[64] = ""; + if (rate > 0.0 && bytes_processed < total_bytes) { + double remaining_bytes = (double)(total_bytes - bytes_processed); + double eta_seconds = remaining_bytes / (rate * 1024.0 * 1024.0); // Convert rate back to bytes/sec + format_time_remaining(eta_seconds, eta_str, sizeof(eta_str)); + } + + // Clear previous line and show progress bar with cumulative TrueRNG data generated + printf("\r\033[K"); // Clear line + printf("["); + int bar_width = 30; + int filled = (int)(percentage / 100.0 * bar_width); + for (int i = 0; i < filled; i++) printf("ā–ˆ"); + for (int i = filled; i < bar_width; i++) printf("ā–‘"); + + if (strlen(eta_str) > 0) { + printf("] %.1f%% - TrueRNG: %zu MB / %zu MB (%.1f MB/s) ETA: %s", + percentage, bytes_processed / (1024*1024), total_bytes / (1024*1024), rate, eta_str); + } else { + printf("] %.1f%% - TrueRNG: %zu MB / %zu MB (%.1f MB/s)", + percentage, bytes_processed / (1024*1024), total_bytes / (1024*1024), rate); + } + fflush(stdout); + } + + // Show entropy samples every 64MB for visual verification of randomness + if (bytes_processed % (64 * 1024 * 1024) == 0 && bytes_processed > 0) { + printf("\nšŸ”¬ TrueRNG entropy sample: "); + // Display first 16 bytes of current entropy chunk as hex + size_t sample_size = (current_chunk_size < 16) ? current_chunk_size : 16; + for (size_t i = 0; i < sample_size; i++) { + printf("%02x", entropy_chunk[i]); + if (i == 7) printf(" "); // Space in middle for readability + } + printf("\n"); + } } } @@ -2805,7 +3104,7 @@ int collect_truerng_entropy_streaming(const char* pad_chksum, size_t total_bytes int collect_dice_entropy(unsigned char* entropy_buffer, size_t target_bytes, size_t* collected_bytes, int display_progress) { if (display_progress) { - printf("=== Dice Entropy Collection ===\n"); + print_centered_header("Dice Entropy Collection"); printf("Enter dice rolls as sequences of digits 1-6.\n"); printf("Target: %zu bytes (%zu dice rolls needed)\n", target_bytes, target_bytes * 4); printf("Press Enter after each sequence, or 'done' when finished.\n\n"); @@ -3493,7 +3792,7 @@ int generate_ascii_armor(const char* chksum, uint64_t offset, const unsigned cha strcpy(*ascii_output, "-----BEGIN OTP MESSAGE-----\n"); char temp_line[256]; - snprintf(temp_line, sizeof(temp_line), "Version: v0.3.9\n"); + snprintf(temp_line, sizeof(temp_line), "Version: v0.3.10\n"); strcat(*ascii_output, temp_line); snprintf(temp_line, sizeof(temp_line), "Pad-ChkSum: %s\n", chksum); @@ -4157,7 +4456,8 @@ int launch_file_manager(const char* start_directory, char* selected_file, size_t } int handle_text_encrypt(void) { - printf("\n=== Text Encrypt ===\n"); + printf("\n"); + print_centered_header("Text Encrypt"); // Launch text editor directly char text_buffer[MAX_INPUT_SIZE]; @@ -4172,7 +4472,7 @@ int handle_text_encrypt(void) { } // Use unified pad selection - char* selected_pad = select_pad_interactive("=== Select Pad for Text Encryption ===", + char* selected_pad = select_pad_interactive("Select Pad for Text Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { @@ -4186,7 +4486,8 @@ int handle_text_encrypt(void) { } int handle_file_encrypt(void) { - printf("\n=== File Encrypt ===\n"); + printf("\n"); + print_centered_header("File Encrypt"); // Launch file manager directly char input_file[512]; @@ -4202,7 +4503,7 @@ int handle_file_encrypt(void) { } // Use unified pad selection - char* selected_pad = select_pad_interactive("=== Select Pad for File Encryption ===", + char* selected_pad = select_pad_interactive("Select Pad for File Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { @@ -4373,7 +4674,7 @@ char* select_pad_interactive(const char* title, const char* prompt, pad_filter_t 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)); + get_directory_display(full_path, pads[pad_count].location, sizeof(pads[pad_count].location)); pad_count++; } @@ -4543,7 +4844,8 @@ char* select_pad_interactive(const char* title, const char* prompt, pad_filter_t } int handle_pads_menu(void) { - printf("\n=== Pad Management ===\n"); + printf("\n"); + print_centered_header("Pad Management"); // Get list of pads from current directory DIR* dir = opendir(current_pads_dir); @@ -4606,7 +4908,7 @@ int handle_pads_menu(void) { 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)); + get_directory_display(full_path, pads[pad_count].location, sizeof(pads[pad_count].location)); pad_count++; } @@ -4712,6 +5014,8 @@ int handle_pads_menu(void) { printf("\nActions:\n"); printf(" \033[4mG\033[0menerate new pad\n"); printf(" \033[4mA\033[0mdd entropy to pad\n"); + printf(" \033[4mV\033[0merify pad integrity\n"); + printf(" \033[4mD\033[0melete pad\n"); printf(" \033[4mS\033[0met default pad\n"); printf(" E\033[4mx\033[0mit\n"); printf("\nSelect action: "); @@ -4733,7 +5037,7 @@ int handle_pads_menu(void) { 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 ===", + 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) { @@ -4749,9 +5053,37 @@ int handle_pads_menu(void) { return handle_pads_menu(); } return result; + } else if (toupper(input[0]) == 'V') { + // Verify pad integrity - use unified function + char* selected_pad = select_pad_interactive("Select Pad for Verification", + "Select pad to verify (by prefix)", + PAD_FILTER_ALL, 1); + if (!selected_pad) { + printf("Pad verification cancelled.\n"); + return handle_pads_menu(); + } + + // Verify the selected pad + handle_verify_pad(selected_pad); + free(selected_pad); + return handle_pads_menu(); // Always return to pads menu after verification + } else if (toupper(input[0]) == 'D') { + // Delete pad - use unified function + char* selected_pad = select_pad_interactive("Select Pad for Deletion", + "Select pad to delete (by prefix)", + PAD_FILTER_ALL, 1); + if (!selected_pad) { + printf("Pad deletion cancelled.\n"); + return handle_pads_menu(); + } + + // Delete the selected pad + handle_delete_pad(selected_pad); + free(selected_pad); + return handle_pads_menu(); // Always return to pads menu after deletion attempt } else if (toupper(input[0]) == 'S') { // Set default pad - use unified function - char* selected_pad = select_pad_interactive("=== Select Default Pad ===", + char* selected_pad = select_pad_interactive("Select Default Pad", "Select pad to set as default (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { @@ -4811,139 +5143,11 @@ int handle_pads_menu(void) { } } -// To be removed -// 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); + char header_text[128]; + snprintf(header_text, sizeof(header_text), "Add Entropy to Pad: %.16s...", pad_chksum); + printf("\n"); + print_centered_header(header_text); // Present entropy source selection menu with consistent formatting printf("Select entropy source:\n"); @@ -5130,9 +5334,199 @@ int handle_add_entropy_to_pad(const char* pad_chksum) { return 0; } +int handle_verify_pad(const char* pad_chksum) { + char header_text[128]; + snprintf(header_text, sizeof(header_text), "Verify Pad Integrity: %.16s...", pad_chksum); + printf("\n"); + print_centered_header(header_text); + + // Construct pad file path + char pad_path[1024]; + char state_path[1024]; + get_pad_path(pad_chksum, pad_path, state_path); + + // Check if pad file exists + if (access(pad_path, R_OK) != 0) { + printf("āŒ ERROR: Pad file not found: %s\n", pad_path); + return 1; + } + + printf("Calculating pad checksum...\n"); + + // Calculate actual checksum of the pad file + char calculated_checksum[65]; + if (calculate_checksum(pad_path, calculated_checksum) != 0) { + printf("āŒ ERROR: Failed to calculate pad checksum\n"); + return 1; + } + + // Compare calculated checksum with filename (expected checksum) + if (strcmp(pad_chksum, calculated_checksum) == 0) { + printf("āœ… SUCCESS: Pad integrity verified!\n"); + printf(" Expected: %.16s...\n", pad_chksum); + printf(" Actual: %.16s...\n", calculated_checksum); + printf(" Status: MATCH - Pad is intact and valid\n"); + + // Get additional pad info + struct stat pad_stat; + if (stat(pad_path, &pad_stat) == 0) { + uint64_t used_bytes; + read_state_offset(pad_chksum, &used_bytes); + + double size_gb = (double)pad_stat.st_size / (1024.0 * 1024.0 * 1024.0); + double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0); + double usage_percent = (double)used_bytes / pad_stat.st_size * 100.0; + + printf(" Size: %.2f GB (%lu bytes)\n", size_gb, pad_stat.st_size); + printf(" Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes); + printf(" Usage: %.1f%%\n", usage_percent); + } + + printf("\nāœ… This pad is safe to use for encryption.\n"); + return 0; + } else { + printf("āŒ FAILURE: Pad integrity check failed!\n"); + printf(" Expected: %.16s...\n", pad_chksum); + printf(" Actual: %.16s...\n", calculated_checksum); + printf(" Status: MISMATCH - Pad may be corrupted!\n"); + printf("\nāš ļø WARNING: This pad should NOT be used for encryption.\n"); + printf(" The pad file may have been modified or corrupted.\n"); + printf(" Consider regenerating this pad or using a different one.\n"); + return 1; + } +} + +int handle_delete_pad(const char* pad_chksum) { + char header_text[128]; + snprintf(header_text, sizeof(header_text), "Delete Pad: %.16s...", pad_chksum); + printf("\n"); + print_centered_header(header_text); + + // Construct pad and state file paths + char pad_path[1024]; + char state_path[1024]; + get_pad_path(pad_chksum, pad_path, state_path); + + // Check if pad file exists + if (access(pad_path, F_OK) != 0) { + printf("āŒ ERROR: Pad file not found: %s\n", pad_path); + return 1; + } + + // Get pad information for display + struct stat pad_stat; + if (stat(pad_path, &pad_stat) == 0) { + uint64_t used_bytes; + read_state_offset(pad_chksum, &used_bytes); + + double size_gb = (double)pad_stat.st_size / (1024.0 * 1024.0 * 1024.0); + double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0); + double usage_percent = (double)used_bytes / pad_stat.st_size * 100.0; + + printf("Pad Information:\n"); + printf(" Checksum: %s\n", pad_chksum); + printf(" Size: %.2f GB (%lu bytes)\n", size_gb, pad_stat.st_size); + printf(" Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes); + printf(" Usage: %.1f%%\n", usage_percent); + printf(" Path: %s\n", pad_path); + } + + // Check if this is the default pad + char* current_default = get_default_pad_path(); + int is_default_pad = 0; + if (current_default) { + // Check if the pad to be deleted is the current default + if (strstr(current_default, pad_chksum)) { + is_default_pad = 1; + printf(" Status: āš ļø This is your DEFAULT pad\n"); + } + free(current_default); + } + + // Warning and confirmation + printf("\nāš ļø WARNING: This action cannot be undone!\n"); + if (is_default_pad) { + printf("āš ļø Deleting the default pad will require setting a new default.\n"); + } + printf("\nAre you absolutely sure you want to delete this pad? (y/N): "); + + char response[10]; + if (!fgets(response, sizeof(response), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + + // Require explicit 'y' or 'Y' to proceed + if (response[0] != 'y' && response[0] != 'Y') { + printf("Pad deletion cancelled.\n"); + return 0; // User cancelled - not an error + } + + // Double confirmation for extra safety + printf("\nFinal confirmation - type 'DELETE' to proceed: "); + char final_response[20]; + if (!fgets(final_response, sizeof(final_response), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + + // Remove newline + final_response[strcspn(final_response, "\n")] = 0; + + if (strcmp(final_response, "DELETE") != 0) { + printf("Confirmation text did not match. Pad deletion cancelled.\n"); + return 0; // User didn't confirm - not an error + } + + // Proceed with deletion + printf("\nDeleting pad files...\n"); + + // Delete pad file + if (unlink(pad_path) != 0) { + printf("āŒ ERROR: Failed to delete pad file: %s\n", pad_path); + perror("unlink"); + return 1; + } else { + printf("āœ… Deleted pad file: %s\n", pad_path); + } + + // Delete state file (if it exists) + if (access(state_path, F_OK) == 0) { + if (unlink(state_path) != 0) { + printf("āš ļø WARNING: Failed to delete state file: %s\n", state_path); + perror("unlink"); + // Continue - pad file was deleted successfully + } else { + printf("āœ… Deleted state file: %s\n", state_path); + } + } else { + printf("ā„¹ļø No state file found (this is normal)\n"); + } + + // Handle default pad update if necessary + if (is_default_pad) { + printf("\nšŸ”„ Updating default pad preference...\n"); + + // Clear the current default pad + if (set_preference("default_pad", NULL) == 0) { + printf("āœ… Default pad preference cleared\n"); + printf("ā„¹ļø You can set a new default pad from the pad management menu\n"); + } else { + printf("āš ļø WARNING: Failed to clear default pad preference\n"); + printf(" You may need to manually update your configuration\n"); + } + } + + printf("\nāœ… SUCCESS: Pad deleted successfully!\n"); + printf(" Checksum: %.16s...\n", pad_chksum); + printf(" Both pad and state files have been removed\n"); + + return 0; +} + void print_usage(const char* program_name) { - printf("OTP Cipher - One Time Pad Implementation v0.3.9\n"); + printf("OTP Cipher - One Time Pad Implementation v0.3.10\n"); printf("Built for testing entropy system\n"); printf("Usage:\n"); printf(" %s - Interactive mode\n", program_name); diff --git a/otp.h b/otp.h index df93e29..2600e5f 100644 --- a/otp.h +++ b/otp.h @@ -29,7 +29,7 @@ #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 +#define MAX_ENTROPY_BUFFER (4 * 1024 * 1024) // 4MB entropy buffer for large operations //////////////////////////////////////////////////////////////////////////////// // TYPE DEFINITIONS @@ -248,6 +248,14 @@ int universal_decrypt(const char* input_data, const char* output_target, decrypt char* custom_base64_encode(const unsigned char* input, int length); unsigned char* custom_base64_decode(const char* input, int* output_length); +//////////////////////////////////////////////////////////////////////////////// +// TERMINAL UI FUNCTIONS +//////////////////////////////////////////////////////////////////////////////// + +// Terminal dimension and UI functions +void init_terminal_dimensions(void); +void print_centered_header(const char* text); + //////////////////////////////////////////////////////////////////////////////// // MENU SYSTEM FUNCTIONS //////////////////////////////////////////////////////////////////////////////// @@ -260,6 +268,8 @@ int handle_decrypt_menu(void); int handle_pads_menu(void); int handle_text_encrypt(void); int handle_file_encrypt(void); +int handle_verify_pad(const char* pad_chksum); +int handle_delete_pad(const char* pad_chksum); //////////////////////////////////////////////////////////////////////////////// // ENHANCED INPUT FUNCTIONS