Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 12b9884572 | |||
| 83b60b5cc2 | |||
| 2d6546ab83 | |||
| c255185084 | |||
| 24800d69d5 | |||
| 7e50727163 | |||
| f118c23c60 | |||
| b149175f24 | |||
| 206e8042d8 | |||
| 2a5249d93c | |||
| 0e02eaee53 | |||
| e32eb8b2b1 | |||
| 0aecae0c5e | |||
| fa90e0eafd | |||
| 2a10d974b2 | |||
| 7e04896394 |
@@ -2,3 +2,6 @@
|
||||
When building, use build.sh, not make.
|
||||
|
||||
Use it as follows: build.sh -m "useful comment on changes being made"
|
||||
|
||||
When making TUI menus, try to use the first leter of the command and the key to press to execute that command. For example, if the command is "Open file" try to use a keypress of "o" upper or lower case to signal to open the file. Use this instead of number keyed menus when possible. In the command, the letter should be underlined that signifies the command.
|
||||
|
||||
|
||||
10
README.md
10
README.md
@@ -164,11 +164,9 @@ Offset | Size | Field | Description
|
||||
4 | 2 | Version | Format version (currently 1)
|
||||
6 | 32 | Pad Checksum | Binary pad checksum (32 bytes)
|
||||
38 | 8 | Pad Offset | Offset in pad file (uint64_t)
|
||||
46 | 2 | Filename Length | Original filename length (uint16_t)
|
||||
48 | var | Original Filename | Original filename string
|
||||
var | 4 | File Mode | Original file permissions (uint32_t)
|
||||
var | 8 | File Size | Original file size (uint64_t)
|
||||
var | var | Encrypted Data | XOR-encrypted file contents
|
||||
46 | 4 | File Mode | Original file permissions (uint32_t)
|
||||
50 | 8 | File Size | Original file size (uint64_t)
|
||||
58 | var | Encrypted Data | XOR-encrypted file contents
|
||||
```
|
||||
|
||||
### .otp.asc File Format (ASCII Armored)
|
||||
@@ -185,7 +183,7 @@ Pad-Offset: <decimal-offset-value>
|
||||
-----END OTP MESSAGE-----
|
||||
```
|
||||
|
||||
**Note:** ASCII armored files lose original filename and permission metadata.
|
||||
**Note:** ASCII armored files do not preserve original file permissions metadata.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
|
||||
1
decrypted.bin
Normal file
1
decrypted.bin
Normal file
@@ -0,0 +1 @@
|
||||
Hello, this is a test file for encryption!
|
||||
879
otp.c
879
otp.c
@@ -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,12 @@ 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);
|
||||
|
||||
// Enhanced input functions
|
||||
int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size);
|
||||
|
||||
void print_usage(const char* program_name);
|
||||
|
||||
@@ -101,7 +113,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 +122,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 +151,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 +167,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 +271,11 @@ int command_line_mode(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
void show_main_menu(void) {
|
||||
printf("=== Main Menu ===\n");
|
||||
printf("======================== Main Menu ========================\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 +322,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 +330,238 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Generate default output filename and use enhanced input function
|
||||
char default_output[512];
|
||||
snprintf(default_output, sizeof(default_output), "%s.decrypted", input_file);
|
||||
|
||||
// Remove common encrypted extensions to get a better default
|
||||
if (strstr(default_output, ".otp.asc.decrypted")) {
|
||||
// Replace .otp.asc.decrypted with original extension or no extension
|
||||
char* ext_pos = strstr(default_output, ".otp.asc.decrypted");
|
||||
*ext_pos = '\0';
|
||||
} else if (strstr(default_output, ".otp.decrypted")) {
|
||||
// Replace .otp.decrypted with original extension or no extension
|
||||
char* ext_pos = strstr(default_output, ".otp.decrypted");
|
||||
*ext_pos = '\0';
|
||||
}
|
||||
|
||||
char output_file[512];
|
||||
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
|
||||
printf("Error: Failed to read input\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* output_filename = output_file;
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
// Generate smart default output filename and use enhanced input function
|
||||
char default_output[512];
|
||||
strncpy(default_output, input_file, sizeof(default_output) - 1);
|
||||
default_output[sizeof(default_output) - 1] = '\0';
|
||||
|
||||
// Remove common encrypted extensions to get a better default
|
||||
if (strstr(default_output, ".otp.asc")) {
|
||||
// Replace .otp.asc with original extension or no extension
|
||||
char* ext_pos = strstr(default_output, ".otp.asc");
|
||||
*ext_pos = '\0';
|
||||
} else if (strstr(default_output, ".otp")) {
|
||||
// Replace .otp with original extension or no extension
|
||||
char* ext_pos = strstr(default_output, ".otp");
|
||||
*ext_pos = '\0';
|
||||
} else {
|
||||
// No recognized encrypted extension, add .decrypted suffix
|
||||
strncat(default_output, ".decrypted", sizeof(default_output) - strlen(default_output) - 1);
|
||||
}
|
||||
|
||||
char output_file[512];
|
||||
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
|
||||
printf("Error: Failed to read input\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* output_filename = output_file;
|
||||
|
||||
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,31 +617,7 @@ 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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find pads that start with the prefix
|
||||
// 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
|
||||
@@ -414,6 +633,31 @@ char* find_pad_by_prefix(const char* prefix) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
@@ -944,6 +1188,22 @@ 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("\nText input options:\n");
|
||||
printf("1. Type text directly\n");
|
||||
printf("2. Use text editor\n");
|
||||
printf("Enter choice (1-2): ");
|
||||
|
||||
char input_choice[10];
|
||||
if (!fgets(input_choice, sizeof(input_choice), stdin)) {
|
||||
printf("Error: Failed to read input\n");
|
||||
free(pad_chksum);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -959,6 +1219,24 @@ int encrypt_text(const char* pad_identifier, const char* input_text) {
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t input_len = strlen(text_buffer);
|
||||
if (input_len == 0) {
|
||||
@@ -2050,6 +2328,527 @@ void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size,
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced input function with editable pre-filled text
|
||||
int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size) {
|
||||
// Find the last directory separator
|
||||
char* last_slash = strrchr(default_path, '/');
|
||||
char directory[1024] = "";
|
||||
char filename[512] = "";
|
||||
|
||||
if (last_slash) {
|
||||
// Extract directory path
|
||||
size_t dir_len = last_slash - default_path + 1; // Include the trailing slash
|
||||
if (dir_len < sizeof(directory)) {
|
||||
strncpy(directory, default_path, dir_len);
|
||||
directory[dir_len] = '\0';
|
||||
}
|
||||
|
||||
// Extract filename
|
||||
strncpy(filename, last_slash + 1, sizeof(filename) - 1);
|
||||
filename[sizeof(filename) - 1] = '\0';
|
||||
} else {
|
||||
// No directory separator, treat as filename only
|
||||
strncpy(filename, default_path, sizeof(filename) - 1);
|
||||
filename[sizeof(filename) - 1] = '\0';
|
||||
}
|
||||
|
||||
// Setup terminal for raw input
|
||||
struct termios orig_termios;
|
||||
if (tcgetattr(STDIN_FILENO, &orig_termios) != 0) {
|
||||
// Fallback to simple input if terminal control fails
|
||||
printf("\n%s\n%s: ", prompt, directory);
|
||||
fflush(stdout);
|
||||
|
||||
char input_buffer[512];
|
||||
if (!fgets(input_buffer, sizeof(input_buffer), stdin)) {
|
||||
return 1;
|
||||
}
|
||||
input_buffer[strcspn(input_buffer, "\n")] = 0;
|
||||
|
||||
if (strlen(input_buffer) == 0) {
|
||||
strncpy(result, default_path, result_size - 1);
|
||||
} else {
|
||||
if (strlen(directory) > 0) {
|
||||
snprintf(result, result_size, "%s%s", directory, input_buffer);
|
||||
} else {
|
||||
strncpy(result, input_buffer, result_size - 1);
|
||||
}
|
||||
}
|
||||
result[result_size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set up raw terminal mode
|
||||
struct termios raw_termios = orig_termios;
|
||||
raw_termios.c_lflag &= ~(ECHO | ICANON);
|
||||
raw_termios.c_cc[VMIN] = 1;
|
||||
raw_termios.c_cc[VTIME] = 0;
|
||||
|
||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_termios) != 0) {
|
||||
// Fallback if terminal setup fails
|
||||
printf("\n%s\n%s: ", prompt, directory);
|
||||
fflush(stdout);
|
||||
|
||||
char input_buffer[512];
|
||||
if (!fgets(input_buffer, sizeof(input_buffer), stdin)) {
|
||||
return 1;
|
||||
}
|
||||
input_buffer[strcspn(input_buffer, "\n")] = 0;
|
||||
|
||||
if (strlen(input_buffer) == 0) {
|
||||
strncpy(result, default_path, result_size - 1);
|
||||
} else {
|
||||
if (strlen(directory) > 0) {
|
||||
snprintf(result, result_size, "%s%s", directory, input_buffer);
|
||||
} else {
|
||||
strncpy(result, input_buffer, result_size - 1);
|
||||
}
|
||||
}
|
||||
result[result_size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Display prompt and directory
|
||||
printf("\n%s\n%s", prompt, directory);
|
||||
fflush(stdout);
|
||||
|
||||
// Initialize editing buffer with default filename
|
||||
char edit_buffer[512];
|
||||
strncpy(edit_buffer, filename, sizeof(edit_buffer) - 1);
|
||||
edit_buffer[sizeof(edit_buffer) - 1] = '\0';
|
||||
int cursor_pos = strlen(edit_buffer);
|
||||
int buffer_len = cursor_pos;
|
||||
|
||||
// Display initial filename
|
||||
printf("%s", edit_buffer);
|
||||
fflush(stdout);
|
||||
|
||||
// Main editing loop
|
||||
int c;
|
||||
while ((c = getchar()) != EOF) {
|
||||
if (c == '\n' || c == '\r') {
|
||||
// Enter key - accept input
|
||||
break;
|
||||
} else if (c == 127 || c == 8) {
|
||||
// Backspace/Delete
|
||||
if (cursor_pos > 0) {
|
||||
// Move everything after cursor one position left
|
||||
memmove(&edit_buffer[cursor_pos - 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1);
|
||||
cursor_pos--;
|
||||
buffer_len--;
|
||||
|
||||
// Redraw line: move cursor to start of filename area, clear to end, redraw buffer
|
||||
printf("\r%s\033[K%s", directory, edit_buffer);
|
||||
|
||||
// Position cursor correctly
|
||||
if (cursor_pos < buffer_len) {
|
||||
printf("\033[%dD", buffer_len - cursor_pos);
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
} else if (c == 27) {
|
||||
// Escape sequence (arrow keys, etc.)
|
||||
int c1 = getchar();
|
||||
int c2 = getchar();
|
||||
if (c1 == '[') {
|
||||
if (c2 == 'C' && cursor_pos < buffer_len) {
|
||||
// Right arrow
|
||||
cursor_pos++;
|
||||
printf("\033[1C");
|
||||
fflush(stdout);
|
||||
} else if (c2 == 'D' && cursor_pos > 0) {
|
||||
// Left arrow
|
||||
cursor_pos--;
|
||||
printf("\033[1D");
|
||||
fflush(stdout);
|
||||
} else if (c2 == 'H') {
|
||||
// Home key
|
||||
printf("\033[%dD", cursor_pos);
|
||||
cursor_pos = 0;
|
||||
fflush(stdout);
|
||||
} else if (c2 == 'F') {
|
||||
// End key
|
||||
printf("\033[%dC", buffer_len - cursor_pos);
|
||||
cursor_pos = buffer_len;
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
} else if (c >= 32 && c <= 126) {
|
||||
// Printable character
|
||||
if (buffer_len < (int)sizeof(edit_buffer) - 1) {
|
||||
// Move everything after cursor one position right
|
||||
memmove(&edit_buffer[cursor_pos + 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1);
|
||||
edit_buffer[cursor_pos] = c;
|
||||
cursor_pos++;
|
||||
buffer_len++;
|
||||
|
||||
// Redraw from cursor position
|
||||
printf("%c", c);
|
||||
if (cursor_pos < buffer_len) {
|
||||
// Print remaining characters and move cursor back
|
||||
printf("%s\033[%dD", &edit_buffer[cursor_pos], buffer_len - cursor_pos);
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
// Ignore other control characters
|
||||
}
|
||||
|
||||
// Restore terminal
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
|
||||
printf("\n");
|
||||
|
||||
// Construct final result
|
||||
if (buffer_len == 0) {
|
||||
// Empty input, use default
|
||||
strncpy(result, default_path, result_size - 1);
|
||||
} else {
|
||||
// Combine directory with edited filename
|
||||
edit_buffer[buffer_len] = '\0';
|
||||
if (strlen(directory) > 0) {
|
||||
snprintf(result, result_size, "%s%s", directory, edit_buffer);
|
||||
} else {
|
||||
strncpy(result, edit_buffer, result_size - 1);
|
||||
}
|
||||
}
|
||||
result[result_size - 1] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Editor and file manager 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);
|
||||
}
|
||||
|
||||
// Use enhanced input function for output filename
|
||||
char output_file[512];
|
||||
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
|
||||
printf("Error: Failed to read input\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* output_filename = output_file;
|
||||
|
||||
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());
|
||||
|
||||
8
otp.code-workspace
Normal file
8
otp.code-workspace
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
BIN
output.otp
Normal file
BIN
output.otp
Normal file
Binary file not shown.
BIN
test.txt.otp
BIN
test.txt.otp
Binary file not shown.
@@ -1 +0,0 @@
|
||||
This is a test file for OTP encryption.
|
||||
@@ -1,7 +0,0 @@
|
||||
-----BEGIN OTP MESSAGE-----
|
||||
Version: v0.2.15
|
||||
Pad-ChkSum: 0c8e19fde996e683fdbd348d1052eec168ffe6f67a88bb1278d0d02e9341b87b
|
||||
Pad-Offset: 210
|
||||
|
||||
mMIm7iVtUO6NbXbskMxtydI/A16UXEQUGTcIya/8Dja6PB3EC0MLdw==
|
||||
-----END OTP MESSAGE-----
|
||||
1
test_decrypt.txt
Normal file
1
test_decrypt.txt
Normal file
@@ -0,0 +1 @@
|
||||
Test file content for decryption
|
||||
BIN
test_decrypt.txt.otp
Normal file
BIN
test_decrypt.txt.otp
Normal file
Binary file not shown.
1
test_file.txt
Normal file
1
test_file.txt
Normal file
@@ -0,0 +1 @@
|
||||
Hello, this is a test file for encryption!
|
||||
7
test_file.txt.otp.asc
Normal file
7
test_file.txt.otp.asc
Normal file
@@ -0,0 +1,7 @@
|
||||
-----BEGIN OTP MESSAGE-----
|
||||
Version: v0.2.29
|
||||
Pad-ChkSum: d0d4a489354348b08d8c7b324814d8c50010042e9da47f2c973f32a16a09101b
|
||||
Pad-Offset: 57
|
||||
|
||||
05S8GfS0tFfczNMUz0xrieFGoPSREM4uo5QhFGoBCcOzjfTXTDMt3hRtAQ==
|
||||
-----END OTP MESSAGE-----
|
||||
1
test_input.txt
Normal file
1
test_input.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a test file for encryption.
|
||||
BIN
toc.txt.otp
Normal file
BIN
toc.txt.otp
Normal file
Binary file not shown.
Reference in New Issue
Block a user