From 12f92d2c96a6344bc362214e45a62a325e1fb4c1 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Wed, 27 Aug 2025 08:44:11 -0400 Subject: [PATCH] Version v0.2.94 - Refactor code --- TODO.md | 5 - otp.c | 633 +++++++++++++++++++++++++++++++------------------------- 2 files changed, 351 insertions(+), 287 deletions(-) diff --git a/TODO.md b/TODO.md index fdadc5c..77f1c68 100644 --- a/TODO.md +++ b/TODO.md @@ -3,8 +3,6 @@ ## Change technique for adding keyboard entropy. -## Some of the processing seems similar, so maybe code could be more compact. - ## 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. @@ -14,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. -## We have three different decrypt file functions - - ## Setup for multiple USB drives diff --git a/otp.c b/otp.c index da205c0..1748c50 100644 --- a/otp.c +++ b/otp.c @@ -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 write_state_offset(const char* pad_chksum, uint64_t offset); 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); unsigned char* custom_base64_decode(const char* input, int* output_length); @@ -1479,50 +1488,44 @@ int encrypt_text(const char* pad_identifier, const char* input_text) { } fclose(pad_file); - // XOR encrypt the input + // Use universal XOR operation for encryption unsigned char* ciphertext = malloc(input_len); - for (size_t i = 0; i < input_len; i++) { - ciphertext[i] = text_buffer[i] ^ pad_data[i]; + if (universal_xor_operation((const unsigned char*)text_buffer, input_len, pad_data, ciphertext) != 0) { + 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 if (write_state_offset(pad_chksum, current_offset + input_len) != 0) { 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 if (is_interactive) { - printf("\n\n-----BEGIN OTP MESSAGE-----\n"); + printf("\n\n%s\n\n", ascii_output); } else { - printf("-----BEGIN OTP MESSAGE-----\n"); - } - - 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"); + printf("%s", ascii_output); } // Cleanup free(pad_data); free(ciphertext); - free(base64_cipher); + free(ascii_output); free(pad_chksum); return 0; @@ -1532,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 (void)pad_identifier; // Suppress unused parameter warning - char line[MAX_LINE_LENGTH]; 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); - } - } - line_ptr = strtok(NULL, "\n"); - } - free(message_copy); - - if (!found_begin) { + // Use universal ASCII parser to extract message components + if (parse_ascii_message(encrypted_message, stored_chksum, &pad_offset, base64_data) != 0) { printf("Error: Invalid message format - missing BEGIN header\n"); return 1; } @@ -1579,49 +1549,29 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) { // Interactive mode - read from stdin printf("Enter encrypted message (paste the full ASCII armor block):\n"); - // Read the ASCII armor format - int found_begin = 0; + // Read entire message from stdin + char full_message[MAX_INPUT_SIZE * 4] = {0}; + char line[MAX_LINE_LENGTH]; while (fgets(line, sizeof(line), stdin)) { - line[strcspn(line, "\n")] = 0; - - if (strcmp(line, "-----BEGIN OTP MESSAGE-----") == 0) { - found_begin = 1; - continue; - } - - if (strcmp(line, "-----END OTP MESSAGE-----") == 0) { + strncat(full_message, line, sizeof(full_message) - strlen(full_message) - 1); + if (strstr(line, "-----END OTP MESSAGE-----")) { 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"); 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 state_path[MAX_HASH_LENGTH + 20]; 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) { printf("Error: Required pad not found: %s\n", stored_chksum); printf("Available pads:\n"); @@ -1629,30 +1579,28 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) { return 1; } - // Verify pad integrity - if (calculate_checksum(pad_path, current_chksum) != 0) { - printf("Error: Cannot calculate current pad checksum\n"); - return 1; - } - - if (strcmp(stored_chksum, current_chksum) != 0) { + // Use universal pad integrity validator + int integrity_result = validate_pad_integrity(pad_path, stored_chksum); + if (integrity_result == 3) { printf("Warning: Pad integrity check failed!\n"); printf("Expected: %s\n", stored_chksum); - printf("Current: %s\n", current_chksum); printf("Continue anyway? (y/N): "); fflush(stdout); char response[10]; - if (fgets(response, sizeof(response), stdin) == NULL || + if (fgets(response, sizeof(response), stdin) == NULL || (response[0] != 'y' && response[0] != 'Y')) { printf("Decryption aborted.\n"); return 1; } + } else if (integrity_result != 0) { + printf("Error: Cannot verify pad integrity\n"); + return 1; } else { printf("Pad integrity: VERIFIED\n"); } - // Decode base64 + // Decode base64 ciphertext int ciphertext_len; unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); if (!ciphertext) { @@ -1660,36 +1608,24 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) { return 1; } - // Read pad data at specified offset - FILE* pad_file = fopen(pad_path, "rb"); - if (!pad_file) { - printf("Error: Cannot open pad file %s\n", pad_path); + // Use universal pad loader + unsigned char* pad_data; + int load_result = load_pad_data(stored_chksum, pad_offset, ciphertext_len, &pad_data); + if (load_result != 0) { + printf("Error: Cannot load pad data (code %d)\n", load_result); free(ciphertext); return 1; } - if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { - printf("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) { - printf("Error: Cannot read pad data\n"); + // Use universal XOR operation for decryption + char* plaintext = malloc(ciphertext_len + 1); + if (universal_xor_operation(ciphertext, ciphertext_len, pad_data, (unsigned char*)plaintext) != 0) { + printf("Error: Decryption operation failed\n"); free(ciphertext); free(pad_data); - fclose(pad_file); + free(plaintext); 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'; printf("Decrypted: %s\n", plaintext); @@ -1707,79 +1643,38 @@ int decrypt_text_silent(const char* pad_identifier, const char* encrypted_messag (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 { + if (encrypted_message == NULL) { fprintf(stderr, "Error: No encrypted message provided\n"); 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 state_path[MAX_HASH_LENGTH + 20]; 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) { 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) { + // Use universal pad integrity validator (silent check) + if (validate_pad_integrity(pad_path, stored_chksum) != 0) { fprintf(stderr, "Error: Pad integrity check failed!\n"); return 1; } - // Decode base64 + // Decode base64 ciphertext int ciphertext_len; unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); if (!ciphertext) { @@ -1787,36 +1682,23 @@ int decrypt_text_silent(const char* pad_identifier, const char* encrypted_messag 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); + // Use universal pad loader + unsigned char* pad_data; + if (load_pad_data(stored_chksum, pad_offset, ciphertext_len, &pad_data) != 0) { + fprintf(stderr, "Error: Cannot load pad data\n"); 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"); + // Use universal XOR operation for decryption + char* plaintext = malloc(ciphertext_len + 1); + if (universal_xor_operation(ciphertext, ciphertext_len, pad_data, (unsigned char*)plaintext) != 0) { + fprintf(stderr, "Error: Decryption operation failed\n"); free(ciphertext); free(pad_data); - fclose(pad_file); + free(plaintext); 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 @@ -1980,9 +1862,14 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char* return 1; } - // XOR encrypt - for (uint64_t i = 0; i < chunk_size; i++) { - encrypted_data[bytes_processed + i] = buffer[i] ^ pad_buffer[i]; + // Use universal XOR operation for encryption + if (universal_xor_operation(buffer, chunk_size, pad_buffer, &encrypted_data[bytes_processed]) != 0) { + printf("Error: Encryption operation failed\n"); + free(encrypted_data); + fclose(input_fp); + fclose(pad_file); + free(pad_chksum); + return 1; } bytes_processed += chunk_size; @@ -2012,26 +1899,21 @@ int encrypt_file(const char* pad_identifier, const char* input_file, const char* return 1; } - // Encode as base64 - char* base64_data = custom_base64_encode(encrypted_data, file_size); - - // Write ASCII armor - fprintf(output_fp, "-----BEGIN OTP MESSAGE-----\n"); - fprintf(output_fp, "Version: %s\n", get_version()); - fprintf(output_fp, "Pad-ChkSum: %s\n", chksum_hex); - fprintf(output_fp, "Pad-Offset: %lu\n", current_offset); - 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); + // Use universal ASCII armor generator + char* ascii_output; + if (generate_ascii_armor(chksum_hex, current_offset, encrypted_data, file_size, &ascii_output) != 0) { + printf("Error: Failed to generate ASCII armor\n"); + fclose(output_fp); + free(encrypted_data); + free(pad_chksum); + return 1; } - fprintf(output_fp, "-----END OTP MESSAGE-----\n"); + // Write the ASCII armored output to file + fprintf(output_fp, "%s", ascii_output); fclose(output_fp); - free(base64_data); + free(ascii_output); } else { // Binary format FILE* output_fp = fopen(output_file, "wb"); @@ -2217,9 +2099,12 @@ int decrypt_binary_file(FILE* input_fp, const char* output_file) { } fclose(pad_file); - // XOR decrypt - for (uint64_t i = 0; i < file_size; i++) { - encrypted_data[i] ^= pad_data[i]; + // Use universal XOR operation for decryption + if (universal_xor_operation(encrypted_data, file_size, pad_data, encrypted_data) != 0) { + printf("Error: Decryption operation failed\n"); + free(encrypted_data); + free(pad_data); + return 1; } // Write decrypted file @@ -2262,49 +2147,34 @@ int decrypt_ascii_file(const char* input_file, const char* output_file) { return 1; } - // Read the entire ASCII armored content - char line[MAX_LINE_LENGTH]; - char stored_chksum[MAX_HASH_LENGTH]; - uint64_t pad_offset; - char base64_data[MAX_INPUT_SIZE * 8] = {0}; // Larger buffer for files - int in_data_section = 0; - int found_begin = 0; + // Read the entire file content into a string + fseek(input_fp, 0, SEEK_END); + long file_size = ftell(input_fp); + fseek(input_fp, 0, SEEK_SET); - while (fgets(line, sizeof(line), input_fp)) { - line[strcspn(line, "\n\r")] = 0; // Remove newlines - - if (strcmp(line, "-----BEGIN OTP MESSAGE-----") == 0) { - 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"); + char* file_content = malloc(file_size + 1); + if (!file_content) { + printf("Error: Memory allocation failed\n"); + fclose(input_fp); 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"); // Check if we have the required pad @@ -2319,7 +2189,7 @@ int decrypt_ascii_file(const char* input_file, const char* output_file) { return 1; } - // Decode base64 + // Decode base64 ciphertext int ciphertext_len; unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); if (!ciphertext) { @@ -2344,35 +2214,21 @@ int decrypt_ascii_file(const char* input_file, const char* output_file) { output_file = default_output; } - // Open pad file and decrypt - FILE* pad_file = fopen(pad_path, "rb"); - if (!pad_file) { - printf("Error: Cannot open pad file\n"); + // Use universal pad loader + unsigned char* pad_data; + if (load_pad_data(stored_chksum, pad_offset, ciphertext_len, &pad_data) != 0) { + printf("Error: Cannot load pad data\n"); free(ciphertext); return 1; } - if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { - printf("Error: Cannot seek to offset in pad file\n"); - 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) { - printf("Error: Cannot read pad data\n"); + // Use universal XOR operation for decryption + if (universal_xor_operation(ciphertext, ciphertext_len, pad_data, ciphertext) != 0) { + printf("Error: Decryption operation failed\n"); free(ciphertext); free(pad_data); - fclose(pad_file); return 1; } - fclose(pad_file); - - // XOR decrypt - for (int i = 0; i < ciphertext_len; i++) { - ciphertext[i] ^= pad_data[i]; - } // Write decrypted file FILE* output_fp = fopen(output_file, "wb"); @@ -2724,7 +2580,6 @@ int load_preferences(void) { while ((entry = readdir(dir)) != NULL && !found_pad) { if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { // Found a pad file - construct full absolute path - char absolute_path[1024]; if (current_pads_dir[0] == '/') { // Already absolute path int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name); @@ -3064,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 int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size) { // Find the last directory separator