Compare commits

...

5 Commits

2 changed files with 450 additions and 304 deletions

View File

@@ -3,9 +3,6 @@
## Change technique for adding keyboard entropy. ## 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. ## 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.
@@ -15,9 +12,6 @@ Or, better yet, assume the offset is a very large size, and use the pad itself t
## Take a look at how the file header is being handled. ## 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 ## Setup for multiple USB drives

746
otp.c
View File

@@ -111,6 +111,15 @@ void show_progress(uint64_t current, uint64_t total, time_t start_time);
int read_state_offset(const char* pad_chksum, uint64_t* offset); 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);
int calculate_checksum(const char* filename, char* checksum_hex); int calculate_checksum(const char* filename, char* checksum_hex);
// Universal core functions for code consolidation
int universal_xor_operation(const unsigned char* data, size_t data_len,
const unsigned char* pad_data, unsigned char* result);
int parse_ascii_message(const char* message, char* chksum, uint64_t* offset, char* base64_data);
int load_pad_data(const char* pad_chksum, uint64_t offset, size_t length, unsigned char** pad_data);
int generate_ascii_armor(const char* chksum, uint64_t offset, const unsigned char* encrypted_data,
size_t data_length, char** ascii_output);
int validate_pad_integrity(const char* pad_path, const char* expected_chksum);
char* custom_base64_encode(const unsigned char* input, int length); char* custom_base64_encode(const unsigned char* input, int length);
unsigned char* custom_base64_decode(const char* input, int* output_length); unsigned char* custom_base64_decode(const char* input, int* output_length);
@@ -244,13 +253,68 @@ int command_line_mode(int argc, char* argv[]) {
return generate_pad_with_entropy(size, 1, 0); // No keyboard entropy for command line return generate_pad_with_entropy(size, 1, 0); // No keyboard entropy for command line
} }
else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) { else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) {
if (argc < 3 || argc > 4) { if (argc < 2 || argc > 4) {
printf("Usage: %s encrypt|-e <pad_chksum_or_prefix> [text_to_encrypt]\n", argv[0]); printf("Usage: %s encrypt|-e [pad_chksum_or_prefix] [text_to_encrypt]\n", argv[0]);
return 1; return 1;
} }
// Pass text if provided, otherwise NULL for interactive mode
const char* text = (argc == 4) ? argv[3] : NULL; // Check if pad was specified or use default
return encrypt_text(argv[2], text); const char* pad_identifier = NULL;
const char* text = NULL;
if (argc == 2) {
// Just -e, use default pad, no text (interactive)
pad_identifier = NULL;
text = NULL;
} else if (argc == 3) {
// Could be -e <pad> or -e <text> (using default pad)
// Check if default pad is available to determine interpretation
char* default_pad = get_default_pad_path();
if (default_pad) {
// Default pad available, treat argument as text
pad_identifier = NULL;
text = argv[2];
free(default_pad);
} else {
// No default pad, treat as pad identifier
pad_identifier = argv[2];
text = NULL;
}
} else {
// argc == 4: -e <pad> <text>
pad_identifier = argv[2];
text = argv[3];
}
// If pad_identifier is NULL, we need to use default pad
if (pad_identifier == NULL) {
char* default_pad = get_default_pad_path();
if (default_pad) {
// Extract checksum from default pad path
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")) {
static char default_checksum[65];
strncpy(default_checksum, filename, 64);
default_checksum[64] = '\0';
pad_identifier = default_checksum;
}
free(default_pad);
// Call encrypt_text and then comment about using default pad
int result = encrypt_text(pad_identifier, text);
return result;
} else {
printf("Error: No default pad configured. Specify pad explicitly or configure default pad.\n");
return 1;
}
} else {
// Explicit pad specified, normal operation
return encrypt_text(pad_identifier, text);
}
} }
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) {
@@ -1424,50 +1488,44 @@ int encrypt_text(const char* pad_identifier, const char* input_text) {
} }
fclose(pad_file); fclose(pad_file);
// XOR encrypt the input // Use universal XOR operation for encryption
unsigned char* ciphertext = malloc(input_len); unsigned char* ciphertext = malloc(input_len);
for (size_t i = 0; i < input_len; i++) { if (universal_xor_operation((const unsigned char*)text_buffer, input_len, pad_data, ciphertext) != 0) {
ciphertext[i] = text_buffer[i] ^ pad_data[i]; printf("Error: Encryption operation failed\n");
free(pad_data);
free(ciphertext);
free(pad_chksum);
return 1;
} }
// Encode as base64
char* base64_cipher = custom_base64_encode(ciphertext, input_len);
// Update state offset // Update state offset
if (write_state_offset(pad_chksum, current_offset + input_len) != 0) { if (write_state_offset(pad_chksum, current_offset + input_len) != 0) {
printf("Warning: Failed to update state file\n"); printf("Warning: Failed to update state file\n");
} }
// Output in ASCII armor format - clean format for piping, spaced format for interactive // Use universal ASCII armor generator
char* ascii_output;
if (generate_ascii_armor(chksum_hex, current_offset, ciphertext, input_len, &ascii_output) != 0) {
printf("Error: Failed to generate ASCII armor\n");
free(pad_data);
free(ciphertext);
free(pad_chksum);
return 1;
}
// Output with appropriate formatting - clean format for piping, spaced format for interactive
int is_interactive = (input_text == NULL); // Interactive if no input_text provided int is_interactive = (input_text == NULL); // Interactive if no input_text provided
if (is_interactive) { if (is_interactive) {
printf("\n\n-----BEGIN OTP MESSAGE-----\n"); printf("\n\n%s\n\n", ascii_output);
} else { } else {
printf("-----BEGIN OTP MESSAGE-----\n"); printf("%s", ascii_output);
}
printf("Version: %s\n", get_version());
printf("Pad-ChkSum: %s\n", chksum_hex);
printf("Pad-Offset: %lu\n", current_offset);
printf("\n");
// Print base64 data in 64-character lines
int b64_len = strlen(base64_cipher);
for (int i = 0; i < b64_len; i += 64) {
printf("%.64s\n", base64_cipher + i);
}
if (is_interactive) {
printf("-----END OTP MESSAGE-----\n\n\n");
} else {
printf("-----END OTP MESSAGE-----\n");
} }
// Cleanup // Cleanup
free(pad_data); free(pad_data);
free(ciphertext); free(ciphertext);
free(base64_cipher); free(ascii_output);
free(pad_chksum); free(pad_chksum);
return 0; return 0;
@@ -1477,46 +1535,13 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) {
// For command line mode, pad_identifier is ignored - we'll get the chksum from the message // For command line mode, pad_identifier is ignored - we'll get the chksum from the message
(void)pad_identifier; // Suppress unused parameter warning (void)pad_identifier; // Suppress unused parameter warning
char line[MAX_LINE_LENGTH];
char stored_chksum[MAX_HASH_LENGTH]; char stored_chksum[MAX_HASH_LENGTH];
char current_chksum[MAX_HASH_LENGTH];
uint64_t pad_offset; uint64_t pad_offset;
char base64_data[MAX_INPUT_SIZE * 2] = {0}; char base64_data[MAX_INPUT_SIZE * 2] = {0};
int in_data_section = 0;
if (encrypted_message != NULL) { if (encrypted_message != NULL) {
// Parse provided encrypted message // Use universal ASCII parser to extract message components
char *message_copy = strdup(encrypted_message); if (parse_ascii_message(encrypted_message, stored_chksum, &pad_offset, base64_data) != 0) {
char *line_ptr = strtok(message_copy, "\n");
int found_begin = 0;
while (line_ptr != NULL) {
if (strcmp(line_ptr, "-----BEGIN OTP MESSAGE-----") == 0) {
found_begin = 1;
}
else if (strcmp(line_ptr, "-----END OTP MESSAGE-----") == 0) {
break;
}
else if (found_begin) {
if (strncmp(line_ptr, "Pad-ChkSum: ", 12) == 0) {
strncpy(stored_chksum, line_ptr + 12, 64);
stored_chksum[64] = '\0';
}
else if (strncmp(line_ptr, "Pad-Offset: ", 12) == 0) {
pad_offset = strtoull(line_ptr + 12, NULL, 10);
}
else if (strlen(line_ptr) == 0) {
in_data_section = 1;
}
else if (in_data_section) {
strncat(base64_data, line_ptr, sizeof(base64_data) - strlen(base64_data) - 1);
}
}
line_ptr = strtok(NULL, "\n");
}
free(message_copy);
if (!found_begin) {
printf("Error: Invalid message format - missing BEGIN header\n"); printf("Error: Invalid message format - missing BEGIN header\n");
return 1; return 1;
} }
@@ -1524,49 +1549,29 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) {
// Interactive mode - read from stdin // Interactive mode - read from stdin
printf("Enter encrypted message (paste the full ASCII armor block):\n"); printf("Enter encrypted message (paste the full ASCII armor block):\n");
// Read the ASCII armor format // Read entire message from stdin
int found_begin = 0; char full_message[MAX_INPUT_SIZE * 4] = {0};
char line[MAX_LINE_LENGTH];
while (fgets(line, sizeof(line), stdin)) { while (fgets(line, sizeof(line), stdin)) {
line[strcspn(line, "\n")] = 0; strncat(full_message, line, sizeof(full_message) - strlen(full_message) - 1);
if (strstr(line, "-----END OTP MESSAGE-----")) {
if (strcmp(line, "-----BEGIN OTP MESSAGE-----") == 0) {
found_begin = 1;
continue;
}
if (strcmp(line, "-----END OTP MESSAGE-----") == 0) {
break; break;
} }
if (!found_begin) continue;
if (strncmp(line, "Pad-ChkSum: ", 12) == 0) {
strncpy(stored_chksum, line + 12, 64);
stored_chksum[64] = '\0';
}
else if (strncmp(line, "Pad-Offset: ", 12) == 0) {
pad_offset = strtoull(line + 12, NULL, 10);
}
else if (strlen(line) == 0) {
in_data_section = 1;
}
else if (in_data_section) {
strncat(base64_data, line, sizeof(base64_data) - strlen(base64_data) - 1);
}
} }
if (!found_begin) { // Use universal ASCII parser
if (parse_ascii_message(full_message, stored_chksum, &pad_offset, base64_data) != 0) {
printf("Error: Invalid message format - missing BEGIN header\n"); printf("Error: Invalid message format - missing BEGIN header\n");
return 1; return 1;
} }
} }
// Now we have the pad chksum from the message, construct filename // Get pad path for integrity check
char pad_path[MAX_HASH_LENGTH + 20]; char pad_path[MAX_HASH_LENGTH + 20];
char state_path[MAX_HASH_LENGTH + 20]; char state_path[MAX_HASH_LENGTH + 20];
get_pad_path(stored_chksum, pad_path, state_path); get_pad_path(stored_chksum, pad_path, state_path);
// Check if we have this pad // Check if pad exists
if (access(pad_path, R_OK) != 0) { if (access(pad_path, R_OK) != 0) {
printf("Error: Required pad not found: %s\n", stored_chksum); printf("Error: Required pad not found: %s\n", stored_chksum);
printf("Available pads:\n"); printf("Available pads:\n");
@@ -1574,16 +1579,11 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) {
return 1; return 1;
} }
// Verify pad integrity // Use universal pad integrity validator
if (calculate_checksum(pad_path, current_chksum) != 0) { int integrity_result = validate_pad_integrity(pad_path, stored_chksum);
printf("Error: Cannot calculate current pad checksum\n"); if (integrity_result == 3) {
return 1;
}
if (strcmp(stored_chksum, current_chksum) != 0) {
printf("Warning: Pad integrity check failed!\n"); printf("Warning: Pad integrity check failed!\n");
printf("Expected: %s\n", stored_chksum); printf("Expected: %s\n", stored_chksum);
printf("Current: %s\n", current_chksum);
printf("Continue anyway? (y/N): "); printf("Continue anyway? (y/N): ");
fflush(stdout); fflush(stdout);
@@ -1593,11 +1593,14 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) {
printf("Decryption aborted.\n"); printf("Decryption aborted.\n");
return 1; return 1;
} }
} else if (integrity_result != 0) {
printf("Error: Cannot verify pad integrity\n");
return 1;
} else { } else {
printf("Pad integrity: VERIFIED\n"); printf("Pad integrity: VERIFIED\n");
} }
// Decode base64 // Decode base64 ciphertext
int ciphertext_len; int ciphertext_len;
unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len);
if (!ciphertext) { if (!ciphertext) {
@@ -1605,36 +1608,24 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) {
return 1; return 1;
} }
// Read pad data at specified offset // Use universal pad loader
FILE* pad_file = fopen(pad_path, "rb"); unsigned char* pad_data;
if (!pad_file) { int load_result = load_pad_data(stored_chksum, pad_offset, ciphertext_len, &pad_data);
printf("Error: Cannot open pad file %s\n", pad_path); if (load_result != 0) {
printf("Error: Cannot load pad data (code %d)\n", load_result);
free(ciphertext); free(ciphertext);
return 1; return 1;
} }
if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { // Use universal XOR operation for decryption
printf("Error: Cannot seek to offset %lu in pad file\n", pad_offset); char* plaintext = malloc(ciphertext_len + 1);
free(ciphertext); if (universal_xor_operation(ciphertext, ciphertext_len, pad_data, (unsigned char*)plaintext) != 0) {
fclose(pad_file); printf("Error: Decryption operation failed\n");
return 1;
}
unsigned char* pad_data = malloc(ciphertext_len);
if (fread(pad_data, 1, ciphertext_len, pad_file) != (size_t)ciphertext_len) {
printf("Error: Cannot read pad data\n");
free(ciphertext); free(ciphertext);
free(pad_data); free(pad_data);
fclose(pad_file); free(plaintext);
return 1; 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'; plaintext[ciphertext_len] = '\0';
printf("Decrypted: %s\n", plaintext); printf("Decrypted: %s\n", plaintext);
@@ -1652,79 +1643,38 @@ int decrypt_text_silent(const char* pad_identifier, const char* encrypted_messag
(void)pad_identifier; // Suppress unused parameter warning (void)pad_identifier; // Suppress unused parameter warning
char stored_chksum[MAX_HASH_LENGTH]; char stored_chksum[MAX_HASH_LENGTH];
char current_chksum[MAX_HASH_LENGTH];
uint64_t pad_offset; uint64_t pad_offset;
char base64_data[MAX_INPUT_SIZE * 2] = {0}; char base64_data[MAX_INPUT_SIZE * 2] = {0};
int in_data_section = 0;
if (encrypted_message != NULL) { 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"); fprintf(stderr, "Error: No encrypted message provided\n");
return 1; return 1;
} }
// Now we have the pad chksum from the message, construct filename // Use universal ASCII parser to extract message components
if (parse_ascii_message(encrypted_message, stored_chksum, &pad_offset, base64_data) != 0) {
fprintf(stderr, "Error: Invalid message format - missing BEGIN header\n");
return 1;
}
// Get pad path for integrity check
char pad_path[MAX_HASH_LENGTH + 20]; char pad_path[MAX_HASH_LENGTH + 20];
char state_path[MAX_HASH_LENGTH + 20]; char state_path[MAX_HASH_LENGTH + 20];
get_pad_path(stored_chksum, pad_path, state_path); get_pad_path(stored_chksum, pad_path, state_path);
// Check if we have this pad // Check if pad exists
if (access(pad_path, R_OK) != 0) { if (access(pad_path, R_OK) != 0) {
fprintf(stderr, "Error: Required pad not found: %s\n", stored_chksum); fprintf(stderr, "Error: Required pad not found: %s\n", stored_chksum);
return 1; return 1;
} }
// Verify pad integrity (silent check) // Use universal pad integrity validator (silent check)
if (calculate_checksum(pad_path, current_chksum) != 0) { if (validate_pad_integrity(pad_path, stored_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"); fprintf(stderr, "Error: Pad integrity check failed!\n");
return 1; return 1;
} }
// Decode base64 // Decode base64 ciphertext
int ciphertext_len; int ciphertext_len;
unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len);
if (!ciphertext) { if (!ciphertext) {
@@ -1732,36 +1682,23 @@ int decrypt_text_silent(const char* pad_identifier, const char* encrypted_messag
return 1; return 1;
} }
// Read pad data at specified offset // Use universal pad loader
FILE* pad_file = fopen(pad_path, "rb"); unsigned char* pad_data;
if (!pad_file) { if (load_pad_data(stored_chksum, pad_offset, ciphertext_len, &pad_data) != 0) {
fprintf(stderr, "Error: Cannot open pad file %s\n", pad_path); fprintf(stderr, "Error: Cannot load pad data\n");
free(ciphertext); free(ciphertext);
return 1; return 1;
} }
if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { // Use universal XOR operation for decryption
fprintf(stderr, "Error: Cannot seek to offset %lu in pad file\n", pad_offset); char* plaintext = malloc(ciphertext_len + 1);
free(ciphertext); if (universal_xor_operation(ciphertext, ciphertext_len, pad_data, (unsigned char*)plaintext) != 0) {
fclose(pad_file); fprintf(stderr, "Error: Decryption operation failed\n");
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(ciphertext);
free(pad_data); free(pad_data);
fclose(pad_file); free(plaintext);
return 1; 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'; plaintext[ciphertext_len] = '\0';
// Output only the decrypted text with newline and flush // Output only the decrypted text with newline and flush
@@ -1925,9 +1862,14 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char*
return 1; return 1;
} }
// XOR encrypt // Use universal XOR operation for encryption
for (uint64_t i = 0; i < chunk_size; i++) { if (universal_xor_operation(buffer, chunk_size, pad_buffer, &encrypted_data[bytes_processed]) != 0) {
encrypted_data[bytes_processed + i] = buffer[i] ^ pad_buffer[i]; printf("Error: Encryption operation failed\n");
free(encrypted_data);
fclose(input_fp);
fclose(pad_file);
free(pad_chksum);
return 1;
} }
bytes_processed += chunk_size; bytes_processed += chunk_size;
@@ -1957,26 +1899,21 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char*
return 1; return 1;
} }
// Encode as base64 // Use universal ASCII armor generator
char* base64_data = custom_base64_encode(encrypted_data, file_size); char* ascii_output;
if (generate_ascii_armor(chksum_hex, current_offset, encrypted_data, file_size, &ascii_output) != 0) {
// Write ASCII armor printf("Error: Failed to generate ASCII armor\n");
fprintf(output_fp, "-----BEGIN OTP MESSAGE-----\n"); fclose(output_fp);
fprintf(output_fp, "Version: %s\n", get_version()); free(encrypted_data);
fprintf(output_fp, "Pad-ChkSum: %s\n", chksum_hex); free(pad_chksum);
fprintf(output_fp, "Pad-Offset: %lu\n", current_offset); return 1;
fprintf(output_fp, "\n");
// Write base64 data in 64-character lines
int b64_len = strlen(base64_data);
for (int i = 0; i < b64_len; i += 64) {
fprintf(output_fp, "%.64s\n", base64_data + i);
} }
fprintf(output_fp, "-----END OTP MESSAGE-----\n"); // Write the ASCII armored output to file
fprintf(output_fp, "%s", ascii_output);
fclose(output_fp); fclose(output_fp);
free(base64_data); free(ascii_output);
} else { } else {
// Binary format // Binary format
FILE* output_fp = fopen(output_file, "wb"); FILE* output_fp = fopen(output_file, "wb");
@@ -2162,9 +2099,12 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) {
} }
fclose(pad_file); fclose(pad_file);
// XOR decrypt // Use universal XOR operation for decryption
for (uint64_t i = 0; i < file_size; i++) { if (universal_xor_operation(encrypted_data, file_size, pad_data, encrypted_data) != 0) {
encrypted_data[i] ^= pad_data[i]; printf("Error: Decryption operation failed\n");
free(encrypted_data);
free(pad_data);
return 1;
} }
// Write decrypted file // Write decrypted file
@@ -2207,49 +2147,34 @@ int decrypt_ascii_file(const char* input_file, const char* output_file) {
return 1; return 1;
} }
// Read the entire ASCII armored content // Read the entire file content into a string
char line[MAX_LINE_LENGTH]; fseek(input_fp, 0, SEEK_END);
char stored_chksum[MAX_HASH_LENGTH]; long file_size = ftell(input_fp);
uint64_t pad_offset; fseek(input_fp, 0, SEEK_SET);
char base64_data[MAX_INPUT_SIZE * 8] = {0}; // Larger buffer for files
int in_data_section = 0;
int found_begin = 0;
while (fgets(line, sizeof(line), input_fp)) { char* file_content = malloc(file_size + 1);
line[strcspn(line, "\n\r")] = 0; // Remove newlines if (!file_content) {
printf("Error: Memory allocation failed\n");
if (strcmp(line, "-----BEGIN OTP MESSAGE-----") == 0) { fclose(input_fp);
found_begin = 1;
continue;
}
if (strcmp(line, "-----END OTP MESSAGE-----") == 0) {
break;
}
if (!found_begin) continue;
if (strncmp(line, "Pad-ChkSum: ", 12) == 0) {
strncpy(stored_chksum, line + 12, 64);
stored_chksum[64] = '\0';
}
else if (strncmp(line, "Pad-Offset: ", 12) == 0) {
pad_offset = strtoull(line + 12, NULL, 10);
}
else if (strlen(line) == 0) {
in_data_section = 1;
}
else if (in_data_section) {
strncat(base64_data, line, sizeof(base64_data) - strlen(base64_data) - 1);
}
}
fclose(input_fp);
if (!found_begin) {
printf("Error: Invalid ASCII armored format\n");
return 1; return 1;
} }
size_t bytes_read = fread(file_content, 1, file_size, input_fp);
file_content[bytes_read] = '\0';
fclose(input_fp);
// Use universal ASCII parser
char stored_chksum[MAX_HASH_LENGTH];
uint64_t pad_offset;
char base64_data[MAX_INPUT_SIZE * 8] = {0};
if (parse_ascii_message(file_content, stored_chksum, &pad_offset, base64_data) != 0) {
printf("Error: Invalid ASCII armored format\n");
free(file_content);
return 1;
}
free(file_content);
printf("Decrypting ASCII armored file...\n"); printf("Decrypting ASCII armored file...\n");
// Check if we have the required pad // Check if we have the required pad
@@ -2264,7 +2189,7 @@ int decrypt_ascii_file(const char* input_file, const char* output_file) {
return 1; return 1;
} }
// Decode base64 // Decode base64 ciphertext
int ciphertext_len; int ciphertext_len;
unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len);
if (!ciphertext) { if (!ciphertext) {
@@ -2289,35 +2214,21 @@ int decrypt_ascii_file(const char* input_file, const char* output_file) {
output_file = default_output; output_file = default_output;
} }
// Open pad file and decrypt // Use universal pad loader
FILE* pad_file = fopen(pad_path, "rb"); unsigned char* pad_data;
if (!pad_file) { if (load_pad_data(stored_chksum, pad_offset, ciphertext_len, &pad_data) != 0) {
printf("Error: Cannot open pad file\n"); printf("Error: Cannot load pad data\n");
free(ciphertext); free(ciphertext);
return 1; return 1;
} }
if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { // Use universal XOR operation for decryption
printf("Error: Cannot seek to offset in pad file\n"); if (universal_xor_operation(ciphertext, ciphertext_len, pad_data, ciphertext) != 0) {
free(ciphertext); printf("Error: Decryption operation failed\n");
fclose(pad_file);
return 1;
}
unsigned char* pad_data = malloc(ciphertext_len);
if (fread(pad_data, 1, ciphertext_len, pad_file) != (size_t)ciphertext_len) {
printf("Error: Cannot read pad data\n");
free(ciphertext); free(ciphertext);
free(pad_data); free(pad_data);
fclose(pad_file);
return 1; return 1;
} }
fclose(pad_file);
// XOR decrypt
for (int i = 0; i < ciphertext_len; i++) {
ciphertext[i] ^= pad_data[i];
}
// Write decrypted file // Write decrypted file
FILE* output_fp = fopen(output_file, "wb"); FILE* output_fp = fopen(output_file, "wb");
@@ -2669,18 +2580,29 @@ int load_preferences(void) {
while ((entry = readdir(dir)) != NULL && !found_pad) { while ((entry = readdir(dir)) != NULL && !found_pad) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) {
// Found a pad file - construct full absolute path // Found a pad file - construct full absolute path
char absolute_path[1024];
if (current_pads_dir[0] == '/') { if (current_pads_dir[0] == '/') {
// Already absolute path // Already absolute path
snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name); int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name);
if (ret >= (int)sizeof(first_pad_path)) {
// Path was truncated, skip this entry
continue;
}
} else { } else {
// Relative path - make it absolute // Relative path - make it absolute
char current_dir[512]; char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) { if (getcwd(current_dir, sizeof(current_dir))) {
snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s/%s", current_dir, current_pads_dir, entry->d_name); int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s/%s", current_dir, current_pads_dir, entry->d_name);
if (ret >= (int)sizeof(first_pad_path)) {
// Path was truncated, skip this entry
continue;
}
} else { } else {
// Fallback to relative path // Fallback to relative path
snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name); int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name);
if (ret >= (int)sizeof(first_pad_path)) {
// Path was truncated, skip this entry
continue;
}
} }
} }
strncpy(default_pad_path, first_pad_path, sizeof(default_pad_path) - 1); strncpy(default_pad_path, first_pad_path, sizeof(default_pad_path) - 1);
@@ -2997,6 +2919,220 @@ void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size,
} }
} }
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// UNIVERSAL CORE FUNCTIONS FOR CODE CONSOLIDATION
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Universal XOR operation - handles both encryption and decryption
// Since XOR is symmetric, this single function replaces all 6 duplicate XOR loops
int universal_xor_operation(const unsigned char* data, size_t data_len,
const unsigned char* pad_data, unsigned char* result) {
if (!data || !pad_data || !result) {
return 1; // Error: null pointer
}
for (size_t i = 0; i < data_len; i++) {
result[i] = data[i] ^ pad_data[i];
}
return 0; // Success
}
// Universal ASCII message parser - consolidates 4 duplicate parsing implementations
// Extracts checksum, offset, and base64 data from ASCII armored messages
int parse_ascii_message(const char* message, char* chksum, uint64_t* offset, char* base64_data) {
if (!message || !chksum || !offset || !base64_data) {
return 1; // Error: null pointer
}
char *message_copy = strdup(message);
if (!message_copy) {
return 1; // Memory allocation failed
}
char *line_ptr = strtok(message_copy, "\n");
int found_begin = 0;
int in_data_section = 0;
int found_chksum = 0, found_offset = 0;
// Initialize output
chksum[0] = '\0';
*offset = 0;
base64_data[0] = '\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(chksum, line_ptr + 12, 64);
chksum[64] = '\0';
found_chksum = 1;
}
else if (strncmp(line_ptr, "Pad-Offset: ", 12) == 0) {
*offset = strtoull(line_ptr + 12, NULL, 10);
found_offset = 1;
}
else if (strlen(line_ptr) == 0) {
in_data_section = 1;
}
else if (in_data_section) {
strncat(base64_data, line_ptr, MAX_INPUT_SIZE * 2 - 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, MAX_INPUT_SIZE * 2 - strlen(base64_data) - 1);
}
}
line_ptr = strtok(NULL, "\n");
}
free(message_copy);
if (!found_begin || !found_chksum || !found_offset) {
return 2; // Error: incomplete message format
}
return 0; // Success
}
// Universal pad data loader - consolidates pad loading and validation logic
// Loads pad data at specified offset and validates pad availability
int load_pad_data(const char* pad_chksum, uint64_t offset, size_t length, unsigned char** pad_data) {
if (!pad_chksum || !pad_data) {
return 1; // Error: null pointer
}
char pad_path[MAX_HASH_LENGTH + 20];
char state_path[MAX_HASH_LENGTH + 20];
get_pad_path(pad_chksum, pad_path, state_path);
// Check if pad file exists
if (access(pad_path, R_OK) != 0) {
return 2; // Error: pad file not found
}
// Check pad file size
struct stat pad_stat;
if (stat(pad_path, &pad_stat) != 0) {
return 3; // Error: cannot get pad file size
}
if (offset + length > (uint64_t)pad_stat.st_size) {
return 4; // Error: not enough pad space
}
// Allocate memory for pad data
*pad_data = malloc(length);
if (!*pad_data) {
return 5; // Error: memory allocation failed
}
// Open and read pad file
FILE* pad_file = fopen(pad_path, "rb");
if (!pad_file) {
free(*pad_data);
*pad_data = NULL;
return 6; // Error: cannot open pad file
}
if (fseek(pad_file, offset, SEEK_SET) != 0) {
fclose(pad_file);
free(*pad_data);
*pad_data = NULL;
return 7; // Error: cannot seek to offset
}
if (fread(*pad_data, 1, length, pad_file) != length) {
fclose(pad_file);
free(*pad_data);
*pad_data = NULL;
return 8; // Error: cannot read pad data
}
fclose(pad_file);
return 0; // Success
}
// Universal ASCII armor generator - consolidates duplicate ASCII armor generation
// Creates ASCII armored output format used by both text and file encryption
int generate_ascii_armor(const char* chksum, uint64_t offset, const unsigned char* encrypted_data,
size_t data_length, char** ascii_output) {
if (!chksum || !encrypted_data || !ascii_output) {
return 1; // Error: null pointer
}
// Encode data as base64
char* base64_data = custom_base64_encode(encrypted_data, data_length);
if (!base64_data) {
return 2; // Error: base64 encoding failed
}
// Calculate required buffer size
size_t base64_len = strlen(base64_data);
size_t header_size = 200; // Approximate size for headers
size_t total_size = header_size + base64_len + (base64_len / 64) + 100; // +newlines +footer
*ascii_output = malloc(total_size);
if (!*ascii_output) {
free(base64_data);
return 3; // Error: memory allocation failed
}
// Build ASCII armor
strcpy(*ascii_output, "-----BEGIN OTP MESSAGE-----\n");
char temp_line[256];
snprintf(temp_line, sizeof(temp_line), "Version: %s\n", get_version());
strcat(*ascii_output, temp_line);
snprintf(temp_line, sizeof(temp_line), "Pad-ChkSum: %s\n", chksum);
strcat(*ascii_output, temp_line);
snprintf(temp_line, sizeof(temp_line), "Pad-Offset: %lu\n", offset);
strcat(*ascii_output, temp_line);
strcat(*ascii_output, "\n");
// Add base64 data in 64-character lines
int b64_len = strlen(base64_data);
for (int i = 0; i < b64_len; i += 64) {
char line[70];
snprintf(line, sizeof(line), "%.64s\n", base64_data + i);
strcat(*ascii_output, line);
}
strcat(*ascii_output, "-----END OTP MESSAGE-----\n");
free(base64_data);
return 0; // Success
}
// Universal pad integrity validator - consolidates pad validation logic
// Verifies pad checksum matches expected value for security
int validate_pad_integrity(const char* pad_path, const char* expected_chksum) {
if (!pad_path || !expected_chksum) {
return 1; // Error: null pointer
}
char current_chksum[MAX_HASH_LENGTH];
if (calculate_checksum(pad_path, current_chksum) != 0) {
return 2; // Error: cannot calculate checksum
}
if (strcmp(expected_chksum, current_chksum) != 0) {
return 3; // Error: checksum mismatch
}
return 0; // Success - pad integrity verified
}
// Enhanced input function with editable pre-filled text // 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) { int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size) {
// Find the last directory separator // Find the last directory separator
@@ -3544,7 +3680,7 @@ int handle_pads_menu(void) {
printf("No pads found.\n"); printf("No pads found.\n");
printf("\nOptions:\n"); printf("\nOptions:\n");
printf(" \033[4mG\033[0menerate new pad\n"); printf(" \033[4mG\033[0menerate new pad\n");
printf(" \033[4mB\033[0mack to main menu\n"); printf(" E\033[4mx\033[0mit\n");
printf("\nSelect option: "); printf("\nSelect option: ");
char input[10]; char input[10];
@@ -3638,7 +3774,7 @@ int handle_pads_menu(void) {
printf("\nActions:\n"); printf("\nActions:\n");
printf(" \033[4mG\033[0menerate new pad\n"); printf(" \033[4mG\033[0menerate new pad\n");
printf(" \033[4mS\033[0met default pad\n"); printf(" \033[4mS\033[0met default pad\n");
printf(" \033[4mB\033[0mack to main menu\n"); printf(" E\033[4mx\033[0mit\n");
printf("\nSelect pad (by prefix) or action: "); printf("\nSelect pad (by prefix) or action: ");
char input[MAX_HASH_LENGTH]; char input[MAX_HASH_LENGTH];
@@ -3689,15 +3825,31 @@ int handle_pads_menu(void) {
char new_default_path[1024]; char new_default_path[1024];
if (current_pads_dir[0] == '/') { if (current_pads_dir[0] == '/') {
// Already absolute path // Already absolute path
snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, pads[matched_pad].chksum); int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, pads[matched_pad].chksum);
if (ret >= (int)sizeof(new_default_path)) {
printf("Error: Path too long for default pad setting\n");
return handle_pads_menu();
}
} else { } else {
// Relative path - make it absolute // Relative path - make it absolute
char current_dir[512]; char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) { if (getcwd(current_dir, sizeof(current_dir))) {
snprintf(new_default_path, sizeof(new_default_path), "%s/%s/%s.pad", current_dir, current_pads_dir, pads[matched_pad].chksum); int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s/%s.pad", current_dir, current_pads_dir, pads[matched_pad].chksum);
if (ret >= (int)sizeof(new_default_path)) {
// Path was truncated, fall back to relative path
int ret2 = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, pads[matched_pad].chksum);
if (ret2 >= (int)sizeof(new_default_path)) {
printf("Error: Path too long for default pad setting\n");
return handle_pads_menu();
}
}
} else { } else {
// Fallback to relative path // Fallback to relative path
snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, pads[matched_pad].chksum); int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, pads[matched_pad].chksum);
if (ret >= (int)sizeof(new_default_path)) {
printf("Error: Path too long for default pad setting\n");
return handle_pads_menu();
}
} }
} }
@@ -3709,8 +3861,8 @@ int handle_pads_menu(void) {
} }
return handle_pads_menu(); return handle_pads_menu();
} else if (toupper(input[0]) == 'B') { } else if (toupper(input[0]) == 'X') {
return 0; // Back to main menu return 0; // Exit to main menu
} }
// Find matching pad by prefix // Find matching pad by prefix
@@ -3893,7 +4045,7 @@ void print_usage(const char* program_name) {
printf("Usage:\n"); printf("Usage:\n");
printf(" %s - Interactive mode\n", program_name); printf(" %s - Interactive mode\n", program_name);
printf(" %s generate|-g <size> - Generate new pad\n", program_name); printf(" %s generate|-g <size> - Generate new pad\n", program_name);
printf(" %s encrypt|-e <pad_checksum_prefix> [text] - Encrypt text\n", program_name); printf(" %s encrypt|-e [pad_checksum_prefix] [text] - Encrypt text\n", program_name);
printf(" %s decrypt|-d [encrypted_message] - Decrypt message\n", program_name); printf(" %s decrypt|-d [encrypted_message] - Decrypt message\n", program_name);
printf(" %s -f <file> <pad_prefix> [-a] [-o <out>] - Encrypt file\n", program_name); printf(" %s -f <file> <pad_prefix> [-a] [-o <out>] - Encrypt file\n", program_name);
printf(" %s list|-l - List available pads\n", program_name); printf(" %s list|-l - List available pads\n", program_name);