Compare commits

...

9 Commits

6 changed files with 215 additions and 60 deletions

View File

@@ -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

View File

@@ -1 +0,0 @@
Test file content for decryption

248
otp.c
View File

@@ -81,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);
@@ -134,18 +135,28 @@ int main(int argc, char* argv[]) {
// Load preferences first // Load preferences first
load_preferences(); 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 // Check for OTP thumb drive on startup
char otp_drive_path[512]; char otp_drive_path[512];
if (detect_otp_thumb_drive(otp_drive_path, sizeof(otp_drive_path))) { if (detect_otp_thumb_drive(otp_drive_path, sizeof(otp_drive_path))) {
printf("Detected OTP thumb drive: %s\n", otp_drive_path); // Only show messages in interactive/command mode, not pipe mode
printf("Using as default pads directory for this session.\n\n"); 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); strncpy(current_pads_dir, otp_drive_path, sizeof(current_pads_dir) - 1);
current_pads_dir[sizeof(current_pads_dir) - 1] = '\0'; current_pads_dir[sizeof(current_pads_dir) - 1] = '\0';
} }
// Check for piped input if (is_pipe_mode) {
if (argc == 1 && has_stdin_data()) { // No arguments but has piped data - enter pipe mode
// No arguments but has piped data - enter pipe mode for interactive pad selection
char* piped_text = read_stdin_text(); char* piped_text = read_stdin_text();
if (piped_text) { if (piped_text) {
int result = pipe_mode(argc, argv, piped_text); int result = pipe_mode(argc, argv, piped_text);
@@ -233,6 +244,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);
} }
@@ -1385,8 +1406,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);
@@ -1398,7 +1426,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);
@@ -1583,6 +1615,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) {
@@ -2409,8 +2570,6 @@ int pipe_mode(int argc, char* argv[], const char* piped_text) {
(void)argc; // Suppress unused parameter warning (void)argc; // Suppress unused parameter warning
(void)argv; // Suppress unused parameter warning (void)argv; // Suppress unused parameter warning
printf("Piped text received: \"%s\"\n", piped_text);
// Check if we have a default pad configured // Check if we have a default pad configured
char* default_pad = get_default_pad_path(); char* default_pad = get_default_pad_path();
if (default_pad) { if (default_pad) {
@@ -2427,49 +2586,21 @@ int pipe_mode(int argc, char* argv[], const char* piped_text) {
strncpy(pad_checksum, filename, 64); strncpy(pad_checksum, filename, 64);
pad_checksum[64] = '\0'; pad_checksum[64] = '\0';
printf("Using default pad: %.16s...\n\n", pad_checksum);
free(default_pad); free(default_pad);
// Encrypt using the default pad // Encrypt using the default pad (silent mode)
return encrypt_text(pad_checksum, piped_text); return encrypt_text(pad_checksum, piped_text);
} }
} }
printf("Warning: Default pad not found or invalid: %s\n", default_pad); fprintf(stderr, "Error: Default pad not found or invalid: %s\n", default_pad);
free(default_pad); free(default_pad);
// Fall through to interactive selection
}
printf("No default pad configured.\n\n");
// List available pads for interactive selection
int pad_count = list_available_pads();
if (pad_count == 0) {
printf("No pads available. Generate a pad first.\n");
return 1; return 1;
} }
// Reopen stdin from the controlling terminal for interactive input fprintf(stderr, "Error: No default pad configured for pipe mode\n");
FILE* tty = fopen("/dev/tty", "r"); fprintf(stderr, "Configure a default pad in ~/.otp/otp.conf\n");
if (!tty) { return 1;
printf("Error: Cannot open terminal for input\n");
return 1;
}
printf("\nEnter pad selection (number, checksum, or prefix): ");
fflush(stdout);
char pad_input[MAX_HASH_LENGTH];
if (!fgets(pad_input, sizeof(pad_input), tty)) {
printf("Error: Failed to read pad selection\n");
fclose(tty);
return 1;
}
pad_input[strcspn(pad_input, "\n")] = 0;
fclose(tty);
// Encrypt the piped text
return encrypt_text(pad_input, piped_text);
} }
// Preferences management functions implementation // Preferences management functions implementation
@@ -2601,7 +2732,7 @@ int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) {
while ((mount_entry = readdir(mount_dir)) != NULL) { while ((mount_entry = readdir(mount_dir)) != NULL) {
if (mount_entry->d_name[0] == '.') continue; if (mount_entry->d_name[0] == '.') continue;
char mount_path[512]; char mount_path[1024]; // Increased buffer size
snprintf(mount_path, sizeof(mount_path), "%s/%s", mount_dirs[mount_idx], mount_entry->d_name); 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) // For /media, we need to go one level deeper (user directories)
@@ -2617,18 +2748,23 @@ int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) {
// Check if drive name starts with "OTP" // Check if drive name starts with "OTP"
if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue; if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue;
char user_mount_path[512]; char user_mount_path[1024]; // Increased buffer size
snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name); // 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 // Check if this is a readable directory
DIR* drive_dir = opendir(user_mount_path); DIR* drive_dir = opendir(user_mount_path);
if (drive_dir) { if (drive_dir) {
closedir(drive_dir); closedir(drive_dir);
strncpy(otp_drive_path, user_mount_path, path_size - 1); strncpy(otp_drive_path, user_mount_path, path_size - 1);
otp_drive_path[path_size - 1] = '\0'; otp_drive_path[path_size - 1] = '\0';
closedir(user_dir); closedir(user_dir);
closedir(mount_dir); closedir(mount_dir);
return 1; // Found OTP drive return 1; // Found OTP drive
}
} }
} }
closedir(user_dir); closedir(user_dir);
@@ -3261,7 +3397,7 @@ 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[512]; char full_path[1024]; // Increased buffer size
snprintf(full_path, sizeof(full_path), "%s/%s", current_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) {

View File

@@ -1 +0,0 @@
Testing updated files directory functionality

View File

@@ -1 +0,0 @@
Testing files directory functionality

Binary file not shown.