diff --git a/.gitignore b/.gitignore index ca2707a..2b900aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ otp pads/ Gemini.md +TropicOfCancer-HenryMiller.txt # Auto-generated version files src/version.h diff --git a/README.md b/README.md index 6fdd86a..d32de4c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ -r# OTP Cipher - One Time Pad Implementation +# OTP Cipher - One Time Pad Implementation A secure one-time pad (OTP) cipher implementation in C with automatic versioning system. ## Features - **Perfect Security**: Implements true one-time pad encryption with information-theoretic security +- **Text & File Encryption**: Supports both inline text and file encryption +- **Multiple Output Formats**: Binary (.otp) and ASCII armored (.otp.asc) file formats - **Keyboard Entropy**: Optional keyboard entropy collection for enhanced randomness +- **Short Command Flags**: Convenient single-character flags for all operations - **Automatic Versioning**: Built-in semantic versioning with automatic patch increment - **Multiple Build Options**: Standard and static linking builds - **Cross-Platform**: Works on Linux and other UNIX-like systems @@ -148,45 +151,124 @@ otp/ └── VERSION # Plain text version (generated) ``` -## Examples +## File Formats + +### .otp File Format (Binary) + +Binary encrypted files use a structured header format: + +``` +Offset | Size | Field | Description +-------|------|-------------------|---------------------------------- +0 | 4 | Magic | "OTP\0" - File type identifier +4 | 2 | Version | Format version (currently 1) +6 | 32 | Pad Checksum | Binary pad checksum (32 bytes) +38 | 8 | Pad Offset | Offset in pad file (uint64_t) +46 | 2 | Filename Length | Original filename length (uint16_t) +48 | var | Original Filename | Original filename string +var | 4 | File Mode | Original file permissions (uint32_t) +var | 8 | File Size | Original file size (uint64_t) +var | var | Encrypted Data | XOR-encrypted file contents +``` + +### .otp.asc File Format (ASCII Armored) + +ASCII armored files use the same format as encrypted text messages: + +``` +-----BEGIN OTP MESSAGE----- +Version: v0.2.15 +Pad-ChkSum: <64-character-hex-checksum> +Pad-Offset: + + +-----END OTP MESSAGE----- +``` + +**Note:** ASCII armored files lose original filename and permission metadata. + +## Usage Examples + +### Short Command Flags +```bash +# Quick commands using short flags +./otp -g 1GB # Generate 1GB pad +./otp -l # List available pads +./otp -e 1a2b "Hello world" # Encrypt text inline +./otp -d "-----BEGIN OTP..." # Decrypt message inline + +# File operations +./otp -f document.pdf 1a2b # Encrypt file (binary) +./otp -f document.pdf 1a2b -a # Encrypt file (ASCII) +./otp -f document.pdf 1a2b -o secret.otp # Custom output name +``` + +### Text Encryption +```bash +# Interactive text encryption +./otp encrypt 1a2b3c +Enter text to encrypt: This is my secret message +# Outputs ASCII armored message + +# Inline text encryption +./otp -e 1a2b3c "This is my secret message" +# Outputs ASCII armored message immediately +``` + +### File Encryption +```bash +# Binary format (preserves metadata) +./otp -f sensitive.doc a1b2c3 + +# ASCII armored format (text-safe) +./otp -f sensitive.doc a1b2c3 -a + +# Custom output filename +./otp -f sensitive.doc a1b2c3 -o encrypted_document.otp +``` + +### Decryption +```bash +# Auto-detect format and pad from message/file +./otp -d encrypted.otp.asc +./otp -d "-----BEGIN OTP MESSAGE-----..." + +# Interactive mode +./otp decrypt +# Prompts for encrypted message input +``` ### Build and Version Tracking ```bash $ ./build.sh build [INFO] Incrementing version... -[INFO] Current version: v0.1.4 -[INFO] New version: v0.1.5 -[SUCCESS] Created new version tag: v0.1.5 +[INFO] Current version: v0.2.14 +[INFO] New version: v0.2.15 +[SUCCESS] Created new version tag: v0.2.15 [SUCCESS] Build completed successfully -$ ./otp -=== OTP Cipher v0.1.5 === - -=== Main Menu === -1. Generate new pad -2. Encrypt message -3. Decrypt message -4. List available pads -5. Show pad information -6. Exit - $ ./otp --help -OTP Cipher - One Time Pad Implementation v0.1.5 -Built on 2025-08-10 at 08:17:47 from commit 9edfa5f on branch master -Usage: - ./otp - Interactive mode - ... +OTP Cipher - One Time Pad Implementation v0.2.15 +Built on 2025-08-10 at 14:07:58 from commit ae0afcf on branch master ``` -### Version History +### Advanced Features ```bash -$ git tag --list -v0.1.0 -v0.1.1 -v0.1.2 -v0.1.3 -v0.1.4 -v0.1.5 +# Generate pad with keyboard entropy +./otp generate 5GB +# Follow prompts for keyboard entropy collection + +# Check pad usage +./otp -l +Available pads: +No. ChkSum (first 16 chars) Size Used % Used +--- ------------------- ---------- ---------- ------ +1 97d9d82b5414a943 1.00GB 156B 0.0% +2 0c8e19fde996e683 1000B 248B 24.8% + +# Show detailed pad information +./otp +# Select "S" for show pad info, enter checksum or prefix ``` ## License @@ -199,5 +281,3 @@ When contributing: 1. The version will automatically increment on builds 2. For major features, consider manually creating minor version tags 3. Generated version files (`src/version.*`, `VERSION`) should not be committed -# Test change -# Testing -m flag diff --git a/otp.c b/otp.c index deb8134..01fc1a0 100644 --- a/otp.c +++ b/otp.c @@ -52,6 +52,10 @@ 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 encrypt_file(const char* pad_identifier, const char* input_file, const char* output_file, int ascii_armor); +int decrypt_file(const char* input_file, const char* output_file); +int decrypt_binary_file(FILE* input_fp, const char* output_file); +int decrypt_ascii_file(const char* input_file, const char* output_file); // Keyboard entropy functions int setup_raw_terminal(struct termios* original_termios); @@ -147,9 +151,9 @@ int interactive_mode(void) { } int command_line_mode(int argc, char* argv[]) { - if (strcmp(argv[1], "generate") == 0) { + if (strcmp(argv[1], "generate") == 0 || strcmp(argv[1], "-g") == 0) { if (argc != 3) { - printf("Usage: %s generate \n", argv[0]); + printf("Usage: %s generate|-g \n", argv[0]); printf("Size examples: 1024, 1GB, 5TB, 512MB\n"); return 1; } @@ -160,25 +164,80 @@ int command_line_mode(int argc, char* argv[]) { } return generate_pad_with_entropy(size, 1, 0); // No keyboard entropy for command line } - else if (strcmp(argv[1], "encrypt") == 0) { + else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) { if (argc < 3 || argc > 4) { - printf("Usage: %s encrypt [text_to_encrypt]\n", argv[0]); + printf("Usage: %s encrypt|-e [text_to_encrypt]\n", argv[0]); return 1; } // Pass text if provided, otherwise NULL for interactive mode const char* text = (argc == 4) ? argv[3] : NULL; return encrypt_text(argv[2], text); } - else if (strcmp(argv[1], "decrypt") == 0) { - if (argc < 3 || argc > 4) { - printf("Usage: %s decrypt [encrypted_message]\n", argv[0]); + else if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) { + if (argc == 2) { + // Interactive mode - no arguments needed + return decrypt_text(NULL, NULL); + } + else if (argc == 3) { + // Check if the argument looks like an encrypted message (starts with -----) + if (strncmp(argv[2], "-----BEGIN OTP MESSAGE-----", 27) == 0) { + // Inline decrypt with message only + return decrypt_text(NULL, argv[2]); + } else { + // Check if it's a file (contains . or ends with known extensions) + if (strstr(argv[2], ".") != NULL) { + // Treat as file + return decrypt_file(argv[2], NULL); + } else { + // Interactive decrypt with pad hint (legacy support) + return decrypt_text(argv[2], NULL); + } + } + } + else if (argc == 4) { + // Check for -o flag for output file + if (strcmp(argv[2], "-o") == 0) { + printf("Usage: %s decrypt|-d [-o ]\n", argv[0]); + return 1; + } else { + // Legacy format: pad_chksum and message, or file with output + return decrypt_text(argv[2], argv[3]); + } + } + else if (argc == 5 && strcmp(argv[3], "-o") == 0) { + // File decryption with output: -d -o + return decrypt_file(argv[2], argv[4]); + } + else { + printf("Usage: %s decrypt|-d [encrypted_message|file] [-o output_file]\n", argv[0]); + printf(" %s decrypt|-d [encrypted_message] (pad info from message)\n", argv[0]); return 1; } - // Pass message if provided, otherwise NULL for interactive mode - const char* message = (argc == 4) ? argv[3] : NULL; - return decrypt_text(argv[2], message); } - else if (strcmp(argv[1], "list") == 0) { + else if (strcmp(argv[1], "-f") == 0) { + // File encryption mode: -f [-a] [-o ] + if (argc < 4) { + printf("Usage: %s -f [-a] [-o ]\n", argv[0]); + return 1; + } + + const char* input_file = argv[2]; + const char* pad_prefix = argv[3]; + int ascii_armor = 0; + const char* output_file = NULL; + + // Parse optional flags + for (int i = 4; i < argc; i++) { + if (strcmp(argv[i], "-a") == 0) { + ascii_armor = 1; + } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { + output_file = argv[++i]; + } + } + + return encrypt_file(pad_prefix, input_file, output_file, ascii_armor); + } + else if (strcmp(argv[1], "list") == 0 || strcmp(argv[1], "-l") == 0) { return list_available_pads(); } else { @@ -1128,6 +1187,612 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) { return 0; } +int encrypt_file(const char* pad_identifier, const char* input_file, const char* output_file, int ascii_armor) { + char* pad_chksum = find_pad_by_prefix(pad_identifier); + if (!pad_chksum) { + return 1; + } + + char chksum_hex[MAX_HASH_LENGTH]; + uint64_t current_offset; + + 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 input file exists and get its size + struct stat input_stat; + if (stat(input_file, &input_stat) != 0) { + printf("Error: Input file %s not found\n", input_file); + free(pad_chksum); + return 1; + } + + uint64_t file_size = input_stat.st_size; + if (file_size == 0) { + printf("Error: Input file is empty\n"); + free(pad_chksum); + return 1; + } + + // Check if pad file exists + if (access(pad_path, R_OK) != 0) { + printf("Error: Pad file %s not found\n", pad_path); + free(pad_chksum); + return 1; + } + + // Read current offset + if (read_state_offset(pad_chksum, ¤t_offset) != 0) { + printf("Error: Cannot read state file\n"); + free(pad_chksum); + return 1; + } + + // Ensure we never encrypt before offset 32 + if (current_offset < 32) { + printf("Warning: State offset below reserved area, adjusting to 32\n"); + current_offset = 32; + if (write_state_offset(pad_chksum, current_offset) != 0) { + printf("Warning: Failed to update state file\n"); + } + } + + // Calculate XOR checksum of pad file + if (calculate_checksum(pad_path, chksum_hex) != 0) { + printf("Error: Cannot calculate pad checksum\n"); + free(pad_chksum); + return 1; + } + + // Check if we have enough pad space + struct stat pad_stat; + if (stat(pad_path, &pad_stat) != 0) { + printf("Error: Cannot get pad file size\n"); + free(pad_chksum); + return 1; + } + + if (current_offset + file_size > (uint64_t)pad_stat.st_size) { + printf("Error: Not enough pad space remaining\n"); + printf("Need: %lu bytes, Available: %lu bytes\n", + file_size, (uint64_t)pad_stat.st_size - current_offset); + free(pad_chksum); + return 1; + } + + // Generate output filename if not specified + char default_output[512]; + if (output_file == NULL) { + if (ascii_armor) { + snprintf(default_output, sizeof(default_output), "%s.otp.asc", input_file); + } else { + snprintf(default_output, sizeof(default_output), "%s.otp", input_file); + } + output_file = default_output; + } + + // Open input file + FILE* input_fp = fopen(input_file, "rb"); + if (!input_fp) { + printf("Error: Cannot open input file %s\n", input_file); + free(pad_chksum); + return 1; + } + + // Open pad file + FILE* pad_file = fopen(pad_path, "rb"); + if (!pad_file) { + printf("Error: Cannot open pad file\n"); + fclose(input_fp); + free(pad_chksum); + return 1; + } + + if (fseek(pad_file, current_offset, SEEK_SET) != 0) { + printf("Error: Cannot seek to offset in pad file\n"); + fclose(input_fp); + fclose(pad_file); + free(pad_chksum); + return 1; + } + + // Read and encrypt file + unsigned char buffer[64 * 1024]; + unsigned char pad_buffer[64 * 1024]; + unsigned char* encrypted_data = malloc(file_size); + uint64_t bytes_processed = 0; + time_t start_time = time(NULL); + + printf("Encrypting %s...\n", input_file); + + while (bytes_processed < file_size) { + uint64_t chunk_size = sizeof(buffer); + if (file_size - bytes_processed < chunk_size) { + chunk_size = file_size - bytes_processed; + } + + // Read file data + if (fread(buffer, 1, chunk_size, input_fp) != chunk_size) { + printf("Error: Cannot read input file data\n"); + free(encrypted_data); + fclose(input_fp); + fclose(pad_file); + free(pad_chksum); + return 1; + } + + // Read pad data + if (fread(pad_buffer, 1, chunk_size, pad_file) != chunk_size) { + printf("Error: Cannot read pad data\n"); + free(encrypted_data); + fclose(input_fp); + fclose(pad_file); + free(pad_chksum); + return 1; + } + + // XOR encrypt + for (uint64_t i = 0; i < chunk_size; i++) { + encrypted_data[bytes_processed + i] = buffer[i] ^ pad_buffer[i]; + } + + bytes_processed += chunk_size; + + // Show progress for large files (> 10MB) + if (file_size > 10 * 1024 * 1024 && bytes_processed % (1024 * 1024) == 0) { + show_progress(bytes_processed, file_size, start_time); + } + } + + if (file_size > 10 * 1024 * 1024) { + show_progress(file_size, file_size, start_time); + printf("\n"); + } + + fclose(input_fp); + fclose(pad_file); + + // Write output file + if (ascii_armor) { + // ASCII armored format - same as message format + FILE* output_fp = fopen(output_file, "w"); + if (!output_fp) { + printf("Error: Cannot create output file %s\n", output_file); + free(encrypted_data); + free(pad_chksum); + 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); + } + + fprintf(output_fp, "-----END OTP MESSAGE-----\n"); + + fclose(output_fp); + free(base64_data); + } else { + // Binary format + FILE* output_fp = fopen(output_file, "wb"); + if (!output_fp) { + printf("Error: Cannot create output file %s\n", output_file); + free(encrypted_data); + free(pad_chksum); + return 1; + } + + // Write binary header + // Magic: "OTP\0" + fwrite("OTP\0", 1, 4, output_fp); + + // Version: 2 bytes + uint16_t version = 1; + fwrite(&version, sizeof(uint16_t), 1, output_fp); + + // Pad checksum: 32 bytes (binary) + unsigned char pad_chksum_bin[32]; + for (int i = 0; i < 32; i++) { + sscanf(chksum_hex + i*2, "%2hhx", &pad_chksum_bin[i]); + } + fwrite(pad_chksum_bin, 1, 32, output_fp); + + // Pad offset: 8 bytes + fwrite(¤t_offset, sizeof(uint64_t), 1, output_fp); + + // Original filename length and name + const char* base_filename = strrchr(input_file, '/'); + if (base_filename) { + base_filename++; // Skip the '/' + } else { + base_filename = input_file; + } + uint16_t filename_len = strlen(base_filename); + fwrite(&filename_len, sizeof(uint16_t), 1, output_fp); + fwrite(base_filename, 1, filename_len, output_fp); + + // File mode: 4 bytes + uint32_t file_mode = input_stat.st_mode; + fwrite(&file_mode, sizeof(uint32_t), 1, output_fp); + + // File size: 8 bytes + fwrite(&file_size, sizeof(uint64_t), 1, output_fp); + + // Encrypted data + fwrite(encrypted_data, 1, file_size, output_fp); + + fclose(output_fp); + } + + // Update state offset + if (write_state_offset(pad_chksum, current_offset + file_size) != 0) { + printf("Warning: Failed to update state file\n"); + } + + printf("File encrypted successfully: %s\n", output_file); + if (ascii_armor) { + printf("Format: ASCII armored (.otp.asc)\n"); + } else { + printf("Format: Binary (.otp)\n"); + } + + // Cleanup + free(encrypted_data); + free(pad_chksum); + + return 0; +} + +int decrypt_file(const char* input_file, const char* output_file) { + // Check if input file exists + if (access(input_file, R_OK) != 0) { + printf("Error: Input file %s not found\n", input_file); + return 1; + } + + FILE* input_fp = fopen(input_file, "rb"); + if (!input_fp) { + printf("Error: Cannot open input file %s\n", input_file); + return 1; + } + + // Read first few bytes to determine format + char magic[4]; + if (fread(magic, 1, 4, input_fp) != 4) { + printf("Error: Cannot read file header\n"); + fclose(input_fp); + return 1; + } + + fseek(input_fp, 0, SEEK_SET); // Reset to beginning + + if (memcmp(magic, "OTP\0", 4) == 0) { + // Binary format + return decrypt_binary_file(input_fp, output_file); + } else { + // Assume ASCII armored format, read entire file as text + fclose(input_fp); + return decrypt_ascii_file(input_file, output_file); + } +} + +int decrypt_binary_file(FILE* input_fp, const char* output_file) { + // Read binary header + char magic[4]; + uint16_t version; + unsigned char pad_chksum_bin[32]; + uint64_t pad_offset; + uint16_t filename_len; + uint32_t file_mode; + uint64_t file_size; + + if (fread(magic, 1, 4, input_fp) != 4 || + fread(&version, sizeof(uint16_t), 1, input_fp) != 1 || + fread(pad_chksum_bin, 1, 32, input_fp) != 32 || + fread(&pad_offset, sizeof(uint64_t), 1, input_fp) != 1 || + fread(&filename_len, sizeof(uint16_t), 1, input_fp) != 1) { + printf("Error: Cannot read binary header\n"); + fclose(input_fp); + return 1; + } + + if (memcmp(magic, "OTP\0", 4) != 0) { + printf("Error: Invalid binary format\n"); + fclose(input_fp); + return 1; + } + + // Read original filename + char* original_filename = malloc(filename_len + 1); + if (fread(original_filename, 1, filename_len, input_fp) != filename_len) { + printf("Error: Cannot read original filename\n"); + free(original_filename); + fclose(input_fp); + return 1; + } + original_filename[filename_len] = '\0'; + + // Read remaining header + if (fread(&file_mode, sizeof(uint32_t), 1, input_fp) != 1 || + fread(&file_size, sizeof(uint64_t), 1, input_fp) != 1) { + printf("Error: Cannot read file metadata\n"); + free(original_filename); + fclose(input_fp); + return 1; + } + + // Convert binary checksum to hex + char pad_chksum_hex[65]; + for (int i = 0; i < 32; i++) { + sprintf(pad_chksum_hex + i*2, "%02x", pad_chksum_bin[i]); + } + pad_chksum_hex[64] = '\0'; + + printf("Decrypting binary file...\n"); + printf("Original filename: %s\n", original_filename); + printf("File size: %lu bytes\n", file_size); + + // Check if we have the required pad + char pad_path[MAX_HASH_LENGTH + 20]; + char state_path[MAX_HASH_LENGTH + 20]; + get_pad_path(pad_chksum_hex, pad_path, state_path); + + if (access(pad_path, R_OK) != 0) { + printf("Error: Required pad not found: %s\n", pad_chksum_hex); + printf("Available pads:\n"); + list_available_pads(); + free(original_filename); + fclose(input_fp); + return 1; + } + + // Determine output filename + char default_output[512]; + if (output_file == NULL) { + strncpy(default_output, original_filename, sizeof(default_output) - 1); + default_output[sizeof(default_output) - 1] = '\0'; + output_file = default_output; + } + + // Read encrypted data + unsigned char* encrypted_data = malloc(file_size); + if (fread(encrypted_data, 1, file_size, input_fp) != file_size) { + printf("Error: Cannot read encrypted data\n"); + free(original_filename); + free(encrypted_data); + fclose(input_fp); + return 1; + } + fclose(input_fp); + + // Open pad file and decrypt + FILE* pad_file = fopen(pad_path, "rb"); + if (!pad_file) { + printf("Error: Cannot open pad file\n"); + free(original_filename); + free(encrypted_data); + return 1; + } + + if (fseek(pad_file, pad_offset, SEEK_SET) != 0) { + printf("Error: Cannot seek to offset in pad file\n"); + free(original_filename); + free(encrypted_data); + fclose(pad_file); + return 1; + } + + unsigned char* pad_data = malloc(file_size); + if (fread(pad_data, 1, file_size, pad_file) != file_size) { + printf("Error: Cannot read pad data\n"); + free(original_filename); + free(encrypted_data); + free(pad_data); + fclose(pad_file); + return 1; + } + fclose(pad_file); + + // XOR decrypt + for (uint64_t i = 0; i < file_size; i++) { + encrypted_data[i] ^= pad_data[i]; + } + + // Write decrypted file + FILE* output_fp = fopen(output_file, "wb"); + if (!output_fp) { + printf("Error: Cannot create output file %s\n", output_file); + free(original_filename); + free(encrypted_data); + free(pad_data); + return 1; + } + + if (fwrite(encrypted_data, 1, file_size, output_fp) != file_size) { + printf("Error: Cannot write decrypted data\n"); + free(original_filename); + free(encrypted_data); + free(pad_data); + fclose(output_fp); + return 1; + } + fclose(output_fp); + + // Restore file permissions + if (chmod(output_file, file_mode) != 0) { + printf("Warning: Cannot restore file permissions\n"); + } + + printf("File decrypted successfully: %s\n", output_file); + printf("Restored permissions and metadata\n"); + + // Cleanup + free(original_filename); + free(encrypted_data); + free(pad_data); + + return 0; +} + +int decrypt_ascii_file(const char* input_file, const char* output_file) { + FILE* input_fp = fopen(input_file, "r"); + if (!input_fp) { + printf("Error: Cannot open input file %s\n", input_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; + + 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"); + return 1; + } + + printf("Decrypting ASCII armored file...\n"); + + // Check if we have the required pad + char pad_path[MAX_HASH_LENGTH + 20]; + char state_path[MAX_HASH_LENGTH + 20]; + get_pad_path(stored_chksum, pad_path, state_path); + + if (access(pad_path, R_OK) != 0) { + printf("Error: Required pad not found: %s\n", stored_chksum); + printf("Available pads:\n"); + list_available_pads(); + return 1; + } + + // Decode base64 + int ciphertext_len; + unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len); + if (!ciphertext) { + printf("Error: Invalid base64 data\n"); + return 1; + } + + // Determine output filename + char default_output[512]; + if (output_file == NULL) { + // Remove .otp.asc extension if present + strncpy(default_output, input_file, sizeof(default_output) - 1); + default_output[sizeof(default_output) - 1] = '\0'; + + char* ext = strstr(default_output, ".otp.asc"); + if (ext) { + *ext = '\0'; + } else { + // Just add .decrypted suffix + strncat(default_output, ".decrypted", sizeof(default_output) - strlen(default_output) - 1); + } + 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"); + 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"); + 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"); + if (!output_fp) { + printf("Error: Cannot create output file %s\n", output_file); + free(ciphertext); + free(pad_data); + return 1; + } + + if (fwrite(ciphertext, 1, ciphertext_len, output_fp) != (size_t)ciphertext_len) { + printf("Error: Cannot write decrypted data\n"); + free(ciphertext); + free(pad_data); + fclose(output_fp); + return 1; + } + fclose(output_fp); + + printf("File decrypted successfully: %s\n", output_file); + printf("Note: ASCII format does not preserve original filename/permissions\n"); + + // Cleanup + free(ciphertext); + free(pad_data); + + return 0; +} + int read_state_offset(const char* pad_chksum, uint64_t* offset) { char state_filename[MAX_HASH_LENGTH + 20]; snprintf(state_filename, sizeof(state_filename), "%s/%s.state", PADS_DIR, pad_chksum); @@ -1395,16 +2060,27 @@ void print_usage(const char* program_name) { printf("OTP Cipher - One Time Pad Implementation %s\n", get_version()); printf("%s\n", get_build_info()); printf("Usage:\n"); - printf(" %s - Interactive mode\n", program_name); - printf(" %s generate - Generate new pad\n", program_name); - printf(" %s encrypt [text] - Encrypt text\n", program_name); - printf(" %s decrypt [message] - Decrypt message\n", program_name); - printf(" %s list - List available pads\n", program_name); + printf(" %s - Interactive mode\n", program_name); + printf(" %s generate|-g - Generate new pad\n", program_name); + printf(" %s encrypt|-e [text] - Encrypt text\n", program_name); + printf(" %s decrypt|-d [encrypted_message] - Decrypt message\n", program_name); + printf(" %s -f [-a] [-o ] - Encrypt file\n", program_name); + printf(" %s list|-l - List available pads\n", program_name); + printf("\nFile Operations:\n"); + printf(" -f - Encrypt file (binary .otp format)\n"); + printf(" -f -a - Encrypt file (ASCII .otp.asc format)\n"); + printf(" -o - Specify output filename\n"); + printf("\nShort flags:\n"); + printf(" -g generate -e encrypt -d decrypt -l list -f file\n"); printf("\nExamples:\n"); - printf(" %s encrypt 1a2b3c \"Hello world\" - Encrypt inline text\n", program_name); - printf(" %s encrypt 1a2b3c - Encrypt interactively\n", program_name); - printf(" %s decrypt 1a2b3c \"-----BEGIN OTP...\" - Decrypt inline message\n", program_name); - printf(" %s decrypt 1a2b3c - Decrypt interactively\n", program_name); + printf(" %s -e 1a2b3c \"Hello world\" - Encrypt inline text\n", program_name); + printf(" %s -f document.pdf 1a2b - Encrypt file (binary)\n", program_name); + printf(" %s -f document.pdf 1a2b -a - Encrypt file (ASCII)\n", program_name); + printf(" %s -f document.pdf 1a2b -o secret.otp - Encrypt with custom output\n", program_name); + printf(" %s -d \"-----BEGIN OTP MESSAGE-----...\" - Decrypt message/file\n", program_name); + printf(" %s -d encrypted.otp.asc - Decrypt ASCII file\n", program_name); + printf(" %s -g 1GB - Generate 1GB pad\n", program_name); + printf(" %s -l - List pads\n", program_name); printf("\nSize examples: 1GB, 5TB, 512MB, 2048 (bytes)\n"); printf("Pad selection: Full chksum, prefix, or number from list\n"); } diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..d1083a9 --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +This is a test file for OTP encryption. diff --git a/test.txt.otp b/test.txt.otp new file mode 100644 index 0000000..0f8f732 Binary files /dev/null and b/test.txt.otp differ diff --git a/test_ascii b/test_ascii new file mode 100644 index 0000000..d1083a9 --- /dev/null +++ b/test_ascii @@ -0,0 +1 @@ +This is a test file for OTP encryption. diff --git a/test_ascii.otp.asc b/test_ascii.otp.asc new file mode 100644 index 0000000..cc03279 --- /dev/null +++ b/test_ascii.otp.asc @@ -0,0 +1,7 @@ +-----BEGIN OTP MESSAGE----- +Version: v0.2.15 +Pad-ChkSum: 0c8e19fde996e683fdbd348d1052eec168ffe6f67a88bb1278d0d02e9341b87b +Pad-Offset: 210 + +mMIm7iVtUO6NbXbskMxtydI/A16UXEQUGTcIya/8Dja6PB3EC0MLdw== +-----END OTP MESSAGE-----