|
|
|
@@ -47,6 +47,12 @@ int main(int argc, char* argv[]);
|
|
|
|
|
int interactive_mode(void);
|
|
|
|
|
int command_line_mode(int argc, char* argv[]);
|
|
|
|
|
|
|
|
|
|
// Editor and file manager functions
|
|
|
|
|
char* get_preferred_editor(void);
|
|
|
|
|
char* get_preferred_file_manager(void);
|
|
|
|
|
int launch_text_editor(const char* initial_content, char* result_buffer, size_t buffer_size);
|
|
|
|
|
int launch_file_manager(const char* start_directory, char* selected_file, size_t buffer_size);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
@@ -89,6 +95,9 @@ void show_main_menu(void);
|
|
|
|
|
int handle_generate_menu(void);
|
|
|
|
|
int handle_encrypt_menu(void);
|
|
|
|
|
int handle_decrypt_menu(void);
|
|
|
|
|
int handle_text_encrypt(void);
|
|
|
|
|
int handle_file_encrypt(void);
|
|
|
|
|
int handle_smart_decrypt(void);
|
|
|
|
|
|
|
|
|
|
void print_usage(const char* program_name);
|
|
|
|
|
|
|
|
|
@@ -101,7 +110,7 @@ int main(int argc, char* argv[]) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int interactive_mode(void) {
|
|
|
|
|
printf("=== OTP Cipher %s ===\n\n", get_version());
|
|
|
|
|
printf("\n\n\n\n=== OTP Cipher %s ===\n\n", get_version());
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
show_main_menu();
|
|
|
|
@@ -110,14 +119,17 @@ int interactive_mode(void) {
|
|
|
|
|
char choice = toupper(input[0]);
|
|
|
|
|
|
|
|
|
|
switch (choice) {
|
|
|
|
|
case 'G':
|
|
|
|
|
handle_generate_menu();
|
|
|
|
|
case 'T':
|
|
|
|
|
handle_text_encrypt();
|
|
|
|
|
break;
|
|
|
|
|
case 'E':
|
|
|
|
|
handle_encrypt_menu();
|
|
|
|
|
case 'F':
|
|
|
|
|
handle_file_encrypt();
|
|
|
|
|
break;
|
|
|
|
|
case 'D':
|
|
|
|
|
handle_decrypt_menu();
|
|
|
|
|
handle_smart_decrypt();
|
|
|
|
|
break;
|
|
|
|
|
case 'G':
|
|
|
|
|
handle_generate_menu();
|
|
|
|
|
break;
|
|
|
|
|
case 'L':
|
|
|
|
|
list_available_pads();
|
|
|
|
@@ -136,10 +148,11 @@ int interactive_mode(void) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'X':
|
|
|
|
|
case 'Q':
|
|
|
|
|
printf("Goodbye!\n");
|
|
|
|
|
return 0;
|
|
|
|
|
default:
|
|
|
|
|
printf("Invalid option. Please select G, E, D, L, S, or X.\n");
|
|
|
|
|
printf("Invalid option. Please select T, F, D, G, L, S, or X.\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@@ -151,6 +164,14 @@ int interactive_mode(void) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 <size>\n", argv[0]);
|
|
|
|
@@ -247,10 +268,11 @@ int command_line_mode(int argc, char* argv[]) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void show_main_menu(void) {
|
|
|
|
|
printf("=== Main Menu ===\n");
|
|
|
|
|
printf("=== Main Menu ===\n\n");
|
|
|
|
|
printf("\033[4mT\033[0mext encrypt\n");
|
|
|
|
|
printf("\033[4mF\033[0mile encrypt\n");
|
|
|
|
|
printf("\033[4mD\033[0mecrypt\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");
|
|
|
|
@@ -297,7 +319,7 @@ int handle_generate_menu(void) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int handle_encrypt_menu(void) {
|
|
|
|
|
printf("\n=== Encrypt Message ===\n");
|
|
|
|
|
printf("\n=== Encrypt Data ===\n");
|
|
|
|
|
|
|
|
|
|
int pad_count = list_available_pads();
|
|
|
|
|
if (pad_count == 0) {
|
|
|
|
@@ -305,20 +327,210 @@ int handle_encrypt_menu(void) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("\nEnter pad selection (number, chksum, or prefix): ");
|
|
|
|
|
char input[MAX_HASH_LENGTH];
|
|
|
|
|
if (!fgets(input, sizeof(input), stdin)) {
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input[strcspn(input, "\n")] = 0;
|
|
|
|
|
return encrypt_text(input, NULL); // NULL for interactive mode
|
|
|
|
|
int choice = atoi(choice_input);
|
|
|
|
|
|
|
|
|
|
if (choice == 1) {
|
|
|
|
|
// Text encryption
|
|
|
|
|
printf("\nPad selection options:\n");
|
|
|
|
|
printf("1. Select from numbered list\n");
|
|
|
|
|
printf("2. Enter checksum/prefix manually\n");
|
|
|
|
|
printf("3. Browse pad files\n");
|
|
|
|
|
printf("Enter choice (1-3): ");
|
|
|
|
|
|
|
|
|
|
char pad_choice[10];
|
|
|
|
|
if (!fgets(pad_choice, sizeof(pad_choice), stdin)) {
|
|
|
|
|
printf("Error: Failed to read input\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char input[MAX_HASH_LENGTH];
|
|
|
|
|
if (atoi(pad_choice) == 3) {
|
|
|
|
|
// Use file manager to browse pads directory
|
|
|
|
|
char selected_pad[512];
|
|
|
|
|
if (launch_file_manager("pads", selected_pad, sizeof(selected_pad)) == 0) {
|
|
|
|
|
// Extract checksum from selected .pad file
|
|
|
|
|
char* filename = strrchr(selected_pad, '/');
|
|
|
|
|
if (!filename) filename = selected_pad;
|
|
|
|
|
else filename++; // Skip the '/'
|
|
|
|
|
|
|
|
|
|
// Remove .pad extension to get checksum
|
|
|
|
|
if (strlen(filename) == 68 && strstr(filename, ".pad")) {
|
|
|
|
|
strncpy(input, filename, 64);
|
|
|
|
|
input[64] = '\0';
|
|
|
|
|
} else {
|
|
|
|
|
printf("Invalid pad file selected.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
printf("Falling back to manual pad selection.\n");
|
|
|
|
|
printf("Enter pad selection (number, chksum, or prefix): ");
|
|
|
|
|
if (!fgets(input, sizeof(input), stdin)) {
|
|
|
|
|
printf("Error: Failed to read input\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
input[strcspn(input, "\n")] = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Manual pad selection
|
|
|
|
|
printf("Enter pad selection (number, chksum, or prefix): ");
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("\nEnter pad selection (number, chksum, or prefix): ");
|
|
|
|
|
char pad_input[MAX_HASH_LENGTH];
|
|
|
|
|
if (!fgets(pad_input, sizeof(pad_input), stdin)) {
|
|
|
|
|
printf("Error: Failed to read input\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
pad_input[strcspn(pad_input, "\n")] = 0;
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
// Ask for custom output filename (optional)
|
|
|
|
|
printf("\nEnter output filename (or press Enter for default): ");
|
|
|
|
|
char output_file[512];
|
|
|
|
|
if (!fgets(output_file, sizeof(output_file), stdin)) {
|
|
|
|
|
printf("Error: Failed to read input\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
output_file[strcspn(output_file, "\n")] = 0;
|
|
|
|
|
|
|
|
|
|
const char* output_filename = (strlen(output_file) > 0) ? output_file : NULL;
|
|
|
|
|
|
|
|
|
|
return encrypt_file(pad_input, input_file, output_filename, ascii_armor);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
printf("Invalid choice. Please enter 1 or 2.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int handle_decrypt_menu(void) {
|
|
|
|
|
printf("\n=== Decrypt Message ===\n");
|
|
|
|
|
return decrypt_text(NULL, NULL); // No pad selection needed - chksum comes from message
|
|
|
|
|
printf("\n=== Decrypt Data ===\n");
|
|
|
|
|
|
|
|
|
|
// Ask user to choose between text/message and file decryption
|
|
|
|
|
printf("\nSelect decryption type:\n");
|
|
|
|
|
printf("1. Text message (ASCII armored)\n");
|
|
|
|
|
printf("2. File (.otp or .otp.asc)\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/message decryption - interactive input
|
|
|
|
|
return decrypt_text(NULL, NULL); // No pad selection needed - chksum comes from message
|
|
|
|
|
}
|
|
|
|
|
else if (choice == 2) {
|
|
|
|
|
// File decryption
|
|
|
|
|
printf("\nEnter encrypted file path (.otp or .otp.asc): ");
|
|
|
|
|
char input_file[512];
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ask for custom output filename (optional)
|
|
|
|
|
printf("\nEnter output filename (or press Enter for default): ");
|
|
|
|
|
char output_file[512];
|
|
|
|
|
if (!fgets(output_file, sizeof(output_file), stdin)) {
|
|
|
|
|
printf("Error: Failed to read input\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
output_file[strcspn(output_file, "\n")] = 0;
|
|
|
|
|
|
|
|
|
|
const char* output_filename = (strlen(output_file) > 0) ? output_file : NULL;
|
|
|
|
|
|
|
|
|
|
return decrypt_file(input_file, output_filename);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
printf("Invalid choice. Please enter 1 or 2.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t parse_size_string(const char* size_str) {
|
|
|
|
@@ -374,44 +586,45 @@ char* find_pad_by_prefix(const char* prefix) {
|
|
|
|
|
char* matches[100]; // Store up to 100 matches
|
|
|
|
|
int match_count = 0;
|
|
|
|
|
|
|
|
|
|
// Check if it's a number (for interactive menu selection)
|
|
|
|
|
// Only treat as number if it's a single digit (1-9) to avoid conflicts with hex prefixes
|
|
|
|
|
char* endptr;
|
|
|
|
|
int selection = strtol(prefix, &endptr, 10);
|
|
|
|
|
if (*endptr == '\0' && selection > 0 && selection <= 9 && strlen(prefix) == 1) {
|
|
|
|
|
// It's a number, find the nth pad
|
|
|
|
|
int current = 0;
|
|
|
|
|
rewinddir(dir);
|
|
|
|
|
while ((entry = readdir(dir)) != NULL && match_count == 0) {
|
|
|
|
|
// 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"
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// 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++;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Find pads that start with the prefix
|
|
|
|
|
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++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no hex prefix matches and it looks like a small number, try number selection
|
|
|
|
|
if (match_count == 0) {
|
|
|
|
|
char* endptr;
|
|
|
|
|
int selection = strtol(prefix, &endptr, 10);
|
|
|
|
|
if (*endptr == '\0' && selection > 0 && selection <= 100) {
|
|
|
|
|
// It's a number, find the nth pad
|
|
|
|
|
int current = 0;
|
|
|
|
|
rewinddir(dir);
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
|
// 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"
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -944,19 +1157,53 @@ int encrypt_text(const char* pad_identifier, const char* input_text) {
|
|
|
|
|
text_buffer[sizeof(text_buffer) - 1] = '\0';
|
|
|
|
|
} else {
|
|
|
|
|
// Get input text from user (interactive mode)
|
|
|
|
|
printf("Enter text to encrypt: ");
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
printf("\nText input options:\n");
|
|
|
|
|
printf("1. Type text directly\n");
|
|
|
|
|
printf("2. Use text editor\n");
|
|
|
|
|
printf("Enter choice (1-2): ");
|
|
|
|
|
|
|
|
|
|
if (fgets(text_buffer, sizeof(text_buffer), stdin) == NULL) {
|
|
|
|
|
char input_choice[10];
|
|
|
|
|
if (!fgets(input_choice, sizeof(input_choice), stdin)) {
|
|
|
|
|
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';
|
|
|
|
|
if (atoi(input_choice) == 2) {
|
|
|
|
|
// Use text editor
|
|
|
|
|
if (launch_text_editor(NULL, text_buffer, sizeof(text_buffer)) != 0) {
|
|
|
|
|
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
|
|
|
|
|
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';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -2050,6 +2297,342 @@ void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Editor and file manager implementations
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// List available pads and get selection
|
|
|
|
|
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, checksum, or prefix): ");
|
|
|
|
|
char pad_input[MAX_HASH_LENGTH];
|
|
|
|
|
if (!fgets(pad_input, sizeof(pad_input), stdin)) {
|
|
|
|
|
printf("Error: Failed to read pad selection\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
pad_input[strcspn(pad_input, "\n")] = 0;
|
|
|
|
|
|
|
|
|
|
return encrypt_text(pad_input, text_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// List available pads
|
|
|
|
|
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, checksum, or prefix): ");
|
|
|
|
|
char pad_input[MAX_HASH_LENGTH];
|
|
|
|
|
if (!fgets(pad_input, sizeof(pad_input), stdin)) {
|
|
|
|
|
printf("Error: Failed to read pad selection\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
pad_input[strcspn(pad_input, "\n")] = 0;
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ask for output filename with pre-filled default
|
|
|
|
|
printf("\nOutput filename [%s]: ", default_output);
|
|
|
|
|
char output_file[512];
|
|
|
|
|
if (!fgets(output_file, sizeof(output_file), stdin)) {
|
|
|
|
|
printf("Error: Failed to read input\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
output_file[strcspn(output_file, "\n")] = 0;
|
|
|
|
|
|
|
|
|
|
// Use default if user just pressed Enter
|
|
|
|
|
const char* output_filename = (strlen(output_file) > 0) ? output_file : default_output;
|
|
|
|
|
|
|
|
|
|
return encrypt_file(pad_input, input_file, output_filename, ascii_armor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int handle_smart_decrypt(void) {
|
|
|
|
|
printf("\n=== Smart Decrypt ===\n");
|
|
|
|
|
printf("Enter encrypted data (paste ASCII armor) 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
|
|
|
|
|
char selected_file[512];
|
|
|
|
|
if (launch_file_manager(".", selected_file, sizeof(selected_file)) != 0) {
|
|
|
|
|
printf("Error: Could not launch file manager\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decrypt selected file
|
|
|
|
|
return decrypt_file(selected_file, NULL);
|
|
|
|
|
} 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) {
|
|
|
|
|
return decrypt_file(input_line, NULL);
|
|
|
|
|
} else {
|
|
|
|
|
printf("Input not recognized as ASCII armor or valid file path.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void print_usage(const char* program_name) {
|
|
|
|
|
printf("OTP Cipher - One Time Pad Implementation %s\n", get_version());
|
|
|
|
|
printf("%s\n", get_build_info());
|
|
|
|
|