Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5498a2321e | |||
| fe2eb40ead | |||
| 0db1988d8f | |||
| 97530c8eb3 | |||
| a85c4ed55b | |||
| a9974c7e87 | |||
| 592d54728b | |||
| 21b3c4de52 | |||
| 3a854c3ccf | |||
| 877add0dbf | |||
| 482687cb68 | |||
| e35d94243e | |||
| e88e1b5d3d | |||
| 41ef97c43e | |||
| 7810e66114 | |||
| b4be05c34d | |||
| 1cb0ba935d | |||
| 8c8c873e73 | |||
| 692f65b7f0 | |||
| 1c4200a73a | |||
| 1c9e2ee527 | |||
| 8401e14ae0 | |||
| 0dbd81d1cc | |||
| f979789c11 | |||
| 498d7d31c4 | |||
| e58f05619e | |||
| 992b9349b3 | |||
| 1f4a1fb90f | |||
| c7fae1ad1d | |||
| 37bcb6a6d2 | |||
| 9ded0aed44 | |||
| 4442837ce8 | |||
| 31ee220558 | |||
| 0a25c13b65 | |||
| fd9d87c548 |
@@ -1,7 +1,29 @@
|
|||||||
|
|
||||||
When building, use build.sh, not make.
|
When building, use build.sh, not make.
|
||||||
|
|
||||||
Use it as follows: build.sh -m "useful comment on changes being made"
|
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.
|
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.
|
||||||
|
|
||||||
|
## Buffer Size Guidelines
|
||||||
|
|
||||||
|
### Path Handling
|
||||||
|
- Always use buffers of size 1024 or PATH_MAX (4096) for file paths
|
||||||
|
- When concatenating paths with snprintf, ensure buffer is at least 2x the expected maximum input
|
||||||
|
- Use safer path construction patterns that check lengths before concatenation
|
||||||
|
|
||||||
|
### String Formatting Safety
|
||||||
|
- Before using snprintf with dynamic strings, validate that buffer size >= sum of all input string lengths + format characters + 1
|
||||||
|
- Use strnlen() to check actual string lengths before formatting
|
||||||
|
- Consider using asprintf() for dynamic allocation when exact size is unknown
|
||||||
|
- Add length validation before snprintf calls
|
||||||
|
|
||||||
|
### Compiler Warning Prevention
|
||||||
|
- Always size string buffers generously (minimum 1024 for paths, 512 for general strings)
|
||||||
|
- Use buffer size calculations: `size >= strlen(str1) + strlen(str2) + format_overhead + 1`
|
||||||
|
- Add runtime length checks before snprintf operations
|
||||||
|
- Consider using safer alternatives like strlcpy/strlcat if available
|
||||||
|
|
||||||
|
### Code Patterns to Avoid
|
||||||
|
- Fixed-size buffers (512 bytes) for path operations where inputs could be 255+ bytes each
|
||||||
|
- Concatenating unchecked strings with snprintf
|
||||||
|
- Assuming maximum path component sizes without validation
|
||||||
|
|||||||
54
README.md
54
README.md
@@ -1,6 +1,47 @@
|
|||||||
# OTP Cipher - One Time Pad Implementation
|
# OTP Cipher - One Time Pad Implementation
|
||||||
|
|
||||||
A secure one-time pad (OTP) cipher implementation in C with automatic versioning system.
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
A secure one-time pad (OTP) cipher implementation in C.
|
||||||
|
|
||||||
|
## Why One-Time Pads
|
||||||
|
|
||||||
|
Nostr and much of the web runs on public key cryptography. Public key cryptography is great, but it is vulnerable. Cryptographers know this, and they know what it takes to attack it, so what they do is just make the keys large enough such that the system is resistant to attack given computers as they are today.
|
||||||
|
|
||||||
|
There is one type of cryptography, however, that is invulnerable to any type of attack in our universe, and that is known as a one-time pad.
|
||||||
|
|
||||||
|
One-time pads rely directly on the laws of physics and what it means for a number to be truly random.
|
||||||
|
|
||||||
|
If you take your secret message and mix it with truly random numbers, and don't use those random numbers again, then that message is unbreakable by any computer, no matter how powerful, quantum or not, forever.
|
||||||
|
|
||||||
|
In fact, one-time pads are so powerful that if you have data encrypted by a one-time pad located in a distant galaxy, and that data is not kept anywhere else, then by destroying the pad used for encryption in your galaxy, the data is wiped from the universe and can never be recovered.
|
||||||
|
|
||||||
|
## Advantages and Limitations
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
1. The pad must be shared between the parties wanting to use it.
|
||||||
|
2. The pad must be as long or longer than what you want to encrypt, and it can't be used a second time.
|
||||||
|
|
||||||
|
### Modern Advantages
|
||||||
|
|
||||||
|
While in the past, pad length might have been a problem, readily available USB drives in the terabytes make size less of a problem for many uses.
|
||||||
|
|
||||||
|
We are also becoming very accustomed to YubiKey authenticators in the USB ports of our computers. A small USB drive in our devices can now easily contain a key of greater length than all the text messages we would expect to send over a lifetime.
|
||||||
|
|
||||||
|
### Multi-Device Coordination
|
||||||
|
|
||||||
|
One of the problems to address is the fact that to use an OTP across several devices means that they have to coordinate to know when they are encrypting new plaintext and where to start in the key. Reusing the same section of the pad, while not necessarily fatal, degrades the encryption from its status as "Information Theoretically Secure".
|
||||||
|
|
||||||
|
To address this problem, we can use Nostr to share among devices the place in the pad that was last left off.
|
||||||
|
|
||||||
|
### Additional Benefits
|
||||||
|
|
||||||
|
One-time pads can be trivially encrypted and decrypted using pencil and paper, making them accessible even without electronic devices.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -13,15 +54,6 @@ A secure one-time pad (OTP) cipher implementation in C with automatic versioning
|
|||||||
- **Multiple Build Options**: Standard and static linking builds
|
- **Multiple Build Options**: Standard and static linking builds
|
||||||
- **Cross-Platform**: Works on Linux and other UNIX-like systems
|
- **Cross-Platform**: Works on Linux and other UNIX-like systems
|
||||||
|
|
||||||
## Version Information
|
|
||||||
|
|
||||||
This project uses an automatic versioning system that:
|
|
||||||
- Automatically increments the patch version on each build
|
|
||||||
- Embeds build timestamp, git commit hash, and branch information
|
|
||||||
- Creates git tags for version tracking
|
|
||||||
- Generates version header files with detailed build metadata
|
|
||||||
|
|
||||||
Current version can be viewed with: `./otp --help` or by running the interactive mode.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@@ -31,7 +63,7 @@ Current version can be viewed with: `./otp --help` or by running the interactive
|
|||||||
- Git (for version tracking)
|
- Git (for version tracking)
|
||||||
- Make
|
- Make
|
||||||
|
|
||||||
**Note: OpenSSL is no longer required! This implementation is now completely self-contained.**
|
|
||||||
|
|
||||||
### Build Commands
|
### Build Commands
|
||||||
|
|
||||||
|
|||||||
24
TODO.md
Normal file
24
TODO.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
|
||||||
|
## Change technique for adding keyboard entropy.
|
||||||
|
|
||||||
|
## Some of the processing seems similar, so maybe code could be more compact.
|
||||||
|
|
||||||
|
## Command line otp -e should go to default pad, and then comment after the fact that it used the default pad.
|
||||||
|
|
||||||
|
## There is the problem of the location of the pad revealing metadata about how many messages have been sent in the past, or at least the size of the messsages.
|
||||||
|
|
||||||
|
One solution could be to start the pad at a random location, and then wrap around, so an attacker could never tell the size of the past text sent. This helps. But then you have to store the start location, which you could do within the header of the pad along with the pad?
|
||||||
|
|
||||||
|
Or, better yet, assume the offset is a very large size, and use the pad itself to encrypt the offset.
|
||||||
|
|
||||||
|
## Take a look at how the file header is being handled.
|
||||||
|
|
||||||
|
## We have three different decrypt file functions
|
||||||
|
|
||||||
|
## Preferences directory and files look off. Should probably have ~/.otp as the default directory, and then in there we can have otp.conf, pads/
|
||||||
|
|
||||||
|
## Setup for multiple USB drives
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Test file content for decryption
|
|
||||||
731
otp.c
731
otp.c
@@ -39,14 +39,36 @@ static const int base64_decode_table[256] = {
|
|||||||
#define MAX_LINE_LENGTH 1024
|
#define MAX_LINE_LENGTH 1024
|
||||||
#define MAX_HASH_LENGTH 65
|
#define MAX_HASH_LENGTH 65
|
||||||
#define PROGRESS_UPDATE_INTERVAL (64 * 1024 * 1024) // 64MB intervals
|
#define PROGRESS_UPDATE_INTERVAL (64 * 1024 * 1024) // 64MB intervals
|
||||||
#define PADS_DIR "pads"
|
#define DEFAULT_PADS_DIR "pads"
|
||||||
#define FILES_DIR "files"
|
#define FILES_DIR "files"
|
||||||
#define MAX_ENTROPY_BUFFER 32768 // 32KB entropy buffer
|
#define MAX_ENTROPY_BUFFER 32768 // 32KB entropy buffer
|
||||||
|
|
||||||
|
// Global variable for current pads directory (can be local or OTP thumb drive)
|
||||||
|
static char current_pads_dir[512] = DEFAULT_PADS_DIR;
|
||||||
|
|
||||||
|
// Global variable for default pad path from preferences
|
||||||
|
static char default_pad_path[1024] = "";
|
||||||
|
|
||||||
// Function prototypes
|
// Function prototypes
|
||||||
int main(int argc, char* argv[]);
|
int main(int argc, char* argv[]);
|
||||||
int interactive_mode(void);
|
int interactive_mode(void);
|
||||||
int command_line_mode(int argc, char* argv[]);
|
int command_line_mode(int argc, char* argv[]);
|
||||||
|
int pipe_mode(int argc, char* argv[], const char* piped_text);
|
||||||
|
|
||||||
|
// Stdin detection functions
|
||||||
|
int has_stdin_data(void);
|
||||||
|
char* read_stdin_text(void);
|
||||||
|
|
||||||
|
// Preferences management functions
|
||||||
|
int load_preferences(void);
|
||||||
|
int save_preferences(void);
|
||||||
|
char* get_preference(const char* key);
|
||||||
|
int set_preference(const char* key, const char* value);
|
||||||
|
char* get_default_pad_path(void);
|
||||||
|
int set_default_pad_path(const char* pad_path);
|
||||||
|
|
||||||
|
// OTP thumb drive detection function
|
||||||
|
int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size);
|
||||||
|
|
||||||
// Editor and file manager functions
|
// Editor and file manager functions
|
||||||
char* get_preferred_editor(void);
|
char* get_preferred_editor(void);
|
||||||
@@ -59,6 +81,7 @@ 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 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 encrypt_text(const char* pad_identifier, const char* input_text);
|
||||||
int decrypt_text(const char* pad_identifier, const char* encrypted_message);
|
int decrypt_text(const char* pad_identifier, const char* encrypted_message);
|
||||||
|
int decrypt_text_silent(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 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_file(const char* input_file, const char* output_file);
|
||||||
int decrypt_binary_file(FILE* input_fp, const char* output_file);
|
int decrypt_binary_file(FILE* input_fp, const char* output_file);
|
||||||
@@ -108,8 +131,51 @@ void get_directory_display(const char* file_path, char* result, size_t result_si
|
|||||||
|
|
||||||
void print_usage(const char* program_name);
|
void print_usage(const char* program_name);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// MAIN
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc == 1) {
|
// Load preferences first
|
||||||
|
load_preferences();
|
||||||
|
|
||||||
|
// Check for piped input first (before any output)
|
||||||
|
int is_pipe_mode = (argc == 1 && has_stdin_data());
|
||||||
|
|
||||||
|
// Check for decrypt command with piped input
|
||||||
|
int is_decrypt_pipe = (argc == 2 &&
|
||||||
|
(strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) &&
|
||||||
|
has_stdin_data());
|
||||||
|
|
||||||
|
// Check for OTP thumb drive on startup
|
||||||
|
char otp_drive_path[512];
|
||||||
|
if (detect_otp_thumb_drive(otp_drive_path, sizeof(otp_drive_path))) {
|
||||||
|
// Only show messages in interactive/command mode, not pipe mode
|
||||||
|
if (!is_pipe_mode && !is_decrypt_pipe) {
|
||||||
|
printf("Detected OTP thumb drive: %s\n", otp_drive_path);
|
||||||
|
printf("Using as default pads directory for this session.\n\n");
|
||||||
|
}
|
||||||
|
strncpy(current_pads_dir, otp_drive_path, sizeof(current_pads_dir) - 1);
|
||||||
|
current_pads_dir[sizeof(current_pads_dir) - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_pipe_mode) {
|
||||||
|
// No arguments but has piped data - enter pipe mode
|
||||||
|
char* piped_text = read_stdin_text();
|
||||||
|
if (piped_text) {
|
||||||
|
int result = pipe_mode(argc, argv, piped_text);
|
||||||
|
free(piped_text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// If reading stdin failed, fall back to interactive mode
|
||||||
|
return interactive_mode();
|
||||||
|
} else if (argc == 1) {
|
||||||
return interactive_mode();
|
return interactive_mode();
|
||||||
} else {
|
} else {
|
||||||
return command_line_mode(argc, argv);
|
return command_line_mode(argc, argv);
|
||||||
@@ -117,41 +183,42 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int interactive_mode(void) {
|
int interactive_mode(void) {
|
||||||
// printf("\n\n\n\n=== OTP Cipher %s ===\n\n", get_version());
|
char input[10];
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
show_main_menu();
|
show_main_menu();
|
||||||
char input[10];
|
|
||||||
if (fgets(input, sizeof(input), stdin)) {
|
|
||||||
char choice = toupper(input[0]);
|
|
||||||
|
|
||||||
switch (choice) {
|
if (!fgets(input, sizeof(input), stdin)) {
|
||||||
case 'T':
|
printf("Goodbye!\n");
|
||||||
handle_text_encrypt();
|
break;
|
||||||
break;
|
}
|
||||||
case 'F':
|
|
||||||
handle_file_encrypt();
|
char choice = toupper(input[0]);
|
||||||
break;
|
|
||||||
case 'D':
|
switch (choice) {
|
||||||
handle_decrypt_menu();
|
case 'T':
|
||||||
break;
|
handle_text_encrypt();
|
||||||
case 'P':
|
break;
|
||||||
handle_pads_menu();
|
case 'F':
|
||||||
break;
|
handle_file_encrypt();
|
||||||
case 'X':
|
break;
|
||||||
case 'Q':
|
case 'D':
|
||||||
printf("Goodbye!\n");
|
handle_decrypt_menu();
|
||||||
return 0;
|
break;
|
||||||
default:
|
case 'P':
|
||||||
printf("Invalid option. Please select T, F, D, P, or X.\n");
|
handle_pads_menu();
|
||||||
continue;
|
break;
|
||||||
}
|
case 'X':
|
||||||
} else {
|
case 'Q':
|
||||||
printf("Error reading input. Please try again.\n");
|
printf("Goodbye!\n");
|
||||||
continue;
|
return 0;
|
||||||
|
default:
|
||||||
|
printf("Invalid choice. Please try again.\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
printf("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int command_line_mode(int argc, char* argv[]) {
|
int command_line_mode(int argc, char* argv[]) {
|
||||||
@@ -187,6 +254,16 @@ int command_line_mode(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
else if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) {
|
else if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) {
|
||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
|
// Check for piped input first
|
||||||
|
if (has_stdin_data()) {
|
||||||
|
// Piped decrypt mode - read stdin and decrypt silently
|
||||||
|
char* piped_message = read_stdin_text();
|
||||||
|
if (piped_message) {
|
||||||
|
int result = decrypt_text_silent(NULL, piped_message);
|
||||||
|
free(piped_message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Interactive mode - no arguments needed
|
// Interactive mode - no arguments needed
|
||||||
return decrypt_text(NULL, NULL);
|
return decrypt_text(NULL, NULL);
|
||||||
}
|
}
|
||||||
@@ -262,11 +339,11 @@ void show_main_menu(void) {
|
|||||||
|
|
||||||
printf("\n\n\n\n=========================== Main Menu - OTP %s ===========================\n\n", get_version() );
|
printf("\n\n\n\n=========================== Main Menu - OTP %s ===========================\n\n", get_version() );
|
||||||
|
|
||||||
printf(" \033[4mT\033[0mext encrypt\n");
|
printf(" \033[4mT\033[0mext encrypt\n"); //TEXT ENCRYPT
|
||||||
printf(" \033[4mF\033[0mile encrypt\n");
|
printf(" \033[4mF\033[0mile encrypt\n"); //FILE ENCRYPT
|
||||||
printf(" \033[4mD\033[0mecrypt\n");
|
printf(" \033[4mD\033[0mecrypt\n"); //DECRYPT
|
||||||
printf(" \033[4mP\033[0mads\n");
|
printf(" \033[4mP\033[0mads\n"); //PADS
|
||||||
printf(" E\033[4mx\033[0mit\n");
|
printf(" E\033[4mx\033[0mit\n"); //EXIT
|
||||||
printf("\nSelect option: ");
|
printf("\nSelect option: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,9 +709,9 @@ uint64_t parse_size_string(const char* size_str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char* find_pad_by_prefix(const char* prefix) {
|
char* find_pad_by_prefix(const char* prefix) {
|
||||||
DIR* dir = opendir(PADS_DIR);
|
DIR* dir = opendir(current_pads_dir);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
printf("Error: Cannot open pads directory\n");
|
printf("Error: Cannot open pads directory %s\n", current_pads_dir);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,10 +745,9 @@ char* find_pad_by_prefix(const char* prefix) {
|
|||||||
int current = 0;
|
int current = 0;
|
||||||
rewinddir(dir);
|
rewinddir(dir);
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
// Skip . and .. entries, and only process .pad files
|
|
||||||
if (entry->d_name[0] == '.') continue;
|
if (entry->d_name[0] == '.') continue;
|
||||||
if (!strstr(entry->d_name, ".pad")) continue;
|
if (!strstr(entry->d_name, ".pad")) continue;
|
||||||
if (strlen(entry->d_name) != 68) continue; // 64 char chksum + ".pad"
|
if (strlen(entry->d_name) != 68) continue;
|
||||||
|
|
||||||
current++;
|
current++;
|
||||||
if (current == selection) {
|
if (current == selection) {
|
||||||
@@ -732,9 +808,9 @@ char* find_pad_by_prefix(const char* prefix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int list_available_pads(void) {
|
int list_available_pads(void) {
|
||||||
DIR* dir = opendir(PADS_DIR);
|
DIR* dir = opendir(current_pads_dir);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
printf("Error: Cannot open pads directory\n");
|
printf("Error: Cannot open pads directory %s\n", current_pads_dir);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,8 +829,8 @@ int list_available_pads(void) {
|
|||||||
chksum[64] = '\0';
|
chksum[64] = '\0';
|
||||||
|
|
||||||
// Get pad file size
|
// Get pad file size
|
||||||
char full_path[300]; // Increased buffer size to accommodate longer paths
|
char full_path[1024]; // Increased buffer size to accommodate longer paths
|
||||||
snprintf(full_path, sizeof(full_path), "%s/%s", PADS_DIR, entry->d_name);
|
snprintf(full_path, sizeof(full_path), "%s/%s", current_pads_dir, entry->d_name);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(full_path, &st) == 0) {
|
if (stat(full_path, &st) == 0) {
|
||||||
// Get used bytes from state
|
// Get used bytes from state
|
||||||
@@ -1113,10 +1189,39 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use
|
|||||||
// Get final paths in pads directory
|
// Get final paths in pads directory
|
||||||
get_pad_path(chksum_hex, pad_path, state_path);
|
get_pad_path(chksum_hex, pad_path, state_path);
|
||||||
|
|
||||||
|
// Try rename first (works for same filesystem)
|
||||||
if (rename(temp_filename, pad_path) != 0) {
|
if (rename(temp_filename, pad_path) != 0) {
|
||||||
printf("Error: Cannot move pad file to pads directory\n");
|
// If rename fails, try copy and delete (works across filesystems)
|
||||||
|
FILE* temp_file = fopen(temp_filename, "rb");
|
||||||
|
FILE* dest_file = fopen(pad_path, "wb");
|
||||||
|
|
||||||
|
if (!temp_file || !dest_file) {
|
||||||
|
printf("Error: Cannot copy pad file to pads directory\n");
|
||||||
|
if (temp_file) fclose(temp_file);
|
||||||
|
if (dest_file) fclose(dest_file);
|
||||||
|
unlink(temp_filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy file in chunks
|
||||||
|
unsigned char copy_buffer[64 * 1024];
|
||||||
|
size_t bytes_read;
|
||||||
|
while ((bytes_read = fread(copy_buffer, 1, sizeof(copy_buffer), temp_file)) > 0) {
|
||||||
|
if (fwrite(copy_buffer, 1, bytes_read, dest_file) != bytes_read) {
|
||||||
|
printf("Error: Failed to copy pad file to pads directory\n");
|
||||||
|
fclose(temp_file);
|
||||||
|
fclose(dest_file);
|
||||||
|
unlink(temp_filename);
|
||||||
|
unlink(pad_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(temp_file);
|
||||||
|
fclose(dest_file);
|
||||||
|
|
||||||
|
// Remove temporary file after successful copy
|
||||||
unlink(temp_filename);
|
unlink(temp_filename);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set pad file to read-only
|
// Set pad file to read-only
|
||||||
@@ -1311,8 +1416,15 @@ int encrypt_text(const char* pad_identifier, const char* input_text) {
|
|||||||
printf("Warning: Failed to update state file\n");
|
printf("Warning: Failed to update state file\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output in ASCII armor format
|
// Output in ASCII armor format - clean format for piping, spaced format for interactive
|
||||||
printf("\n\n-----BEGIN OTP MESSAGE-----\n");
|
int is_interactive = (input_text == NULL); // Interactive if no input_text provided
|
||||||
|
|
||||||
|
if (is_interactive) {
|
||||||
|
printf("\n\n-----BEGIN OTP MESSAGE-----\n");
|
||||||
|
} else {
|
||||||
|
printf("-----BEGIN OTP MESSAGE-----\n");
|
||||||
|
}
|
||||||
|
|
||||||
printf("Version: %s\n", get_version());
|
printf("Version: %s\n", get_version());
|
||||||
printf("Pad-ChkSum: %s\n", chksum_hex);
|
printf("Pad-ChkSum: %s\n", chksum_hex);
|
||||||
printf("Pad-Offset: %lu\n", current_offset);
|
printf("Pad-Offset: %lu\n", current_offset);
|
||||||
@@ -1324,7 +1436,11 @@ int encrypt_text(const char* pad_identifier, const char* input_text) {
|
|||||||
printf("%.64s\n", base64_cipher + i);
|
printf("%.64s\n", base64_cipher + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("-----END OTP MESSAGE-----\n\n\n");
|
if (is_interactive) {
|
||||||
|
printf("-----END OTP MESSAGE-----\n\n\n");
|
||||||
|
} else {
|
||||||
|
printf("-----END OTP MESSAGE-----\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
free(pad_data);
|
free(pad_data);
|
||||||
@@ -1509,6 +1625,135 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int decrypt_text_silent(const char* pad_identifier, const char* encrypted_message) {
|
||||||
|
// For piped decrypt mode - silent operation with minimal output
|
||||||
|
(void)pad_identifier; // Suppress unused parameter warning
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else if (strncmp(line_ptr, "Version:", 8) != 0 && strncmp(line_ptr, "Pad-", 4) != 0) {
|
||||||
|
// This might be base64 data without a blank line separator
|
||||||
|
strncat(base64_data, line_ptr, sizeof(base64_data) - strlen(base64_data) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line_ptr = strtok(NULL, "\n");
|
||||||
|
}
|
||||||
|
free(message_copy);
|
||||||
|
|
||||||
|
if (!found_begin) {
|
||||||
|
fprintf(stderr, "Error: Invalid message format - missing BEGIN header\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error: No encrypted message provided\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) {
|
||||||
|
fprintf(stderr, "Error: Required pad not found: %s\n", stored_chksum);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify pad integrity (silent check)
|
||||||
|
if (calculate_checksum(pad_path, current_chksum) != 0) {
|
||||||
|
fprintf(stderr, "Error: Cannot calculate current pad checksum\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(stored_chksum, current_chksum) != 0) {
|
||||||
|
fprintf(stderr, "Error: Pad integrity check failed!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode base64
|
||||||
|
int ciphertext_len;
|
||||||
|
unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len);
|
||||||
|
if (!ciphertext) {
|
||||||
|
fprintf(stderr, "Error: Invalid base64 data\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read pad data at specified offset
|
||||||
|
FILE* pad_file = fopen(pad_path, "rb");
|
||||||
|
if (!pad_file) {
|
||||||
|
fprintf(stderr, "Error: Cannot open pad file %s\n", pad_path);
|
||||||
|
free(ciphertext);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fseek(pad_file, pad_offset, SEEK_SET) != 0) {
|
||||||
|
fprintf(stderr, "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) {
|
||||||
|
fprintf(stderr, "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';
|
||||||
|
|
||||||
|
// Output only the decrypted text with newline and flush
|
||||||
|
printf("%s\n", plaintext);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
// 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) {
|
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);
|
char* pad_chksum = find_pad_by_prefix(pad_identifier);
|
||||||
if (!pad_chksum) {
|
if (!pad_chksum) {
|
||||||
@@ -2081,8 +2326,8 @@ int decrypt_ascii_file(const char* input_file, const char* output_file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int read_state_offset(const char* pad_chksum, uint64_t* offset) {
|
int read_state_offset(const char* pad_chksum, uint64_t* offset) {
|
||||||
char state_filename[MAX_HASH_LENGTH + 20];
|
char state_filename[1024];
|
||||||
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", PADS_DIR, pad_chksum);
|
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", current_pads_dir, pad_chksum);
|
||||||
|
|
||||||
FILE* state_file = fopen(state_filename, "rb");
|
FILE* state_file = fopen(state_filename, "rb");
|
||||||
if (!state_file) {
|
if (!state_file) {
|
||||||
@@ -2101,8 +2346,8 @@ int read_state_offset(const char* pad_chksum, uint64_t* offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int write_state_offset(const char* pad_chksum, uint64_t offset) {
|
int write_state_offset(const char* pad_chksum, uint64_t offset) {
|
||||||
char state_filename[MAX_HASH_LENGTH + 20];
|
char state_filename[1024];
|
||||||
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", PADS_DIR, pad_chksum);
|
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", current_pads_dir, pad_chksum);
|
||||||
|
|
||||||
FILE* state_file = fopen(state_filename, "wb");
|
FILE* state_file = fopen(state_filename, "wb");
|
||||||
if (!state_file) {
|
if (!state_file) {
|
||||||
@@ -2243,8 +2488,8 @@ int collect_keyboard_entropy(unsigned char* entropy_buffer, size_t max_size, siz
|
|||||||
// Directory management functions
|
// Directory management functions
|
||||||
int ensure_pads_directory(void) {
|
int ensure_pads_directory(void) {
|
||||||
struct stat st = {0};
|
struct stat st = {0};
|
||||||
if (stat(PADS_DIR, &st) == -1) {
|
if (stat(current_pads_dir, &st) == -1) {
|
||||||
if (mkdir(PADS_DIR, 0755) != 0) {
|
if (mkdir(current_pads_dir, 0755) != 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2275,10 +2520,313 @@ void get_default_file_path(const char* filename, char* result_path, size_t resul
|
|||||||
}
|
}
|
||||||
|
|
||||||
void get_pad_path(const char* chksum, char* pad_path, char* state_path) {
|
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(pad_path, 1024, "%s/%s.pad", current_pads_dir, chksum);
|
||||||
snprintf(state_path, MAX_HASH_LENGTH + 20, "%s/%s.state", PADS_DIR, chksum);
|
snprintf(state_path, 1024, "%s/%s.state", current_pads_dir, chksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stdin detection functions implementation
|
||||||
|
int has_stdin_data(void) {
|
||||||
|
// Check if stdin is a pipe/redirect (not a terminal)
|
||||||
|
if (!isatty(STDIN_FILENO)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* read_stdin_text(void) {
|
||||||
|
size_t capacity = 4096;
|
||||||
|
size_t length = 0;
|
||||||
|
char* buffer = malloc(capacity);
|
||||||
|
|
||||||
|
if (!buffer) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char chunk[1024];
|
||||||
|
while (fgets(chunk, sizeof(chunk), stdin)) {
|
||||||
|
size_t chunk_len = strlen(chunk);
|
||||||
|
|
||||||
|
// Ensure we have enough capacity
|
||||||
|
while (length + chunk_len >= capacity) {
|
||||||
|
capacity *= 2;
|
||||||
|
char* new_buffer = realloc(buffer, capacity);
|
||||||
|
if (!new_buffer) {
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buffer = new_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(buffer + length, chunk);
|
||||||
|
length += chunk_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove trailing newline if present
|
||||||
|
if (length > 0 && buffer[length - 1] == '\n') {
|
||||||
|
buffer[length - 1] = '\0';
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If empty, free and return NULL
|
||||||
|
if (length == 0) {
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pipe_mode(int argc, char* argv[], const char* piped_text) {
|
||||||
|
(void)argc; // Suppress unused parameter warning
|
||||||
|
(void)argv; // Suppress unused parameter warning
|
||||||
|
|
||||||
|
// Check if we have a default pad configured
|
||||||
|
char* default_pad = get_default_pad_path();
|
||||||
|
if (default_pad) {
|
||||||
|
// Verify the default pad exists and extract checksum
|
||||||
|
if (access(default_pad, R_OK) == 0) {
|
||||||
|
// Extract checksum from pad filename
|
||||||
|
char* filename = strrchr(default_pad, '/');
|
||||||
|
if (!filename) filename = default_pad;
|
||||||
|
else filename++; // Skip the '/'
|
||||||
|
|
||||||
|
// Extract checksum (remove .pad extension)
|
||||||
|
if (strlen(filename) >= 68 && strstr(filename, ".pad")) {
|
||||||
|
char pad_checksum[65];
|
||||||
|
strncpy(pad_checksum, filename, 64);
|
||||||
|
pad_checksum[64] = '\0';
|
||||||
|
|
||||||
|
free(default_pad);
|
||||||
|
|
||||||
|
// Encrypt using the default pad (silent mode)
|
||||||
|
return encrypt_text(pad_checksum, piped_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Error: Default pad not found or invalid: %s\n", default_pad);
|
||||||
|
free(default_pad);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Error: No default pad configured for pipe mode\n");
|
||||||
|
fprintf(stderr, "Configure a default pad in ~/.otp/otp.conf\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preferences management functions implementation
|
||||||
|
int load_preferences(void) {
|
||||||
|
char* home_dir = getenv("HOME");
|
||||||
|
if (!home_dir) {
|
||||||
|
return 1; // No home directory
|
||||||
|
}
|
||||||
|
|
||||||
|
char preferences_dir[1024];
|
||||||
|
char preferences_file[2048]; // Increased buffer size to accommodate longer paths
|
||||||
|
snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir);
|
||||||
|
snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir);
|
||||||
|
|
||||||
|
FILE* file = fopen(preferences_file, "r");
|
||||||
|
if (!file) {
|
||||||
|
return 0; // No preferences file, use defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[1024];
|
||||||
|
while (fgets(line, sizeof(line), file)) {
|
||||||
|
// Remove newline
|
||||||
|
line[strcspn(line, "\n")] = 0;
|
||||||
|
|
||||||
|
// Skip empty lines and comments
|
||||||
|
if (strlen(line) == 0 || line[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse key=value pairs
|
||||||
|
char* equals = strchr(line, '=');
|
||||||
|
if (equals) {
|
||||||
|
*equals = '\0';
|
||||||
|
char* key = line;
|
||||||
|
char* value = equals + 1;
|
||||||
|
|
||||||
|
// Trim whitespace
|
||||||
|
while (*key == ' ' || *key == '\t') key++;
|
||||||
|
while (*value == ' ' || *value == '\t') value++;
|
||||||
|
|
||||||
|
if (strcmp(key, "default_pad") == 0) {
|
||||||
|
strncpy(default_pad_path, value, sizeof(default_pad_path) - 1);
|
||||||
|
default_pad_path[sizeof(default_pad_path) - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_preferences(void) {
|
||||||
|
char* home_dir = getenv("HOME");
|
||||||
|
if (!home_dir) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char preferences_dir[1024];
|
||||||
|
char preferences_file[2048]; // Increased buffer size to accommodate longer paths
|
||||||
|
snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir);
|
||||||
|
snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir);
|
||||||
|
|
||||||
|
// Create .otp directory if it doesn't exist
|
||||||
|
struct stat st = {0};
|
||||||
|
if (stat(preferences_dir, &st) == -1) {
|
||||||
|
if (mkdir(preferences_dir, 0755) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* file = fopen(preferences_file, "w");
|
||||||
|
if (!file) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(file, "# OTP Preferences File\n");
|
||||||
|
fprintf(file, "# This file is automatically generated and updated by the OTP program\n\n");
|
||||||
|
|
||||||
|
if (strlen(default_pad_path) > 0) {
|
||||||
|
fprintf(file, "default_pad=%s\n", default_pad_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_preference(const char* key) {
|
||||||
|
if (strcmp(key, "default_pad") == 0) {
|
||||||
|
if (strlen(default_pad_path) > 0) {
|
||||||
|
return strdup(default_pad_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_preference(const char* key, const char* value) {
|
||||||
|
if (strcmp(key, "default_pad") == 0) {
|
||||||
|
if (value) {
|
||||||
|
strncpy(default_pad_path, value, sizeof(default_pad_path) - 1);
|
||||||
|
default_pad_path[sizeof(default_pad_path) - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
default_pad_path[0] = '\0';
|
||||||
|
}
|
||||||
|
return save_preferences();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_default_pad_path(void) {
|
||||||
|
if (strlen(default_pad_path) > 0) {
|
||||||
|
return strdup(default_pad_path);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_default_pad_path(const char* pad_path) {
|
||||||
|
return set_preference("default_pad", pad_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OTP thumb drive detection function implementation
|
||||||
|
int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) {
|
||||||
|
const char* mount_dirs[] = {"/media", "/run/media", "/mnt", NULL};
|
||||||
|
|
||||||
|
for (int mount_idx = 0; mount_dirs[mount_idx] != NULL; mount_idx++) {
|
||||||
|
DIR* mount_dir = opendir(mount_dirs[mount_idx]);
|
||||||
|
if (!mount_dir) continue;
|
||||||
|
|
||||||
|
struct dirent* mount_entry;
|
||||||
|
while ((mount_entry = readdir(mount_dir)) != NULL) {
|
||||||
|
if (mount_entry->d_name[0] == '.') continue;
|
||||||
|
|
||||||
|
char mount_path[1024]; // Increased buffer size
|
||||||
|
snprintf(mount_path, sizeof(mount_path), "%s/%s", mount_dirs[mount_idx], mount_entry->d_name);
|
||||||
|
|
||||||
|
// For /media, we need to go one level deeper (user directories)
|
||||||
|
if (strcmp(mount_dirs[mount_idx], "/media") == 0) {
|
||||||
|
// This is /media/[username] - look inside for drives
|
||||||
|
DIR* user_dir = opendir(mount_path);
|
||||||
|
if (!user_dir) continue;
|
||||||
|
|
||||||
|
struct dirent* user_entry;
|
||||||
|
while ((user_entry = readdir(user_dir)) != NULL) {
|
||||||
|
if (user_entry->d_name[0] == '.') continue;
|
||||||
|
|
||||||
|
// Check if drive name starts with "OTP"
|
||||||
|
if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue;
|
||||||
|
|
||||||
|
char user_mount_path[2048]; // Increased buffer size
|
||||||
|
// Verify buffer has enough space before concatenation
|
||||||
|
size_t mount_len = strlen(mount_path);
|
||||||
|
size_t entry_len = strlen(user_entry->d_name);
|
||||||
|
if (mount_len + entry_len + 2 < sizeof(user_mount_path)) {
|
||||||
|
snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name);
|
||||||
|
|
||||||
|
// Check if this is a readable directory
|
||||||
|
DIR* drive_dir = opendir(user_mount_path);
|
||||||
|
if (drive_dir) {
|
||||||
|
closedir(drive_dir);
|
||||||
|
strncpy(otp_drive_path, user_mount_path, path_size - 1);
|
||||||
|
otp_drive_path[path_size - 1] = '\0';
|
||||||
|
closedir(user_dir);
|
||||||
|
closedir(mount_dir);
|
||||||
|
return 1; // Found OTP drive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(user_dir);
|
||||||
|
} else if (strcmp(mount_dirs[mount_idx], "/run/media") == 0) {
|
||||||
|
// For /run/media, we need to go one level deeper (skip username)
|
||||||
|
DIR* user_dir = opendir(mount_path);
|
||||||
|
if (!user_dir) continue;
|
||||||
|
|
||||||
|
struct dirent* user_entry;
|
||||||
|
while ((user_entry = readdir(user_dir)) != NULL) {
|
||||||
|
if (user_entry->d_name[0] == '.') continue;
|
||||||
|
|
||||||
|
// Check if drive name starts with "OTP"
|
||||||
|
if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue;
|
||||||
|
|
||||||
|
char user_mount_path[2048]; // Increased buffer size
|
||||||
|
snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name);
|
||||||
|
|
||||||
|
// Check if this is a readable directory
|
||||||
|
DIR* drive_dir = opendir(user_mount_path);
|
||||||
|
if (drive_dir) {
|
||||||
|
closedir(drive_dir);
|
||||||
|
strncpy(otp_drive_path, user_mount_path, path_size - 1);
|
||||||
|
otp_drive_path[path_size - 1] = '\0';
|
||||||
|
closedir(user_dir);
|
||||||
|
closedir(mount_dir);
|
||||||
|
return 1; // Found OTP drive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(user_dir);
|
||||||
|
} else {
|
||||||
|
// Direct mount point (like /mnt/OTP_DRIVE)
|
||||||
|
// Check if drive name starts with "OTP"
|
||||||
|
if (strncmp(mount_entry->d_name, "OTP", 3) == 0) {
|
||||||
|
DIR* drive_dir = opendir(mount_path);
|
||||||
|
if (drive_dir) {
|
||||||
|
closedir(drive_dir);
|
||||||
|
strncpy(otp_drive_path, mount_path, path_size - 1);
|
||||||
|
otp_drive_path[path_size - 1] = '\0';
|
||||||
|
closedir(mount_dir);
|
||||||
|
return 1; // Found OTP drive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(mount_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // No OTP drive found
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Custom base64 encode function
|
// Custom base64 encode function
|
||||||
@@ -2832,10 +3380,10 @@ int handle_file_encrypt(void) {
|
|||||||
int handle_pads_menu(void) {
|
int handle_pads_menu(void) {
|
||||||
printf("\n=== Pad Management ===\n");
|
printf("\n=== Pad Management ===\n");
|
||||||
|
|
||||||
// Get list of all pads
|
// Get list of pads from current directory
|
||||||
DIR* dir = opendir(PADS_DIR);
|
DIR* dir = opendir(current_pads_dir);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
printf("Error: Cannot open pads directory\n");
|
printf("Error: Cannot open pads directory %s\n", current_pads_dir);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2845,6 +3393,7 @@ int handle_pads_menu(void) {
|
|||||||
char size_str[32];
|
char size_str[32];
|
||||||
char used_str[32];
|
char used_str[32];
|
||||||
double percentage;
|
double percentage;
|
||||||
|
char location[256]; // Store location info
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PadInfo pads[100]; // Support up to 100 pads
|
struct PadInfo pads[100]; // Support up to 100 pads
|
||||||
@@ -2858,8 +3407,8 @@ int handle_pads_menu(void) {
|
|||||||
pads[pad_count].chksum[64] = '\0';
|
pads[pad_count].chksum[64] = '\0';
|
||||||
|
|
||||||
// Get pad file size and usage info
|
// Get pad file size and usage info
|
||||||
char full_path[300];
|
char full_path[1024]; // Increased buffer size
|
||||||
snprintf(full_path, sizeof(full_path), "%s/%s", PADS_DIR, entry->d_name);
|
snprintf(full_path, sizeof(full_path), "%s/%s", current_pads_dir, entry->d_name);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(full_path, &st) == 0) {
|
if (stat(full_path, &st) == 0) {
|
||||||
// Get used bytes from state
|
// Get used bytes from state
|
||||||
@@ -2891,6 +3440,9 @@ int handle_pads_menu(void) {
|
|||||||
// Calculate percentage
|
// Calculate percentage
|
||||||
pads[pad_count].percentage = (double)used_bytes / st.st_size * 100.0;
|
pads[pad_count].percentage = (double)used_bytes / st.st_size * 100.0;
|
||||||
|
|
||||||
|
// Set location info using directory display
|
||||||
|
get_directory_display(full_path, pads[pad_count].location, sizeof(pads[pad_count].location));
|
||||||
|
|
||||||
pad_count++;
|
pad_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2906,10 +3458,15 @@ int handle_pads_menu(void) {
|
|||||||
|
|
||||||
char input[10];
|
char input[10];
|
||||||
if (fgets(input, sizeof(input), stdin)) {
|
if (fgets(input, sizeof(input), stdin)) {
|
||||||
char choice = toupper(input[0]);
|
char choice = toupper(input[0]);
|
||||||
if (choice == 'G') {
|
if (choice == 'G') {
|
||||||
return handle_generate_menu();
|
int result = handle_generate_menu();
|
||||||
|
if (result == 0) {
|
||||||
|
// After successful pad generation, return to pads menu
|
||||||
|
return handle_pads_menu();
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -2950,12 +3507,6 @@ int handle_pads_menu(void) {
|
|||||||
printf("%-8s %-12s %-12s %-12s %-8s\n", "--------", "------------", "----------", "----------", "------");
|
printf("%-8s %-12s %-12s %-12s %-8s\n", "--------", "------------", "----------", "----------", "------");
|
||||||
|
|
||||||
for (int i = 0; i < pad_count; i++) {
|
for (int i = 0; i < pad_count; i++) {
|
||||||
// Get directory information for this pad
|
|
||||||
char full_path[300];
|
|
||||||
snprintf(full_path, sizeof(full_path), "%s/%s.pad", PADS_DIR, pads[i].chksum);
|
|
||||||
char dir_display[13]; // 12 chars + null terminator
|
|
||||||
get_directory_display(full_path, dir_display, sizeof(dir_display));
|
|
||||||
|
|
||||||
// Display first 8 characters of checksum with prefix underlined
|
// Display first 8 characters of checksum with prefix underlined
|
||||||
char checksum_8char[9];
|
char checksum_8char[9];
|
||||||
strncpy(checksum_8char, pads[i].chksum, 8);
|
strncpy(checksum_8char, pads[i].chksum, 8);
|
||||||
@@ -2964,7 +3515,7 @@ int handle_pads_menu(void) {
|
|||||||
printf("\033[4m%.*s\033[0m%s %-12s %-12s %-12s %.1f%%\n",
|
printf("\033[4m%.*s\033[0m%s %-12s %-12s %-12s %.1f%%\n",
|
||||||
prefix_lengths[i], checksum_8char, // Underlined prefix
|
prefix_lengths[i], checksum_8char, // Underlined prefix
|
||||||
checksum_8char + prefix_lengths[i], // Rest of 8-char checksum
|
checksum_8char + prefix_lengths[i], // Rest of 8-char checksum
|
||||||
dir_display,
|
pads[i].location, // Use the stored location info
|
||||||
pads[i].size_str,
|
pads[i].size_str,
|
||||||
pads[i].used_str,
|
pads[i].used_str,
|
||||||
pads[i].percentage);
|
pads[i].percentage);
|
||||||
@@ -2984,7 +3535,12 @@ int handle_pads_menu(void) {
|
|||||||
|
|
||||||
// Handle actions first
|
// Handle actions first
|
||||||
if (toupper(input[0]) == 'G') {
|
if (toupper(input[0]) == 'G') {
|
||||||
return handle_generate_menu();
|
int result = handle_generate_menu();
|
||||||
|
if (result == 0) {
|
||||||
|
// After successful pad generation, return to pads menu
|
||||||
|
return handle_pads_menu();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
} else if (toupper(input[0]) == 'B') {
|
} else if (toupper(input[0]) == 'B') {
|
||||||
return 0; // Back to main menu
|
return 0; // Back to main menu
|
||||||
}
|
}
|
||||||
@@ -3071,8 +3627,15 @@ void get_directory_display(const char* file_path, char* result, size_t result_si
|
|||||||
if (path_after_media) {
|
if (path_after_media) {
|
||||||
path_after_media++; // Skip the slash
|
path_after_media++; // Skip the slash
|
||||||
|
|
||||||
|
// For /media/user/LABEL pattern, skip the username to get to the drive label
|
||||||
|
if (strstr(media_start, "/media/")) {
|
||||||
|
char* next_slash = strchr(path_after_media, '/');
|
||||||
|
if (next_slash) {
|
||||||
|
path_after_media = next_slash + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
// For /run/media/user/LABEL pattern, skip the username
|
// For /run/media/user/LABEL pattern, skip the username
|
||||||
if (strstr(media_start, "/run/media/")) {
|
else if (strstr(media_start, "/run/media/")) {
|
||||||
char* next_slash = strchr(path_after_media, '/');
|
char* next_slash = strchr(path_after_media, '/');
|
||||||
if (next_slash) {
|
if (next_slash) {
|
||||||
path_after_media = next_slash + 1;
|
path_after_media = next_slash + 1;
|
||||||
@@ -3081,15 +3644,23 @@ void get_directory_display(const char* file_path, char* result, size_t result_si
|
|||||||
|
|
||||||
// Extract just the USB label (up to next slash or end)
|
// Extract just the USB label (up to next slash or end)
|
||||||
char* label_end = strchr(path_after_media, '/');
|
char* label_end = strchr(path_after_media, '/');
|
||||||
|
char usb_label[32];
|
||||||
if (label_end) {
|
if (label_end) {
|
||||||
size_t label_len = label_end - path_after_media;
|
size_t label_len = label_end - path_after_media;
|
||||||
if (label_len > 11) label_len = 11; // Max 11 chars for display
|
if (label_len > sizeof(usb_label) - 1) label_len = sizeof(usb_label) - 1;
|
||||||
strncpy(result, path_after_media, label_len);
|
strncpy(usb_label, path_after_media, label_len);
|
||||||
result[label_len] = '\0';
|
usb_label[label_len] = '\0';
|
||||||
} else {
|
} else {
|
||||||
// USB label is the last part
|
// USB label is the last part
|
||||||
strncpy(result, path_after_media, result_size - 1);
|
strncpy(usb_label, path_after_media, sizeof(usb_label) - 1);
|
||||||
result[result_size - 1] = '\0';
|
usb_label[sizeof(usb_label) - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format with USB: prefix, limiting total length to fit in result
|
||||||
|
snprintf(result, result_size, "USB:%s", usb_label);
|
||||||
|
// Truncate if too long
|
||||||
|
if (strlen(result) > 11) {
|
||||||
|
result[11] = '\0';
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3122,7 +3693,7 @@ void get_directory_display(const char* file_path, char* result, size_t result_si
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Current working directory
|
// Current working directory
|
||||||
if (strcmp(dir_path, ".") == 0 || strcmp(dir_path, PADS_DIR) == 0) {
|
if (strcmp(dir_path, ".") == 0 || strcmp(dir_path, current_pads_dir) == 0) {
|
||||||
strncpy(result, "pads", result_size - 1);
|
strncpy(result, "pads", result_size - 1);
|
||||||
result[result_size - 1] = '\0';
|
result[result_size - 1] = '\0';
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
Testing updated files directory functionality
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Testing files directory functionality
|
|
||||||
BIN
test_new.txt.otp
BIN
test_new.txt.otp
Binary file not shown.
Reference in New Issue
Block a user