#define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "src/version.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 PADS_DIR "pads" #define MAX_ENTROPY_BUFFER 32768 // 32KB entropy buffer // Function prototypes int main(int argc, char* argv[]); int interactive_mode(void); int command_line_mode(int argc, char* argv[]); // Core functions int generate_pad(uint64_t size_bytes, int show_progress); int generate_pad_with_entropy(uint64_t size_bytes, int show_progress, int use_keyboard_entropy); int encrypt_text(const char* pad_identifier, const char* input_text); int decrypt_text(const char* pad_identifier, const char* encrypted_message); int encrypt_file(const char* pad_identifier, const char* input_file, const char* output_file, int ascii_armor); int decrypt_file(const char* input_file, const char* output_file); int decrypt_binary_file(FILE* input_fp, const char* output_file); int decrypt_ascii_file(const char* input_file, const char* output_file); // Keyboard entropy functions int setup_raw_terminal(struct termios* original_termios); void restore_terminal(struct termios* original_termios); int collect_keyboard_entropy(unsigned char* entropy_buffer, size_t max_size, size_t* collected); void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size, const unsigned char* entropy_data, size_t entropy_size); // Directory management int ensure_pads_directory(void); void get_pad_path(const char* chksum, char* pad_path, char* state_path); // Utility functions uint64_t parse_size_string(const char* size_str); char* find_pad_by_prefix(const char* prefix); int list_available_pads(void); int show_pad_info(const char* chksum); int get_user_choice(int min, int max); void show_progress(uint64_t current, uint64_t total, time_t start_time); // File operations int read_state_offset(const char* pad_chksum, uint64_t* offset); int write_state_offset(const char* pad_chksum, uint64_t offset); int calculate_checksum(const char* filename, char* checksum_hex); void xor_checksum_256(const unsigned char* data, size_t len, unsigned char checksum[32]); char* custom_base64_encode(const unsigned char* input, int length); unsigned char* custom_base64_decode(const char* input, int* output_length); // Menu functions void show_main_menu(void); int handle_generate_menu(void); int handle_encrypt_menu(void); int handle_decrypt_menu(void); void print_usage(const char* program_name); int main(int argc, char* argv[]) { if (argc == 1) { return interactive_mode(); } else { return command_line_mode(argc, argv); } } int interactive_mode(void) { printf("=== OTP Cipher %s ===\n\n", get_version()); while (1) { show_main_menu(); char input[10]; if (fgets(input, sizeof(input), stdin)) { char choice = toupper(input[0]); switch (choice) { case 'G': handle_generate_menu(); break; case 'E': handle_encrypt_menu(); break; case 'D': handle_decrypt_menu(); break; case 'L': list_available_pads(); break; case 'S': { printf("Enter pad checksum (or prefix): "); char input[MAX_HASH_LENGTH]; if (fgets(input, sizeof(input), stdin)) { input[strcspn(input, "\n")] = 0; char* chksum = find_pad_by_prefix(input); if (chksum) { show_pad_info(chksum); free(chksum); } } break; } case 'X': printf("Goodbye!\n"); return 0; default: printf("Invalid option. Please select G, E, D, L, S, or X.\n"); continue; } } else { printf("Error reading input. Please try again.\n"); continue; } printf("\n"); } } int command_line_mode(int argc, char* argv[]) { 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_with_entropy(size, 1, 0); // No keyboard entropy for command line } else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) { if (argc < 3 || argc > 4) { printf("Usage: %s encrypt|-e [text_to_encrypt]\n", argv[0]); return 1; } // Pass text if provided, otherwise NULL for interactive mode const char* text = (argc == 4) ? argv[3] : NULL; return encrypt_text(argv[2], text); } else if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) { if (argc == 2) { // 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 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 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) { return list_available_pads(); } else { print_usage(argv[0]); return 1; } } void show_main_menu(void) { printf("=== Main Menu ===\n"); printf("\033[4mG\033[0menerate new pad\n"); printf("\033[4mE\033[0mncrypt message\n"); printf("\033[4mD\033[0mecrypt message\n"); printf("\033[4mL\033[0mist available pads\n"); printf("\033[4mS\033[0mhow pad information\n"); printf("E\033[4mx\033[0mit\n"); 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; } // Ask about keyboard entropy printf("\nAdd keyboard entropy for enhanced security? (y/N): "); char entropy_choice[10]; int use_keyboard_entropy = 0; if (fgets(entropy_choice, sizeof(entropy_choice), stdin)) { if (entropy_choice[0] == 'y' || entropy_choice[0] == 'Y') { use_keyboard_entropy = 1; } } double size_gb = (double)size / (1024.0 * 1024.0 * 1024.0); if (use_keyboard_entropy) { printf("Generating %.2f GB pad with keyboard entropy...\n", size_gb); } else { printf("Generating %.2f GB pad...\n", size_gb); } return generate_pad_with_entropy(size, 1, use_keyboard_entropy); } int handle_encrypt_menu(void) { printf("\n=== Encrypt Message ===\n"); int pad_count = list_available_pads(); if (pad_count == 0) { printf("No pads available. Generate a pad first.\n"); return 1; } printf("\nEnter pad selection (number, chksum, or prefix): "); 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; return encrypt_text(input, NULL); // NULL for interactive mode } int handle_decrypt_menu(void) { printf("\n=== Decrypt Message ===\n"); return decrypt_text(NULL, NULL); // No pad selection needed - chksum comes from message } 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(PADS_DIR); if (!dir) return NULL; struct dirent* entry; char* matches[100]; // Store up to 100 matches int match_count = 0; // Check if it's a number (for interactive menu selection) char* endptr; int selection = strtol(prefix, &endptr, 10); if (*endptr == '\0' && selection > 0) { // It's a number, find the nth pad int current = 0; rewinddir(dir); while ((entry = readdir(dir)) != NULL && match_count == 0) { if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { // 64 char chksum + ".pad" current++; if (current == selection) { matches[match_count] = malloc(65); strncpy(matches[match_count], entry->d_name, 64); matches[match_count][64] = '\0'; match_count = 1; } } } } else { // Find pads that start with the prefix size_t prefix_len = strlen(prefix); while ((entry = readdir(dir)) != NULL && match_count < 100) { if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { 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++; } } } } closedir(dir); if (match_count == 0) { printf("No pads found matching '%s'\n", prefix); printf("Available pads:\n"); list_available_pads(); 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("%d. %.16s...\n", i + 1, matches[i]); if (i > 0) free(matches[i]); } printf("Please be more specific.\n"); char* result = matches[0]; for (int i = 1; i < match_count; i++) { free(matches[i]); } return result; } } int list_available_pads(void) { DIR* dir = opendir(PADS_DIR); if (!dir) { printf("Error: Cannot open pads directory\n"); return 0; } struct dirent* entry; int count = 0; printf("Available pads:\n"); printf("%-4s %-20s %-12s %-12s %-8s\n", "No.", "ChkSum (first 16 chars)", "Size", "Used", "% Used"); printf("%-4s %-20s %-12s %-12s %-8s\n", "---", "-------------------", "----------", "----------", "------"); while ((entry = readdir(dir)) != NULL) { if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { count++; char chksum[65]; strncpy(chksum, entry->d_name, 64); chksum[64] = '\0'; // Get pad file size char full_path[300]; // Increased buffer size to accommodate longer paths snprintf(full_path, sizeof(full_path), "%s/%s", 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(chksum, &used_bytes); // Format sizes char size_str[32], used_str[32]; // Format total size if (st.st_size < 1024) { snprintf(size_str, sizeof(size_str), "%luB", st.st_size); } else if (st.st_size < 1024 * 1024) { snprintf(size_str, sizeof(size_str), "%.1fKB", (double)st.st_size / 1024.0); } else if (st.st_size < 1024 * 1024 * 1024) { snprintf(size_str, sizeof(size_str), "%.1fMB", (double)st.st_size / (1024.0 * 1024.0)); } else { snprintf(size_str, sizeof(size_str), "%.2fGB", (double)st.st_size / (1024.0 * 1024.0 * 1024.0)); } // Format used size if (used_bytes < 1024) { snprintf(used_str, sizeof(used_str), "%luB", used_bytes); } else if (used_bytes < 1024 * 1024) { snprintf(used_str, sizeof(used_str), "%.1fKB", (double)used_bytes / 1024.0); } else if (used_bytes < 1024 * 1024 * 1024) { snprintf(used_str, sizeof(used_str), "%.1fMB", (double)used_bytes / (1024.0 * 1024.0)); } else { snprintf(used_str, sizeof(used_str), "%.2fGB", (double)used_bytes / (1024.0 * 1024.0 * 1024.0)); } // Calculate percentage double percentage = (double)used_bytes / st.st_size * 100.0; printf("%-4d %-20.16s %-12s %-12s %.1f%%\n", count, chksum, size_str, used_str, percentage); } } } closedir(dir); if (count == 0) { printf("No pads found.\n"); } return count; } 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; } int get_user_choice(int min, int max) { char input[64]; int choice; while (1) { if (fgets(input, sizeof(input), stdin)) { choice = atoi(input); if (choice >= min && choice <= max) { return choice; } } printf("Please enter a number between %d and %d: ", min, max); } } 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) { char temp_filename[32]; char pad_filename[MAX_HASH_LENGTH + 10]; char state_filename[MAX_HASH_LENGTH + 10]; 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; } // Rename file to its chksum snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum_hex); snprintf(state_filename, sizeof(state_filename), "%s.state", chksum_hex); if (rename(temp_filename, pad_filename) != 0) { printf("Error: Cannot rename pad file to chksum-based name\n"); unlink(temp_filename); return 1; } // Set pad file to read-only if (chmod(pad_filename, S_IRUSR) != 0) { printf("Warning: Cannot set pad file to read-only\n"); } // Initialize state file with offset 0 if (write_state_offset(chksum_hex, 0) != 0) { printf("Error: Failed to create state file\n"); unlink(pad_filename); return 1; } double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); printf("Generated pad: %s (%.2f GB)\n", pad_filename, size_gb); printf("Pad chksum: %s\n", chksum_hex); printf("State file: %s\n", state_filename); printf("Pad file set to read-only\n"); return 0; } int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use_keyboard_entropy) { 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; } // Setup keyboard entropy collection if requested struct termios original_termios; unsigned char* entropy_buffer = NULL; size_t entropy_collected = 0; int terminal_setup = 0; if (use_keyboard_entropy) { entropy_buffer = malloc(MAX_ENTROPY_BUFFER); if (!entropy_buffer) { printf("Error: Cannot allocate entropy buffer\n"); fclose(urandom); fclose(pad_file); unlink(temp_filename); return 1; } if (setup_raw_terminal(&original_termios) == 0) { terminal_setup = 1; printf("Type random keys to add entropy (optional):\n"); } else { printf("Warning: Cannot setup terminal for keyboard entropy collection\n"); use_keyboard_entropy = 0; free(entropy_buffer); entropy_buffer = NULL; } } unsigned char urandom_buffer[64 * 1024]; // 64KB buffer unsigned char output_buffer[64 * 1024]; uint64_t bytes_written = 0; if (display_progress) { printf("Generating pad...\n"); if (use_keyboard_entropy) { printf("(Keyboard entropy: collecting...)\n"); } } while (bytes_written < size_bytes) { uint64_t chunk_size = sizeof(urandom_buffer); if (size_bytes - bytes_written < chunk_size) { chunk_size = size_bytes - bytes_written; } // Read from /dev/urandom if (fread(urandom_buffer, 1, (size_t)chunk_size, urandom) != (size_t)chunk_size) { printf("Error: Failed to read from /dev/urandom\n"); if (terminal_setup) restore_terminal(&original_termios); if (entropy_buffer) free(entropy_buffer); fclose(urandom); fclose(pad_file); unlink(temp_filename); return 1; } if (use_keyboard_entropy && terminal_setup) { // Collect available keyboard entropy size_t chunk_entropy = 0; collect_keyboard_entropy(entropy_buffer + entropy_collected, MAX_ENTROPY_BUFFER - entropy_collected, &chunk_entropy); entropy_collected += chunk_entropy; if (entropy_collected > 512) { // Have enough entropy to mix // Copy urandom data to output buffer memcpy(output_buffer, urandom_buffer, chunk_size); // Simple XOR mixing with keyboard entropy simple_entropy_mix(output_buffer, chunk_size, entropy_buffer, entropy_collected); // Reset entropy buffer for next chunk entropy_collected = 0; } else { // Not enough entropy yet, use urandom only memcpy(output_buffer, urandom_buffer, chunk_size); } } else { // No keyboard entropy, use urandom directly memcpy(output_buffer, urandom_buffer, chunk_size); } if (fwrite(output_buffer, 1, (size_t)chunk_size, pad_file) != (size_t)chunk_size) { printf("Error: Failed to write to pad file\n"); if (terminal_setup) restore_terminal(&original_termios); if (entropy_buffer) free(entropy_buffer); fclose(urandom); fclose(pad_file); unlink(temp_filename); return 1; } bytes_written += chunk_size; if (display_progress && bytes_written % PROGRESS_UPDATE_INTERVAL == 0) { printf("\rProgress: %.1f%% ", (double)bytes_written / size_bytes * 100.0); if (use_keyboard_entropy && terminal_setup) { printf("(keyboard entropy: %.1fKB) ", (double)entropy_collected / 1024.0); } fflush(stdout); } } if (terminal_setup) { restore_terminal(&original_termios); } if (entropy_buffer) { free(entropy_buffer); } if (display_progress) { printf("\rProgress: 100.0%%"); if (use_keyboard_entropy) { printf(" (keyboard entropy: MIXED)"); } 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); if (rename(temp_filename, pad_path) != 0) { printf("Error: Cannot move pad file to pads directory\n"); unlink(temp_filename); return 1; } // Set pad file to read-only if (chmod(pad_path, S_IRUSR) != 0) { printf("Warning: Cannot set pad file to read-only\n"); } // Initialize state file with offset 32 (first 32 bytes used 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); if (use_keyboard_entropy) { printf("Enhanced with keyboard entropy!\n"); } printf("Pad file set to read-only\n"); return 0; } 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) 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); // XOR encrypt the input unsigned char* ciphertext = malloc(input_len); for (size_t i = 0; i < input_len; i++) { ciphertext[i] = text_buffer[i] ^ pad_data[i]; } // Encode as base64 char* base64_cipher = custom_base64_encode(ciphertext, input_len); // Update state offset if (write_state_offset(pad_chksum, current_offset + input_len) != 0) { printf("Warning: Failed to update state file\n"); } // Output in ASCII armor format printf("\n\n-----BEGIN OTP MESSAGE-----\n"); printf("Version: %s\n", get_version()); printf("Pad-ChkSum: %s\n", chksum_hex); printf("Pad-Offset: %lu\n", current_offset); printf("\n"); // Print base64 data in 64-character lines int b64_len = strlen(base64_cipher); for (int i = 0; i < b64_len; i += 64) { printf("%.64s\n", base64_cipher + i); } printf("-----END OTP MESSAGE-----\n\n\n"); // Cleanup free(pad_data); free(ciphertext); free(base64_cipher); free(pad_chksum); return 0; } int decrypt_text(const char* pad_identifier, const char* encrypted_message) { // For command line mode, pad_identifier is ignored - we'll get the chksum from the message (void)pad_identifier; // Suppress unused parameter warning char line[MAX_LINE_LENGTH]; char stored_chksum[MAX_HASH_LENGTH]; char current_chksum[MAX_HASH_LENGTH]; uint64_t pad_offset; char base64_data[MAX_INPUT_SIZE * 2] = {0}; int in_data_section = 0; if (encrypted_message != NULL) { // Parse provided encrypted message char *message_copy = strdup(encrypted_message); char *line_ptr = strtok(message_copy, "\n"); int found_begin = 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(stored_chksum, line_ptr + 12, 64); stored_chksum[64] = '\0'; } else if (strncmp(line_ptr, "Pad-Offset: ", 12) == 0) { pad_offset = strtoull(line_ptr + 12, NULL, 10); } else if (strlen(line_ptr) == 0) { in_data_section = 1; } else if (in_data_section) { strncat(base64_data, line_ptr, sizeof(base64_data) - strlen(base64_data) - 1); } } line_ptr = strtok(NULL, "\n"); } free(message_copy); if (!found_begin) { printf("Error: Invalid message format - missing BEGIN header\n"); return 1; } } else { // Interactive mode - read from stdin printf("Enter encrypted message (paste the full ASCII armor block):\n"); // Read the ASCII armor format int found_begin = 0; while (fgets(line, sizeof(line), stdin)) { line[strcspn(line, "\n")] = 0; if (strcmp(line, "-----BEGIN OTP MESSAGE-----") == 0) { found_begin = 1; continue; } if (strcmp(line, "-----END OTP MESSAGE-----") == 0) { break; } if (!found_begin) continue; if (strncmp(line, "Pad-ChkSum: ", 12) == 0) { strncpy(stored_chksum, line + 12, 64); stored_chksum[64] = '\0'; } else if (strncmp(line, "Pad-Offset: ", 12) == 0) { pad_offset = strtoull(line + 12, NULL, 10); } else if (strlen(line) == 0) { in_data_section = 1; } else if (in_data_section) { strncat(base64_data, line, sizeof(base64_data) - strlen(base64_data) - 1); } } if (!found_begin) { printf("Error: Invalid message format - missing BEGIN header\n"); return 1; } } // Now we have the pad chksum from the message, construct filename char pad_path[MAX_HASH_LENGTH + 20]; char state_path[MAX_HASH_LENGTH + 20]; get_pad_path(stored_chksum, pad_path, state_path); // Check if we have this pad if (access(pad_path, R_OK) != 0) { printf("Error: Required pad not found: %s\n", stored_chksum); printf("Available pads:\n"); list_available_pads(); return 1; } // Verify pad integrity if (calculate_checksum(pad_path, current_chksum) != 0) { printf("Error: Cannot calculate current pad checksum\n"); return 1; } if (strcmp(stored_chksum, current_chksum) != 0) { printf("Warning: Pad integrity check failed!\n"); printf("Expected: %s\n", stored_chksum); printf("Current: %s\n", current_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 { printf("Pad integrity: VERIFIED\n"); } // Decode base64 int ciphertext_len; unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); if (!ciphertext) { printf("Error: Invalid base64 data\n"); return 1; } // Read pad data at specified offset FILE* pad_file = fopen(pad_path, "rb"); if (!pad_file) { printf("Error: Cannot open pad file %s\n", pad_path); free(ciphertext); return 1; } if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { printf("Error: Cannot seek to offset %lu in pad file\n", pad_offset); free(ciphertext); fclose(pad_file); return 1; } unsigned char* pad_data = malloc(ciphertext_len); if (fread(pad_data, 1, ciphertext_len, pad_file) != (size_t)ciphertext_len) { printf("Error: Cannot read pad data\n"); free(ciphertext); free(pad_data); fclose(pad_file); return 1; } fclose(pad_file); // XOR decrypt char* plaintext = malloc(ciphertext_len + 1); for (int i = 0; i < ciphertext_len; i++) { plaintext[i] = ciphertext[i] ^ pad_data[i]; } plaintext[ciphertext_len] = '\0'; printf("Decrypted: %s\n", plaintext); // Cleanup free(ciphertext); free(pad_data); free(plaintext); return 0; } 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 char default_output[512]; if (output_file == NULL) { 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); } 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; } // XOR encrypt for (uint64_t i = 0; i < chunk_size; i++) { encrypted_data[bytes_processed + i] = buffer[i] ^ pad_buffer[i]; } 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; } // Encode as base64 char* base64_data = custom_base64_encode(encrypted_data, file_size); // Write ASCII armor fprintf(output_fp, "-----BEGIN OTP MESSAGE-----\n"); fprintf(output_fp, "Version: %s\n", get_version()); fprintf(output_fp, "Pad-ChkSum: %s\n", chksum_hex); fprintf(output_fp, "Pad-Offset: %lu\n", current_offset); fprintf(output_fp, "\n"); // Write base64 data in 64-character lines int b64_len = strlen(base64_data); for (int i = 0; i < b64_len; i += 64) { fprintf(output_fp, "%.64s\n", base64_data + i); } fprintf(output_fp, "-----END OTP MESSAGE-----\n"); fclose(output_fp); free(base64_data); } 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); // Original filename length and name const char* base_filename = strrchr(input_file, '/'); if (base_filename) { base_filename++; // Skip the '/' } else { base_filename = input_file; } uint16_t filename_len = strlen(base_filename); fwrite(&filename_len, sizeof(uint16_t), 1, output_fp); fwrite(base_filename, 1, filename_len, 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; uint16_t filename_len; 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(&filename_len, sizeof(uint16_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; } // Read original filename char* original_filename = malloc(filename_len + 1); if (fread(original_filename, 1, filename_len, input_fp) != filename_len) { printf("Error: Cannot read original filename\n"); free(original_filename); fclose(input_fp); return 1; } original_filename[filename_len] = '\0'; // Read remaining header if (fread(&file_mode, sizeof(uint32_t), 1, input_fp) != 1 || fread(&file_size, sizeof(uint64_t), 1, input_fp) != 1) { printf("Error: Cannot read file metadata\n"); free(original_filename); 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("Original filename: %s\n", original_filename); 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"); list_available_pads(); free(original_filename); fclose(input_fp); return 1; } // Determine output filename char default_output[512]; if (output_file == NULL) { strncpy(default_output, original_filename, sizeof(default_output) - 1); default_output[sizeof(default_output) - 1] = '\0'; 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(original_filename); 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(original_filename); 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(original_filename); 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(original_filename); free(encrypted_data); free(pad_data); fclose(pad_file); return 1; } fclose(pad_file); // XOR decrypt for (uint64_t i = 0; i < file_size; i++) { encrypted_data[i] ^= pad_data[i]; } // Write decrypted file FILE* output_fp = fopen(output_file, "wb"); if (!output_fp) { printf("Error: Cannot create output file %s\n", output_file); free(original_filename); 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(original_filename); 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(original_filename); free(encrypted_data); free(pad_data); return 0; } int decrypt_ascii_file(const char* input_file, const char* output_file) { FILE* input_fp = fopen(input_file, "r"); if (!input_fp) { printf("Error: Cannot open input file %s\n", input_file); return 1; } // Read the entire ASCII armored content char line[MAX_LINE_LENGTH]; char stored_chksum[MAX_HASH_LENGTH]; uint64_t pad_offset; char base64_data[MAX_INPUT_SIZE * 8] = {0}; // Larger buffer for files int in_data_section = 0; int found_begin = 0; while (fgets(line, sizeof(line), input_fp)) { line[strcspn(line, "\n\r")] = 0; // Remove newlines if (strcmp(line, "-----BEGIN OTP MESSAGE-----") == 0) { found_begin = 1; continue; } if (strcmp(line, "-----END OTP MESSAGE-----") == 0) { break; } if (!found_begin) continue; if (strncmp(line, "Pad-ChkSum: ", 12) == 0) { strncpy(stored_chksum, line + 12, 64); stored_chksum[64] = '\0'; } else if (strncmp(line, "Pad-Offset: ", 12) == 0) { pad_offset = strtoull(line + 12, NULL, 10); } else if (strlen(line) == 0) { in_data_section = 1; } else if (in_data_section) { strncat(base64_data, line, sizeof(base64_data) - strlen(base64_data) - 1); } } fclose(input_fp); if (!found_begin) { printf("Error: Invalid ASCII armored format\n"); return 1; } printf("Decrypting ASCII armored file...\n"); // Check if we have the required pad 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) { printf("Error: Required pad not found: %s\n", stored_chksum); printf("Available pads:\n"); list_available_pads(); return 1; } // Decode base64 int ciphertext_len; unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); if (!ciphertext) { printf("Error: Invalid base64 data\n"); return 1; } // Determine output filename char default_output[512]; if (output_file == NULL) { // Remove .otp.asc extension if present strncpy(default_output, input_file, sizeof(default_output) - 1); default_output[sizeof(default_output) - 1] = '\0'; char* ext = strstr(default_output, ".otp.asc"); if (ext) { *ext = '\0'; } else { // Just add .decrypted suffix strncat(default_output, ".decrypted", sizeof(default_output) - strlen(default_output) - 1); } output_file = default_output; } // Open pad file and decrypt FILE* pad_file = fopen(pad_path, "rb"); if (!pad_file) { printf("Error: Cannot open pad file\n"); free(ciphertext); return 1; } if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { printf("Error: Cannot seek to offset in pad file\n"); free(ciphertext); fclose(pad_file); return 1; } unsigned char* pad_data = malloc(ciphertext_len); if (fread(pad_data, 1, ciphertext_len, pad_file) != (size_t)ciphertext_len) { printf("Error: Cannot read pad data\n"); free(ciphertext); free(pad_data); fclose(pad_file); return 1; } fclose(pad_file); // XOR decrypt for (int i = 0; i < ciphertext_len; i++) { ciphertext[i] ^= pad_data[i]; } // Write decrypted file 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); printf("File decrypted successfully: %s\n", output_file); printf("Note: ASCII format does not preserve original filename/permissions\n"); // Cleanup free(ciphertext); free(pad_data); return 0; } int read_state_offset(const char* pad_chksum, uint64_t* offset) { char state_filename[MAX_HASH_LENGTH + 20]; snprintf(state_filename, sizeof(state_filename), "%s/%s.state", 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[MAX_HASH_LENGTH + 20]; snprintf(state_filename, sizeof(state_filename), "%s/%s.state", 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; } 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; } // 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); } int collect_keyboard_entropy(unsigned char* entropy_buffer, size_t max_size, size_t* collected) { struct timespec timestamp; unsigned char entropy_block[16]; uint32_t sequence_counter = 0; char key; *collected = 0; while (*collected < max_size - 16) { if (read(STDIN_FILENO, &key, 1) == 1) { clock_gettime(CLOCK_MONOTONIC, ×tamp); // Create entropy block: [key][timestamp][sequence_counter] 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, 3); // Add to entropy buffer memcpy(entropy_buffer + *collected, entropy_block, 16); *collected += 16; sequence_counter++; } else { // No key available, add some timing entropy clock_gettime(CLOCK_MONOTONIC, ×tamp); if (*collected + 12 < max_size) { memcpy(entropy_buffer + *collected, ×tamp, 12); *collected += 12; } usleep(1000); // 1ms delay } } return 0; } // Directory management functions int ensure_pads_directory(void) { struct stat st = {0}; if (stat(PADS_DIR, &st) == -1) { if (mkdir(PADS_DIR, 0755) != 0) { return 1; } } return 0; } void get_pad_path(const char* chksum, char* pad_path, char* state_path) { snprintf(pad_path, MAX_HASH_LENGTH + 20, "%s/%s.pad", PADS_DIR, chksum); snprintf(state_path, MAX_HASH_LENGTH + 20, "%s/%s.state", PADS_DIR, chksum); } // Custom XOR checksum function void xor_checksum_256(const unsigned char* data, size_t len, unsigned char checksum[32]) { memset(checksum, 0, 32); for (size_t i = 0; i < len; i++) { unsigned char bucket = i % 32; checksum[bucket] ^= data[i] ^ ((i >> 8) & 0xFF) ^ ((i >> 16) & 0xFF) ^ ((i >> 24) & 0xFF); } } // 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; } // Simple keyboard entropy mixing function void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size, const unsigned char* entropy_data, size_t entropy_size) { if (!entropy_data || entropy_size == 0) return; for (size_t i = 0; i < buffer_size; i++) { // XOR with entropy data in a rotating pattern unsigned char entropy_byte = entropy_data[i % entropy_size]; // Mix position information entropy_byte ^= (i & 0xFF) ^ ((i >> 8) & 0xFF); urandom_buffer[i] ^= entropy_byte; } } void print_usage(const char* program_name) { printf("OTP Cipher - One Time Pad Implementation %s\n", get_version()); printf("%s\n", get_build_info()); printf("Usage:\n"); printf(" %s - Interactive mode\n", program_name); printf(" %s generate|-g - Generate new pad\n", program_name); printf(" %s encrypt|-e [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, prefix, or number from list\n"); }