From 6c796df30ac366b0d743e70eaa4ec8a338588da5 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Fri, 29 Aug 2025 08:45:08 -0400 Subject: [PATCH] Version v0.2.101 - Update entropy addition --- Makefile | 9 +- nostr_chacha20.c | 163 +++++++++ nostr_chacha20.h | 115 +++++++ otp.c | 881 ++++++++++++++++++++++++++++++++--------------- otp.o | Bin 0 -> 132296 bytes 5 files changed, 888 insertions(+), 280 deletions(-) create mode 100644 nostr_chacha20.c create mode 100644 nostr_chacha20.h create mode 100644 otp.o diff --git a/Makefile b/Makefile index 0a39809..d81032a 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,19 @@ CC = gcc CFLAGS = -Wall -Wextra -std=c99 -LIBS = -LIBS_STATIC = -static +LIBS = -lm +LIBS_STATIC = -static -lm TARGET = otp SOURCE = otp.c +CHACHA20_SOURCE = nostr_chacha20.c VERSION_SOURCE = src/version.c # Default build target $(TARGET): $(SOURCE) - $(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(VERSION_SOURCE) $(LIBS) + $(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(CHACHA20_SOURCE) $(VERSION_SOURCE) $(LIBS) # Static linking target static: $(SOURCE) - $(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(VERSION_SOURCE) $(LIBS_STATIC) + $(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(CHACHA20_SOURCE) $(VERSION_SOURCE) $(LIBS_STATIC) clean: rm -f $(TARGET) *.pad *.state diff --git a/nostr_chacha20.c b/nostr_chacha20.c new file mode 100644 index 0000000..bb95d37 --- /dev/null +++ b/nostr_chacha20.c @@ -0,0 +1,163 @@ +/* + * nostr_chacha20.c - ChaCha20 stream cipher implementation + * + * Implementation based on RFC 8439 "ChaCha20 and Poly1305 for IETF Protocols" + * + * This implementation is adapted from the RFC 8439 reference specification. + * It prioritizes correctness and clarity over performance optimization. + */ + +#include "nostr_chacha20.h" +#include + +/* + * ============================================================================ + * UTILITY MACROS AND FUNCTIONS + * ============================================================================ + */ + +/* Left rotate a 32-bit value by n bits */ +#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) + +/* Convert 4 bytes to 32-bit little-endian */ +static uint32_t bytes_to_u32_le(const uint8_t *bytes) { + return ((uint32_t)bytes[0]) | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); +} + +/* Convert 32-bit to 4 bytes little-endian */ +static void u32_to_bytes_le(uint32_t val, uint8_t *bytes) { + bytes[0] = (uint8_t)(val & 0xff); + bytes[1] = (uint8_t)((val >> 8) & 0xff); + bytes[2] = (uint8_t)((val >> 16) & 0xff); + bytes[3] = (uint8_t)((val >> 24) & 0xff); +} + +/* + * ============================================================================ + * CHACHA20 CORE FUNCTIONS + * ============================================================================ + */ + +void chacha20_quarter_round(uint32_t state[16], int a, int b, int c, int d) { + state[a] += state[b]; + state[d] ^= state[a]; + state[d] = ROTLEFT(state[d], 16); + + state[c] += state[d]; + state[b] ^= state[c]; + state[b] = ROTLEFT(state[b], 12); + + state[a] += state[b]; + state[d] ^= state[a]; + state[d] = ROTLEFT(state[d], 8); + + state[c] += state[d]; + state[b] ^= state[c]; + state[b] = ROTLEFT(state[b], 7); +} + +void chacha20_init_state(uint32_t state[16], const uint8_t key[32], + uint32_t counter, const uint8_t nonce[12]) { + /* ChaCha20 constants "expand 32-byte k" */ + state[0] = 0x61707865; + state[1] = 0x3320646e; + state[2] = 0x79622d32; + state[3] = 0x6b206574; + + /* Key (8 words) */ + state[4] = bytes_to_u32_le(key + 0); + state[5] = bytes_to_u32_le(key + 4); + state[6] = bytes_to_u32_le(key + 8); + state[7] = bytes_to_u32_le(key + 12); + state[8] = bytes_to_u32_le(key + 16); + state[9] = bytes_to_u32_le(key + 20); + state[10] = bytes_to_u32_le(key + 24); + state[11] = bytes_to_u32_le(key + 28); + + /* Counter (1 word) */ + state[12] = counter; + + /* Nonce (3 words) */ + state[13] = bytes_to_u32_le(nonce + 0); + state[14] = bytes_to_u32_le(nonce + 4); + state[15] = bytes_to_u32_le(nonce + 8); +} + +void chacha20_serialize_state(const uint32_t state[16], uint8_t output[64]) { + for (int i = 0; i < 16; i++) { + u32_to_bytes_le(state[i], output + (i * 4)); + } +} + +int chacha20_block(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], uint8_t output[64]) { + uint32_t state[16]; + uint32_t initial_state[16]; + + /* Initialize state */ + chacha20_init_state(state, key, counter, nonce); + + /* Save initial state for later addition */ + memcpy(initial_state, state, sizeof(initial_state)); + + /* Perform 20 rounds (10 iterations of the 8 quarter rounds) */ + for (int i = 0; i < 10; i++) { + /* Column rounds */ + chacha20_quarter_round(state, 0, 4, 8, 12); + chacha20_quarter_round(state, 1, 5, 9, 13); + chacha20_quarter_round(state, 2, 6, 10, 14); + chacha20_quarter_round(state, 3, 7, 11, 15); + + /* Diagonal rounds */ + chacha20_quarter_round(state, 0, 5, 10, 15); + chacha20_quarter_round(state, 1, 6, 11, 12); + chacha20_quarter_round(state, 2, 7, 8, 13); + chacha20_quarter_round(state, 3, 4, 9, 14); + } + + /* Add initial state back (prevents slide attacks) */ + for (int i = 0; i < 16; i++) { + state[i] += initial_state[i]; + } + + /* Serialize to output bytes */ + chacha20_serialize_state(state, output); + + return 0; +} + +int chacha20_encrypt(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], const uint8_t* input, + uint8_t* output, size_t length) { + uint8_t keystream[CHACHA20_BLOCK_SIZE]; + size_t offset = 0; + + while (length > 0) { + /* Generate keystream block */ + int ret = chacha20_block(key, counter, nonce, keystream); + if (ret != 0) { + return ret; + } + + /* XOR with input to produce output */ + size_t block_len = (length < CHACHA20_BLOCK_SIZE) ? length : CHACHA20_BLOCK_SIZE; + for (size_t i = 0; i < block_len; i++) { + output[offset + i] = input[offset + i] ^ keystream[i]; + } + + /* Move to next block */ + offset += block_len; + length -= block_len; + counter++; + + /* Check for counter overflow */ + if (counter == 0) { + return -1; /* Counter wrapped around */ + } + } + + return 0; +} diff --git a/nostr_chacha20.h b/nostr_chacha20.h new file mode 100644 index 0000000..93a2f35 --- /dev/null +++ b/nostr_chacha20.h @@ -0,0 +1,115 @@ +/* + * nostr_chacha20.h - ChaCha20 stream cipher implementation + * + * Implementation based on RFC 8439 "ChaCha20 and Poly1305 for IETF Protocols" + * + * This is a small, portable implementation for NIP-44 support in the NOSTR library. + * The implementation prioritizes correctness and simplicity over performance. + */ + +#ifndef NOSTR_CHACHA20_H +#define NOSTR_CHACHA20_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * CONSTANTS AND DEFINITIONS + * ============================================================================ + */ + +#define CHACHA20_KEY_SIZE 32 /* 256 bits */ +#define CHACHA20_NONCE_SIZE 12 /* 96 bits */ +#define CHACHA20_BLOCK_SIZE 64 /* 512 bits */ + +/* + * ============================================================================ + * CORE CHACHA20 FUNCTIONS + * ============================================================================ + */ + +/** + * ChaCha20 quarter round operation + * + * Operates on four 32-bit words performing the core ChaCha20 quarter round: + * a += b; d ^= a; d <<<= 16; + * c += d; b ^= c; b <<<= 12; + * a += b; d ^= a; d <<<= 8; + * c += d; b ^= c; b <<<= 7; + * + * @param state[in,out] ChaCha state as 16 32-bit words + * @param a, b, c, d Indices into state array for quarter round + */ +void chacha20_quarter_round(uint32_t state[16], int a, int b, int c, int d); + +/** + * ChaCha20 block function + * + * Transforms a 64-byte input block using ChaCha20 algorithm with 20 rounds. + * + * @param key[in] 32-byte key + * @param counter[in] 32-bit block counter + * @param nonce[in] 12-byte nonce + * @param output[out] 64-byte output buffer + * @return 0 on success, negative on error + */ +int chacha20_block(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], uint8_t output[64]); + +/** + * ChaCha20 encryption/decryption + * + * Encrypts or decrypts data using ChaCha20 stream cipher. + * Since ChaCha20 is a stream cipher, encryption and decryption are the same operation. + * + * @param key[in] 32-byte key + * @param counter[in] Initial 32-bit counter value + * @param nonce[in] 12-byte nonce + * @param input[in] Input data to encrypt/decrypt + * @param output[out] Output buffer (can be same as input) + * @param length[in] Length of input data in bytes + * @return 0 on success, negative on error + */ +int chacha20_encrypt(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], const uint8_t* input, + uint8_t* output, size_t length); + +/* + * ============================================================================ + * UTILITY FUNCTIONS + * ============================================================================ + */ + +/** + * Initialize ChaCha20 state matrix + * + * Sets up the initial 16-word state matrix with constants, key, counter, and nonce. + * + * @param state[out] 16-word state array to initialize + * @param key[in] 32-byte key + * @param counter[in] 32-bit block counter + * @param nonce[in] 12-byte nonce + */ +void chacha20_init_state(uint32_t state[16], const uint8_t key[32], + uint32_t counter, const uint8_t nonce[12]); + +/** + * Serialize ChaCha20 state to bytes + * + * Converts 16 32-bit words to 64 bytes in little-endian format. + * + * @param state[in] 16-word state array + * @param output[out] 64-byte output buffer + */ +void chacha20_serialize_state(const uint32_t state[16], uint8_t output[64]); + +#ifdef __cplusplus +} +#endif + +#endif /* NOSTR_CHACHA20_H */ diff --git a/otp.c b/otp.c index f16f11f..d905025 100644 --- a/otp.c +++ b/otp.c @@ -12,7 +12,9 @@ #include #include #include +#include #include "src/version.h" +#include "nostr_chacha20.h" // Custom base64 character set static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -86,7 +88,6 @@ int launch_file_manager(const char* start_directory, char* selected_file, size_t // Core functions int generate_pad(uint64_t size_bytes, int show_progress); -int generate_pad_with_entropy(uint64_t size_bytes, int show_progress, int use_keyboard_entropy); 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_silent(const char* pad_identifier, const char* encrypted_message); @@ -95,12 +96,34 @@ int decrypt_file(const char* input_file, const char* output_file); int decrypt_binary_file(FILE* input_fp, const char* output_file); int decrypt_ascii_file(const char* input_file, const char* output_file); -// Keyboard entropy functions +// Enhanced entropy system functions +typedef struct { + size_t target_bytes; // Target entropy to collect + size_t collected_bytes; // Bytes collected so far + size_t unique_keys; // Number of unique keys pressed + double collection_start_time; // Start timestamp + double last_keypress_time; // Last keypress timestamp + unsigned char quality_score; // Entropy quality (0-100) + int auto_complete_enabled; // Allow auto-complete at minimum + unsigned char key_histogram[256]; // Track key frequency +} entropy_collection_state_t; + int setup_raw_terminal(struct termios* original_termios); void restore_terminal(struct termios* original_termios); -int collect_keyboard_entropy(unsigned char* entropy_buffer, size_t max_size, size_t* collected); -void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size, - const unsigned char* entropy_data, size_t entropy_size); +int collect_entropy_with_feedback(unsigned char* entropy_buffer, size_t target_bytes, + size_t* collected_bytes, int allow_early_exit); +void display_entropy_progress(const entropy_collection_state_t* state); +void draw_progress_bar(double percentage, int width); +void draw_quality_bar(double quality, int width, const char* label); +double calculate_timing_quality(const entropy_collection_state_t* state); +double calculate_variety_quality(const entropy_collection_state_t* state); +unsigned char calculate_overall_quality(const entropy_collection_state_t* state); +double get_precise_time(void); +int derive_chacha20_params(const unsigned char* entropy_data, size_t entropy_size, + unsigned char key[32], unsigned char nonce[12]); +int add_entropy_to_pad(const char* pad_chksum, const unsigned char* entropy_data, + size_t entropy_size, int show_progress); +int handle_add_entropy_to_pad(const char* pad_chksum); // Directory management int ensure_pads_directory(void); @@ -159,8 +182,6 @@ void print_usage(const char* program_name); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// - - int main(int argc, char* argv[]) { // Load preferences first load_preferences(); @@ -202,44 +223,13 @@ int main(int argc, char* argv[]) { } } -int interactive_mode(void) { - char input[10]; - - while (1) { - show_main_menu(); - - if (!fgets(input, sizeof(input), stdin)) { - printf("Goodbye!\n"); - break; - } - - char choice = toupper(input[0]); - - switch (choice) { - case 'T': - handle_text_encrypt(); - break; - case 'F': - handle_file_encrypt(); - break; - case 'D': - handle_decrypt_menu(); - break; - case 'P': - handle_pads_menu(); - break; - case 'X': - case 'Q': - printf("Goodbye!\n"); - return 0; - default: - printf("Invalid choice. Please try again.\n"); - break; - } - } - - return 0; -} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// COMMAND LINE MODE +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// int command_line_mode(int argc, char* argv[]) { // Check for help flags first @@ -261,7 +251,7 @@ int command_line_mode(int argc, char* argv[]) { printf("Error: Invalid size format\n"); return 1; } - return generate_pad_with_entropy(size, 1, 0); // No keyboard entropy for command line + return generate_pad(size, 1); // Use simplified pad generation } else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) { if (argc < 2 || argc > 4) { @@ -410,6 +400,54 @@ int command_line_mode(int argc, char* argv[]) { } } + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// INTERACTIVE MODE +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +int interactive_mode(void) { + char input[10]; + + while (1) { + show_main_menu(); + + if (!fgets(input, sizeof(input), stdin)) { + printf("Goodbye!\n"); + break; + } + + char choice = toupper(input[0]); + + switch (choice) { + case 'T': + handle_text_encrypt(); + break; + case 'F': + handle_file_encrypt(); + break; + case 'D': + handle_decrypt_menu(); + break; + case 'P': + handle_pads_menu(); + break; + case 'X': + case 'Q': + printf("Goodbye!\n"); + return 0; + default: + printf("Invalid choice. Please try again.\n"); + break; + } + } + + return 0; +} + + void show_main_menu(void) { printf("\n\n\n\n=========================== Main Menu - OTP %s ===========================\n\n", get_version() ); @@ -440,25 +478,11 @@ int handle_generate_menu(void) { return 1; } - // Ask about keyboard entropy - printf("\nAdd keyboard entropy for enhanced security? (y/N): "); - char entropy_choice[10]; - int use_keyboard_entropy = 0; - - if (fgets(entropy_choice, sizeof(entropy_choice), stdin)) { - if (entropy_choice[0] == 'y' || entropy_choice[0] == 'Y') { - use_keyboard_entropy = 1; - } - } - double size_gb = (double)size / (1024.0 * 1024.0 * 1024.0); - if (use_keyboard_entropy) { - printf("Generating %.2f GB pad with keyboard entropy...\n", size_gb); - } else { - printf("Generating %.2f GB pad...\n", size_gb); - } + printf("Generating %.2f GB pad...\n", size_gb); + printf("Note: Use 'Add entropy' in Pads menu to enhance randomness after creation.\n"); - return generate_pad_with_entropy(size, 1, use_keyboard_entropy); + return generate_pad(size, 1); } int handle_encrypt_menu(void) { @@ -1026,9 +1050,15 @@ void show_progress(uint64_t current, uint64_t total, time_t start_time) { } int generate_pad(uint64_t size_bytes, int display_progress) { - char temp_filename[32]; - char pad_filename[MAX_HASH_LENGTH + 10]; - char state_filename[MAX_HASH_LENGTH + 10]; + // Ensure pads directory exists + if (ensure_pads_directory() != 0) { + printf("Error: Cannot create pads directory\n"); + return 1; + } + + char temp_filename[64]; + char pad_path[MAX_HASH_LENGTH + 20]; + char state_path[MAX_HASH_LENGTH + 20]; char chksum_hex[MAX_HASH_LENGTH]; // Create temporary filename @@ -1099,190 +1129,6 @@ int generate_pad(uint64_t size_bytes, int display_progress) { return 1; } - // Rename file to its chksum - snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum_hex); - snprintf(state_filename, sizeof(state_filename), "%s.state", chksum_hex); - - if (rename(temp_filename, pad_filename) != 0) { - printf("Error: Cannot rename pad file to chksum-based name\n"); - unlink(temp_filename); - return 1; - } - - // Set pad file to read-only - if (chmod(pad_filename, S_IRUSR) != 0) { - printf("Warning: Cannot set pad file to read-only\n"); - } - - // Initialize state file with offset 0 - if (write_state_offset(chksum_hex, 0) != 0) { - printf("Error: Failed to create state file\n"); - unlink(pad_filename); - return 1; - } - - double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); - printf("Generated pad: %s (%.2f GB)\n", pad_filename, size_gb); - printf("Pad chksum: %s\n", chksum_hex); - printf("State file: %s\n", state_filename); - printf("Pad file set to read-only\n"); - - return 0; -} - -int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use_keyboard_entropy) { - if (ensure_pads_directory() != 0) { - printf("Error: Cannot create pads directory\n"); - return 1; - } - - char temp_filename[64]; - char pad_path[MAX_HASH_LENGTH + 20]; - char state_path[MAX_HASH_LENGTH + 20]; - char chksum_hex[MAX_HASH_LENGTH]; - - // Create temporary filename - snprintf(temp_filename, sizeof(temp_filename), "temp_%ld.pad", time(NULL)); - - FILE* urandom = fopen("/dev/urandom", "rb"); - if (!urandom) { - printf("Error: Cannot open /dev/urandom\n"); - return 1; - } - - FILE* pad_file = fopen(temp_filename, "wb"); - if (!pad_file) { - printf("Error: Cannot create temporary pad file %s\n", temp_filename); - fclose(urandom); - return 1; - } - - // Setup keyboard entropy collection if requested - struct termios original_termios; - unsigned char* entropy_buffer = NULL; - size_t entropy_collected = 0; - int terminal_setup = 0; - - if (use_keyboard_entropy) { - entropy_buffer = malloc(MAX_ENTROPY_BUFFER); - if (!entropy_buffer) { - printf("Error: Cannot allocate entropy buffer\n"); - fclose(urandom); - fclose(pad_file); - unlink(temp_filename); - return 1; - } - - if (setup_raw_terminal(&original_termios) == 0) { - terminal_setup = 1; - printf("Type random keys to add entropy (optional):\n"); - } else { - printf("Warning: Cannot setup terminal for keyboard entropy collection\n"); - use_keyboard_entropy = 0; - free(entropy_buffer); - entropy_buffer = NULL; - } - } - - unsigned char urandom_buffer[64 * 1024]; // 64KB buffer - unsigned char output_buffer[64 * 1024]; - uint64_t bytes_written = 0; - - if (display_progress) { - printf("Generating pad...\n"); - if (use_keyboard_entropy) { - printf("(Keyboard entropy: collecting...)\n"); - } - } - - while (bytes_written < size_bytes) { - uint64_t chunk_size = sizeof(urandom_buffer); - if (size_bytes - bytes_written < chunk_size) { - chunk_size = size_bytes - bytes_written; - } - - // Read from /dev/urandom - if (fread(urandom_buffer, 1, (size_t)chunk_size, urandom) != (size_t)chunk_size) { - printf("Error: Failed to read from /dev/urandom\n"); - if (terminal_setup) restore_terminal(&original_termios); - if (entropy_buffer) free(entropy_buffer); - fclose(urandom); - fclose(pad_file); - unlink(temp_filename); - return 1; - } - - if (use_keyboard_entropy && terminal_setup) { - // Collect available keyboard entropy - size_t chunk_entropy = 0; - collect_keyboard_entropy(entropy_buffer + entropy_collected, - MAX_ENTROPY_BUFFER - entropy_collected, &chunk_entropy); - entropy_collected += chunk_entropy; - - if (entropy_collected > 512) { // Have enough entropy to mix - // Copy urandom data to output buffer - memcpy(output_buffer, urandom_buffer, chunk_size); - - // Simple XOR mixing with keyboard entropy - simple_entropy_mix(output_buffer, chunk_size, entropy_buffer, entropy_collected); - - // Reset entropy buffer for next chunk - entropy_collected = 0; - } else { - // Not enough entropy yet, use urandom only - memcpy(output_buffer, urandom_buffer, chunk_size); - } - } else { - // No keyboard entropy, use urandom directly - memcpy(output_buffer, urandom_buffer, chunk_size); - } - - if (fwrite(output_buffer, 1, (size_t)chunk_size, pad_file) != (size_t)chunk_size) { - printf("Error: Failed to write to pad file\n"); - if (terminal_setup) restore_terminal(&original_termios); - if (entropy_buffer) free(entropy_buffer); - fclose(urandom); - fclose(pad_file); - unlink(temp_filename); - return 1; - } - - bytes_written += chunk_size; - - if (display_progress && bytes_written % PROGRESS_UPDATE_INTERVAL == 0) { - printf("\rProgress: %.1f%% ", (double)bytes_written / size_bytes * 100.0); - if (use_keyboard_entropy && terminal_setup) { - printf("(keyboard entropy: %.1fKB) ", (double)entropy_collected / 1024.0); - } - fflush(stdout); - } - } - - if (terminal_setup) { - restore_terminal(&original_termios); - } - if (entropy_buffer) { - free(entropy_buffer); - } - - if (display_progress) { - printf("\rProgress: 100.0%%"); - if (use_keyboard_entropy) { - printf(" (keyboard entropy: MIXED)"); - } - printf("\n"); - } - - fclose(urandom); - fclose(pad_file); - - // Calculate XOR checksum of the pad file - if (calculate_checksum(temp_filename, chksum_hex) != 0) { - printf("Error: Cannot calculate pad checksum\n"); - unlink(temp_filename); - return 1; - } - // Get final paths in pads directory get_pad_path(chksum_hex, pad_path, state_path); @@ -1326,7 +1172,7 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use printf("Warning: Cannot set pad file to read-only\n"); } - // Initialize state file with offset 32 (first 32 bytes used for checksum encryption) + // Initialize state file with offset 32 (first 32 bytes reserved for checksum encryption) FILE* state_file = fopen(state_path, "wb"); if (state_file) { uint64_t reserved_bytes = 32; @@ -1342,10 +1188,139 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use printf("Generated pad: %s (%.2f GB)\n", pad_path, size_gb); printf("Pad checksum: %s\n", chksum_hex); printf("State file: %s\n", state_path); - if (use_keyboard_entropy) { - printf("Enhanced with keyboard entropy!\n"); - } printf("Pad file set to read-only\n"); + printf("Use 'Add entropy' in Pads menu to enhance randomness.\n"); + + return 0; +} + +// In-place pad entropy addition using Chacha20 +int add_entropy_to_pad(const char* pad_chksum, const unsigned char* entropy_data, + size_t entropy_size, int display_progress) { + if (!pad_chksum || !entropy_data || entropy_size < 512) { + printf("Error: Invalid entropy data or insufficient entropy\n"); + return 1; + } + + // Derive Chacha20 key and nonce from entropy + unsigned char key[32], nonce[12]; + if (derive_chacha20_params(entropy_data, entropy_size, key, nonce) != 0) { + printf("Error: Failed to derive Chacha20 parameters from entropy\n"); + return 1; + } + + // Get pad file path + char pad_path[1024]; + char state_path[1024]; + get_pad_path(pad_chksum, pad_path, state_path); + + // Check if pad exists and get size + struct stat pad_stat; + if (stat(pad_path, &pad_stat) != 0) { + printf("Error: Pad file not found: %s\n", pad_path); + return 1; + } + + uint64_t pad_size = pad_stat.st_size; + + // Open pad file for read/write + FILE* pad_file = fopen(pad_path, "r+b"); + if (!pad_file) { + printf("Error: Cannot open pad file for modification: %s\n", pad_path); + printf("Note: Pad files are read-only. Temporarily changing permissions...\n"); + + // Try to make writable temporarily + if (chmod(pad_path, S_IRUSR | S_IWUSR) != 0) { + printf("Error: Cannot change pad file permissions\n"); + return 1; + } + + pad_file = fopen(pad_path, "r+b"); + if (!pad_file) { + printf("Error: Still cannot open pad file for modification\n"); + // Restore read-only + chmod(pad_path, S_IRUSR); + return 1; + } + } + + if (display_progress) { + printf("Adding entropy to pad using Chacha20...\n"); + printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size); + } + + // Process pad in chunks + unsigned char buffer[64 * 1024]; // 64KB chunks + unsigned char keystream[64 * 1024]; + uint64_t offset = 0; + uint32_t counter = 0; + time_t start_time = time(NULL); + + while (offset < pad_size) { + size_t chunk_size = sizeof(buffer); + if (pad_size - offset < chunk_size) { + chunk_size = pad_size - offset; + } + + // Read current pad data + if (fread(buffer, 1, chunk_size, pad_file) != chunk_size) { + printf("Error: Cannot read pad data at offset %lu\n", offset); + fclose(pad_file); + chmod(pad_path, S_IRUSR); // Restore read-only + return 1; + } + + // Generate keystream for this chunk + if (chacha20_encrypt(key, counter, nonce, buffer, keystream, chunk_size) != 0) { + printf("Error: Chacha20 keystream generation failed\n"); + fclose(pad_file); + chmod(pad_path, S_IRUSR); + return 1; + } + + // XOR existing pad with keystream (adds entropy) + for (size_t i = 0; i < chunk_size; i++) { + buffer[i] ^= keystream[i]; + } + + // Seek back and write modified data + if (fseek(pad_file, offset, SEEK_SET) != 0) { + printf("Error: Cannot seek to offset %lu\n", offset); + fclose(pad_file); + chmod(pad_path, S_IRUSR); + return 1; + } + + if (fwrite(buffer, 1, chunk_size, pad_file) != chunk_size) { + printf("Error: Cannot write modified pad data\n"); + fclose(pad_file); + chmod(pad_path, S_IRUSR); + return 1; + } + + offset += chunk_size; + counter += (chunk_size + 63) / 64; // Round up for block count + + // Show progress for large pads + if (display_progress && offset % (64 * 1024 * 1024) == 0) { // Every 64MB + show_progress(offset, pad_size, start_time); + } + } + + fclose(pad_file); + + // Restore read-only permissions + if (chmod(pad_path, S_IRUSR) != 0) { + printf("Warning: Cannot restore pad file to read-only\n"); + } + + if (display_progress) { + show_progress(pad_size, pad_size, start_time); + printf("\n✓ Entropy successfully added to pad using Chacha20\n"); + printf("✓ Pad integrity maintained\n"); + printf("✓ %zu bytes of entropy distributed across entire pad\n", entropy_size); + printf("✓ Pad restored to read-only mode\n"); + } return 0; } @@ -2110,38 +2085,107 @@ void restore_terminal(struct termios* original_termios) { fcntl(STDIN_FILENO, F_SETFL, flags & ~O_NONBLOCK); } -int collect_keyboard_entropy(unsigned char* entropy_buffer, size_t max_size, size_t* collected) { - struct timespec timestamp; +// Enhanced entropy collection with visual feedback +int collect_entropy_with_feedback(unsigned char* entropy_buffer, size_t target_bytes, + size_t* collected_bytes, int allow_early_exit) { + struct termios original_termios; + entropy_collection_state_t state = {0}; + + // Initialize state + state.target_bytes = target_bytes; + state.auto_complete_enabled = allow_early_exit; + state.collection_start_time = get_precise_time(); + + // Setup raw terminal + if (setup_raw_terminal(&original_termios) != 0) { + printf("Error: Cannot setup terminal for entropy collection\n"); + return 1; + } + + // Clear screen area for display + printf("\n\n\n\n\n\n"); + unsigned char entropy_block[16]; + struct timespec timestamp; uint32_t sequence_counter = 0; char key; - *collected = 0; + unsigned char seen_keys[256] = {0}; - while (*collected < max_size - 16) { + *collected_bytes = 0; + + while (state.collected_bytes < target_bytes) { + // Update display + state.quality_score = calculate_overall_quality(&state); + display_entropy_progress(&state); + + // Non-blocking read if (read(STDIN_FILENO, &key, 1) == 1) { + // Handle ESC key for early exit + if (key == 27 && allow_early_exit && state.collected_bytes >= 1024) { + break; // Early exit allowed + } + + // Record keypress timing + double current_time = get_precise_time(); + state.last_keypress_time = current_time; + + // Update key histogram + state.key_histogram[(unsigned char)key]++; + + // Get high precision timestamp clock_gettime(CLOCK_MONOTONIC, ×tamp); - // Create entropy block: [key][timestamp][sequence_counter] + // Create enhanced entropy block: [key][timestamp][sequence][quality_bits] entropy_block[0] = key; memcpy(&entropy_block[1], ×tamp.tv_sec, 8); memcpy(&entropy_block[9], ×tamp.tv_nsec, 4); - memcpy(&entropy_block[13], &sequence_counter, 3); + memcpy(&entropy_block[13], &sequence_counter, 2); + entropy_block[15] = (unsigned char)(current_time * 1000) & 0xFF; // Sub-millisecond timing // Add to entropy buffer - memcpy(entropy_buffer + *collected, entropy_block, 16); - *collected += 16; + if (state.collected_bytes + 16 <= MAX_ENTROPY_BUFFER) { + memcpy(entropy_buffer + state.collected_bytes, entropy_block, 16); + state.collected_bytes += 16; + } + sequence_counter++; + + // Track unique keys + if (!seen_keys[(unsigned char)key]) { + seen_keys[(unsigned char)key] = 1; + state.unique_keys++; + } } else { - // No key available, add some timing entropy + // No key available, add timing entropy clock_gettime(CLOCK_MONOTONIC, ×tamp); - if (*collected + 12 < max_size) { - memcpy(entropy_buffer + *collected, ×tamp, 12); - *collected += 12; + if (state.collected_bytes + 12 < MAX_ENTROPY_BUFFER) { + memcpy(entropy_buffer + state.collected_bytes, ×tamp, 12); + state.collected_bytes += 12; } usleep(1000); // 1ms delay } + + // Auto-complete at target if enabled + if (state.collected_bytes >= target_bytes) { + break; + } } + // Final display update + state.quality_score = calculate_overall_quality(&state); + display_entropy_progress(&state); + + // Summary + double collection_time = get_precise_time() - state.collection_start_time; + printf("\n\n✓ Entropy collection complete!\n"); + printf(" Collected: %zu bytes in %.1f seconds\n", state.collected_bytes, collection_time); + printf(" Quality: %d%% (Excellent: 80%%+, Good: 60%%+)\n", state.quality_score); + printf(" Unique keys: %zu\n", state.unique_keys); + + // Restore terminal + restore_terminal(&original_termios); + + *collected_bytes = state.collected_bytes; return 0; } @@ -2633,18 +2677,202 @@ unsigned char* custom_base64_decode(const char* input, int* output_length) { return decoded; } -// Simple keyboard entropy mixing function -void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size, - const unsigned char* entropy_data, size_t entropy_size) { - if (!entropy_data || entropy_size == 0) return; +// Enhanced entropy system implementation + +double get_precise_time(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1000000000.0; +} + +void draw_progress_bar(double percentage, int width) { + int filled = (int)(percentage / 100.0 * width); + if (filled > width) filled = width; - for (size_t i = 0; i < buffer_size; i++) { - // XOR with entropy data in a rotating pattern - unsigned char entropy_byte = entropy_data[i % entropy_size]; - // Mix position information - entropy_byte ^= (i & 0xFF) ^ ((i >> 8) & 0xFF); - urandom_buffer[i] ^= entropy_byte; + printf("["); + for (int i = 0; i < filled; i++) { + printf("█"); } + for (int i = filled; i < width; i++) { + printf("░"); + } + printf("]"); +} + +void draw_quality_bar(double quality, int width, const char* label) { + int filled = (int)(quality / 100.0 * width); + if (filled > width) filled = width; + + // Color coding based on quality + const char* color; + if (quality >= 80) color = "\033[32m"; // Green + else if (quality >= 60) color = "\033[33m"; // Yellow + else color = "\033[31m"; // Red + + printf("%s", color); + draw_progress_bar(quality, width); + printf("\033[0m %-10s", label); // Reset color +} + +double calculate_timing_quality(const entropy_collection_state_t* state) { + // Analyze timing variance between keypresses + if (state->collected_bytes < 32) return 0.0; // Need minimum data + + // Simplified timing quality based on collection rate and variation + double elapsed = get_precise_time() - state->collection_start_time; + if (elapsed < 0.1) return 0.0; + + double rate = state->collected_bytes / elapsed; + + // Optimal rate is around 50-200 bytes/second (moderate typing with good timing variance) + if (rate >= 50 && rate <= 200) return 90.0; + if (rate >= 20 && rate <= 500) return 70.0; + if (rate >= 10 && rate <= 1000) return 50.0; + return 30.0; +} + +double calculate_variety_quality(const entropy_collection_state_t* state) { + // Analyze key variety and distribution + if (state->collected_bytes < 16) return 0.0; + + // Calculate entropy from key histogram + double entropy = 0.0; + size_t total_keys = 0; + + // Count total keypresses + for (int i = 0; i < 256; i++) { + total_keys += state->key_histogram[i]; + } + + if (total_keys == 0) return 0.0; + + // Calculate Shannon entropy + for (int i = 0; i < 256; i++) { + if (state->key_histogram[i] > 0) { + double p = (double)state->key_histogram[i] / total_keys; + entropy -= p * log2(p); + } + } + + // Convert entropy to quality score (0-100) + double max_entropy = log2(256); // Perfect entropy for 8-bit keyspace + double normalized_entropy = entropy / max_entropy; + + // Scale based on unique keys as well + double unique_key_factor = (double)state->unique_keys / 50.0; // 50+ unique keys is excellent + if (unique_key_factor > 1.0) unique_key_factor = 1.0; + + return (normalized_entropy * 70.0 + unique_key_factor * 30.0); +} + +unsigned char calculate_overall_quality(const entropy_collection_state_t* state) { + double timing = calculate_timing_quality(state); + double variety = calculate_variety_quality(state); + + // Simple collection progress bonus + double progress_bonus = (double)state->collected_bytes / state->target_bytes * 20.0; + if (progress_bonus > 20.0) progress_bonus = 20.0; + + // Weighted average + double overall = (timing * 0.4 + variety * 0.4 + progress_bonus); + if (overall > 100.0) overall = 100.0; + + return (unsigned char)overall; +} + +void display_entropy_progress(const entropy_collection_state_t* state) { + // Calculate percentages + double progress = (double)state->collected_bytes / state->target_bytes * 100.0; + if (progress > 100.0) progress = 100.0; + + double quality = state->quality_score; + double timing_quality = calculate_timing_quality(state); + double variety_quality = calculate_variety_quality(state); + + // Clear previous output and redraw + printf("\033[2K\r"); // Clear line + printf("\033[A\033[2K\r"); // Move up and clear + printf("\033[A\033[2K\r"); // Move up and clear + printf("\033[A\033[2K\r"); // Move up and clear + printf("\033[A\033[2K\r"); // Move up and clear + printf("\033[A\033[2K\r"); // Move up and clear + + // Header + printf("Adding Entropy to Pad - Target: %zu bytes\n\n", state->target_bytes); + + // Main progress bar + printf("Progress: "); + draw_progress_bar(progress, 50); + printf(" %.1f%% (%zu/%zu bytes)\n", progress, state->collected_bytes, state->target_bytes); + + // Quality indicators + printf("Quality: "); + draw_quality_bar(quality, 50, "OVERALL"); + printf("\n"); + + printf("Timing: "); + draw_quality_bar(timing_quality, 50, "VARIED"); + printf("\n"); + + printf("Keys: "); + draw_quality_bar(variety_quality, 50, "DIVERSE"); + printf("\n"); + + // Instructions + if (state->collected_bytes >= 1024 && state->auto_complete_enabled) { + printf("\nPress ESC to finish (minimum reached) or continue typing..."); + } else if (state->collected_bytes < 1024) { + printf("\nType random keys... (%zu more bytes needed)", 1024 - state->collected_bytes); + } else { + printf("\nType random keys or press ESC when satisfied..."); + } + + fflush(stdout); +} + +// Chacha20 key derivation from collected entropy +int derive_chacha20_params(const unsigned char* entropy_data, size_t entropy_size, + unsigned char key[32], unsigned char nonce[12]) { + if (!entropy_data || entropy_size < 512 || !key || !nonce) { + return 1; // Error: insufficient entropy or null pointers + } + + // Phase 1: Generate base key from entropy using enhanced XOR checksum method + unsigned char enhanced_checksum[44]; // 32 key + 12 nonce + memset(enhanced_checksum, 0, 44); + + // Mix entropy data similar to calculate_checksum but for 44 bytes + for (size_t i = 0; i < entropy_size; i++) { + unsigned char bucket = i % 44; + enhanced_checksum[bucket] ^= entropy_data[i] ^ + ((i >> 8) & 0xFF) ^ + ((i >> 16) & 0xFF) ^ + ((i >> 24) & 0xFF); + } + + // Phase 2: Add system entropy for additional randomness + unsigned char system_entropy[32]; + FILE* urandom = fopen("/dev/urandom", "rb"); + if (!urandom) { + return 2; // Error: cannot access system entropy + } + + if (fread(system_entropy, 1, 32, urandom) != 32) { + fclose(urandom); + return 2; // Error: insufficient system entropy + } + fclose(urandom); + + // Mix system entropy into derived key + for (int i = 0; i < 32; i++) { + enhanced_checksum[i] ^= system_entropy[i]; + } + + // Extract key and nonce + memcpy(key, enhanced_checksum, 32); + memcpy(nonce, enhanced_checksum + 32, 12); + + return 0; // Success } //////////////////////////////////////////////////////////////////////////////// @@ -3848,6 +4076,7 @@ int handle_pads_menu(void) { printf("\nPad Actions:\n"); printf(" \033[4mI\033[0mnfo - Show detailed pad information\n"); + printf(" \033[4mA\033[0mdd entropy - Enhance pad randomness\n"); printf(" \033[4mB\033[0mack to pad list\n"); printf("\nSelect action: "); @@ -3860,6 +4089,9 @@ int handle_pads_menu(void) { char action_choice = toupper(action[0]); if (action_choice == 'I') { return show_pad_info(pads[selected_pad].chksum); + } else if (action_choice == 'A') { + // Handle entropy addition + return handle_add_entropy_to_pad(pads[selected_pad].chksum); } // Default: back to pad list (recursive call) @@ -3996,6 +4228,103 @@ void get_directory_display(const char* file_path, char* result, size_t result_si } } +int handle_add_entropy_to_pad(const char* pad_chksum) { + printf("\n=== Add Entropy to Pad: %.16s... ===\n", pad_chksum); + printf("This will enhance the randomness of your pad using keyboard entropy.\n"); + printf("The entropy will be processed with Chacha20 and distributed throughout the entire pad.\n\n"); + + printf("Entropy collection options:\n"); + printf(" 1. Recommended (2048 bytes) - Optimal security\n"); + printf(" 2. Minimum (1024 bytes) - Good security\n"); + printf(" 3. Maximum (4096 bytes) - Maximum security\n"); + printf(" 4. Custom amount\n"); + printf("Enter choice (1-4): "); + + char choice_input[10]; + if (!fgets(choice_input, sizeof(choice_input), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + + size_t target_bytes = 2048; // Default + int choice = atoi(choice_input); + + switch (choice) { + case 1: + target_bytes = 2048; + break; + case 2: + target_bytes = 1024; + break; + case 3: + target_bytes = 4096; + break; + case 4: + printf("Enter custom amount (512-8192 bytes): "); + char custom_input[32]; + if (!fgets(custom_input, sizeof(custom_input), stdin)) { + printf("Error: Failed to read input\n"); + return 1; + } + + size_t custom_amount = (size_t)atoi(custom_input); + if (custom_amount < 512 || custom_amount > 8192) { + printf("Error: Invalid amount. Must be between 512 and 8192 bytes.\n"); + return 1; + } + target_bytes = custom_amount; + break; + default: + target_bytes = 2048; // Default to recommended + break; + } + + printf("\nCollecting %zu bytes of entropy...\n", target_bytes); + + // Allocate entropy buffer + unsigned char* entropy_buffer = malloc(MAX_ENTROPY_BUFFER); + if (!entropy_buffer) { + printf("Error: Cannot allocate entropy buffer\n"); + return 1; + } + + // Collect entropy with visual feedback + size_t collected_bytes = 0; + int result = collect_entropy_with_feedback(entropy_buffer, target_bytes, &collected_bytes, 1); + + if (result != 0) { + printf("Error: Entropy collection failed\n"); + free(entropy_buffer); + return 1; + } + + if (collected_bytes < 512) { + printf("Error: Insufficient entropy collected (%zu bytes)\n", collected_bytes); + free(entropy_buffer); + return 1; + } + + printf("\nProcessing entropy and modifying pad...\n"); + + // Add entropy to pad + result = add_entropy_to_pad(pad_chksum, entropy_buffer, collected_bytes, 1); + + // Clear entropy buffer for security + memset(entropy_buffer, 0, MAX_ENTROPY_BUFFER); + free(entropy_buffer); + + if (result != 0) { + printf("Error: Failed to add entropy to pad\n"); + return 1; + } + + printf("\n🎉 SUCCESS! Your pad now has enhanced randomness!\n"); + printf("Press Enter to continue..."); + getchar(); + + return 0; +} + void print_usage(const char* program_name) { printf("OTP Cipher - One Time Pad Implementation %s\n", get_version()); printf("%s\n", get_build_info()); diff --git a/otp.o b/otp.o new file mode 100644 index 0000000000000000000000000000000000000000..fcc4e39e72d27eb471c31518af9dd86d41c9979b GIT binary patch literal 132296 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 literal 0 HcmV?d00001