Compare commits

..

3 Commits

10 changed files with 838 additions and 73 deletions

View File

@@ -0,0 +1,4 @@
When building, use build.sh, not make.
Use it as follows: build.sh -m "useful comment on changes being made"

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
otp otp
pads/ pads/
Gemini.md Gemini.md
TropicOfCancer-HenryMiller.txt
# Auto-generated version files # Auto-generated version files
src/version.h src/version.h

142
README.md
View File

@@ -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. A secure one-time pad (OTP) cipher implementation in C with automatic versioning system.
## Features ## Features
- **Perfect Security**: Implements true one-time pad encryption with information-theoretic security - **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 - **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 - **Automatic Versioning**: Built-in semantic versioning with automatic patch increment
- **Multiple Build Options**: Standard and static linking builds - **Multiple Build Options**: Standard and static linking builds
- **Cross-Platform**: Works on Linux and other UNIX-like systems - **Cross-Platform**: Works on Linux and other UNIX-like systems
@@ -148,45 +151,124 @@ otp/
└── VERSION # Plain text version (generated) └── 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: <decimal-offset-value>
<base64-encoded-encrypted-data>
-----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 ### Build and Version Tracking
```bash ```bash
$ ./build.sh build $ ./build.sh build
[INFO] Incrementing version... [INFO] Incrementing version...
[INFO] Current version: v0.1.4 [INFO] Current version: v0.2.14
[INFO] New version: v0.1.5 [INFO] New version: v0.2.15
[SUCCESS] Created new version tag: v0.1.5 [SUCCESS] Created new version tag: v0.2.15
[SUCCESS] Build completed successfully [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 --help
OTP Cipher - One Time Pad Implementation v0.1.5 OTP Cipher - One Time Pad Implementation v0.2.15
Built on 2025-08-10 at 08:17:47 from commit 9edfa5f on branch master Built on 2025-08-10 at 14:07:58 from commit ae0afcf on branch master
Usage:
./otp - Interactive mode
...
``` ```
### Version History ### Advanced Features
```bash ```bash
$ git tag --list # Generate pad with keyboard entropy
v0.1.0 ./otp generate 5GB
v0.1.1 # Follow prompts for keyboard entropy collection
v0.1.2
v0.1.3 # Check pad usage
v0.1.4 ./otp -l
v0.1.5 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 ## License
@@ -199,5 +281,3 @@ When contributing:
1. The version will automatically increment on builds 1. The version will automatically increment on builds
2. For major features, consider manually creating minor version tags 2. For major features, consider manually creating minor version tags
3. Generated version files (`src/version.*`, `VERSION`) should not be committed 3. Generated version files (`src/version.*`, `VERSION`) should not be committed
# Test change
# Testing -m flag

BIN
debug Executable file

Binary file not shown.

1
debug.c Normal file
View File

@@ -0,0 +1 @@
int main() { printf("Testing direct filename: %d\n", strncmp("97d9d82b5414a9439102f3811fb90ab1d6368a00d33229a18b306476f9d04f82.pad", "97", 2)); return 0; }

754
otp.c
View File

@@ -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 generate_pad_with_entropy(uint64_t size_bytes, int show_progress, int use_keyboard_entropy);
int encrypt_text(const char* pad_identifier, const char* input_text); int encrypt_text(const char* pad_identifier, const char* input_text);
int decrypt_text(const char* pad_identifier, const char* encrypted_message); int decrypt_text(const char* pad_identifier, const char* encrypted_message);
int 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 // Keyboard entropy functions
int setup_raw_terminal(struct termios* original_termios); int setup_raw_terminal(struct termios* original_termios);
@@ -147,9 +151,9 @@ int interactive_mode(void) {
} }
int command_line_mode(int argc, char* argv[]) { 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) { if (argc != 3) {
printf("Usage: %s generate <size>\n", argv[0]); printf("Usage: %s generate|-g <size>\n", argv[0]);
printf("Size examples: 1024, 1GB, 5TB, 512MB\n"); printf("Size examples: 1024, 1GB, 5TB, 512MB\n");
return 1; 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 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) { if (argc < 3 || argc > 4) {
printf("Usage: %s encrypt <pad_chksum_or_prefix> [text_to_encrypt]\n", argv[0]); printf("Usage: %s encrypt|-e <pad_chksum_or_prefix> [text_to_encrypt]\n", argv[0]);
return 1; return 1;
} }
// Pass text if provided, otherwise NULL for interactive mode // Pass text if provided, otherwise NULL for interactive mode
const char* text = (argc == 4) ? argv[3] : NULL; const char* text = (argc == 4) ? argv[3] : NULL;
return encrypt_text(argv[2], text); return encrypt_text(argv[2], text);
} }
else if (strcmp(argv[1], "decrypt") == 0) { else if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) {
if (argc < 3 || argc > 4) { if (argc == 2) {
printf("Usage: %s decrypt <pad_chksum_or_prefix> [encrypted_message]\n", argv[0]); // 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 <input_file> [-o <output_file>]\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 <input_file> -o <output_file>
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; 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 <input_file> <pad_prefix> [-a] [-o <output_file>]
if (argc < 4) {
printf("Usage: %s -f <input_file> <pad_prefix> [-a] [-o <output_file>]\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(); return list_available_pads();
} }
else { else {
@@ -306,41 +365,53 @@ uint64_t parse_size_string(const char* size_str) {
char* find_pad_by_prefix(const char* prefix) { char* find_pad_by_prefix(const char* prefix) {
DIR* dir = opendir(PADS_DIR); DIR* dir = opendir(PADS_DIR);
if (!dir) return NULL; if (!dir) {
printf("Error: Cannot open pads directory\n");
return NULL;
}
struct dirent* entry; struct dirent* entry;
char* matches[100]; // Store up to 100 matches char* matches[100]; // Store up to 100 matches
int match_count = 0; int match_count = 0;
// Check if it's a number (for interactive menu selection) // Check if it's a number (for interactive menu selection)
// Only treat as number if it's a single digit (1-9) to avoid conflicts with hex prefixes
char* endptr; char* endptr;
int selection = strtol(prefix, &endptr, 10); int selection = strtol(prefix, &endptr, 10);
if (*endptr == '\0' && selection > 0) { if (*endptr == '\0' && selection > 0 && selection <= 9 && strlen(prefix) == 1) {
// It's a number, find the nth pad // It's a number, find the nth pad
int current = 0; int current = 0;
rewinddir(dir); rewinddir(dir);
while ((entry = readdir(dir)) != NULL && match_count == 0) { while ((entry = readdir(dir)) != NULL && match_count == 0) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { // 64 char chksum + ".pad" // Skip . and .. entries, and only process .pad files
current++; if (entry->d_name[0] == '.') continue;
if (current == selection) { if (!strstr(entry->d_name, ".pad")) continue;
matches[match_count] = malloc(65); if (strlen(entry->d_name) != 68) continue; // 64 char chksum + ".pad"
strncpy(matches[match_count], entry->d_name, 64);
matches[match_count][64] = '\0'; current++;
match_count = 1; if (current == selection) {
} matches[match_count] = malloc(65);
strncpy(matches[match_count], entry->d_name, 64);
matches[match_count][64] = '\0';
match_count = 1;
break;
} }
} }
} else { } else {
// Find pads that start with the prefix // Find pads that start with the prefix
size_t prefix_len = strlen(prefix); size_t prefix_len = strlen(prefix);
while ((entry = readdir(dir)) != NULL && match_count < 100) { while ((entry = readdir(dir)) != NULL && match_count < 100) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { // Skip . and .. entries, and only process .pad files
if (strncmp(entry->d_name, prefix, prefix_len) == 0) { if (entry->d_name[0] == '.') continue;
matches[match_count] = malloc(65); if (!strstr(entry->d_name, ".pad")) continue;
strncpy(matches[match_count], entry->d_name, 64); if (strlen(entry->d_name) != 68) continue; // 64 char chksum + ".pad"
matches[match_count][64] = '\0';
match_count++; // Compare prefix with the filename (checksum part)
} if (strncmp(entry->d_name, prefix, prefix_len) == 0) {
matches[match_count] = malloc(65);
strncpy(matches[match_count], entry->d_name, 64);
matches[match_count][64] = '\0';
match_count++;
} }
} }
} }
@@ -359,14 +430,35 @@ char* find_pad_by_prefix(const char* prefix) {
printf("Multiple matches found for '%s':\n", prefix); printf("Multiple matches found for '%s':\n", prefix);
for (int i = 0; i < match_count; i++) { for (int i = 0; i < match_count; i++) {
printf("%d. %.16s...\n", i + 1, matches[i]); printf("%d. %.16s...\n", i + 1, matches[i]);
if (i > 0) free(matches[i]);
} }
printf("Please be more specific.\n"); printf("Please be more specific or enter a number (1-%d): ", match_count);
char* result = matches[0]; fflush(stdout);
for (int i = 1; i < match_count; i++) {
char choice_input[64];
if (fgets(choice_input, sizeof(choice_input), stdin)) {
choice_input[strcspn(choice_input, "\n")] = 0;
// Check if it's a number
char* endptr;
int choice = strtol(choice_input, &endptr, 10);
if (*endptr == '\0' && choice >= 1 && choice <= match_count) {
// Valid choice, return selected pad
char* result = matches[choice - 1];
// Free the others
for (int i = 0; i < match_count; i++) {
if (i != choice - 1) {
free(matches[i]);
}
}
return result;
}
}
// Invalid choice or no input, free all and return NULL
for (int i = 0; i < match_count; i++) {
free(matches[i]); free(matches[i]);
} }
return result; return NULL;
} }
} }
@@ -1128,6 +1220,573 @@ int decrypt_text(const char* pad_identifier, const char* encrypted_message) {
return 0; 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, &current_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(&current_offset, sizeof(uint64_t), 1, 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;
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(&file_mode, sizeof(uint32_t), 1, input_fp) != 1 ||
fread(&file_size, sizeof(uint64_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;
}
// 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("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();
fclose(input_fp);
return 1;
}
// Determine output filename
char default_output[512];
if (output_file == NULL) {
snprintf(default_output, sizeof(default_output), "decrypted.bin");
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(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(encrypted_data);
return 1;
}
if (fseek(pad_file, pad_offset, SEEK_SET) != 0) {
printf("Error: Cannot seek to offset in pad file\n");
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(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(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(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(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) { int read_state_offset(const char* pad_chksum, uint64_t* offset) {
char state_filename[MAX_HASH_LENGTH + 20]; char state_filename[MAX_HASH_LENGTH + 20];
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", PADS_DIR, pad_chksum); snprintf(state_filename, sizeof(state_filename), "%s/%s.state", PADS_DIR, pad_chksum);
@@ -1395,16 +2054,27 @@ void print_usage(const char* program_name) {
printf("OTP Cipher - One Time Pad Implementation %s\n", get_version()); printf("OTP Cipher - One Time Pad Implementation %s\n", get_version());
printf("%s\n", get_build_info()); printf("%s\n", get_build_info());
printf("Usage:\n"); printf("Usage:\n");
printf(" %s - Interactive mode\n", program_name); printf(" %s - Interactive mode\n", program_name);
printf(" %s generate <size> - Generate new pad\n", program_name); printf(" %s generate|-g <size> - Generate new pad\n", program_name);
printf(" %s encrypt <pad_checksum_prefix> [text] - Encrypt text\n", program_name); printf(" %s encrypt|-e <pad_checksum_prefix> [text] - Encrypt text\n", program_name);
printf(" %s decrypt <pad_checksum_prefix> [message] - Decrypt message\n", program_name); printf(" %s decrypt|-d [encrypted_message] - Decrypt message\n", program_name);
printf(" %s list - List available pads\n", program_name); printf(" %s -f <file> <pad_prefix> [-a] [-o <out>] - Encrypt file\n", program_name);
printf(" %s list|-l - List available pads\n", program_name);
printf("\nFile Operations:\n");
printf(" -f <file> <pad> - Encrypt file (binary .otp format)\n");
printf(" -f <file> <pad> -a - Encrypt file (ASCII .otp.asc format)\n");
printf(" -o <output> - Specify output filename\n");
printf("\nShort flags:\n");
printf(" -g generate -e encrypt -d decrypt -l list -f file\n");
printf("\nExamples:\n"); printf("\nExamples:\n");
printf(" %s encrypt 1a2b3c \"Hello world\" - Encrypt inline text\n", program_name); printf(" %s -e 1a2b3c \"Hello world\" - Encrypt inline text\n", program_name);
printf(" %s encrypt 1a2b3c - Encrypt interactively\n", program_name); printf(" %s -f document.pdf 1a2b - Encrypt file (binary)\n", program_name);
printf(" %s decrypt 1a2b3c \"-----BEGIN OTP...\" - Decrypt inline message\n", program_name); printf(" %s -f document.pdf 1a2b -a - Encrypt file (ASCII)\n", program_name);
printf(" %s decrypt 1a2b3c - Decrypt interactively\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("\nSize examples: 1GB, 5TB, 512MB, 2048 (bytes)\n");
printf("Pad selection: Full chksum, prefix, or number from list\n"); printf("Pad selection: Full chksum, prefix, or number from list\n");
} }

1
test.txt Normal file
View File

@@ -0,0 +1 @@
This is a test file for OTP encryption.

BIN
test.txt.otp Normal file

Binary file not shown.

1
test_ascii Normal file
View File

@@ -0,0 +1 @@
This is a test file for OTP encryption.

7
test_ascii.otp.asc Normal file
View File

@@ -0,0 +1,7 @@
-----BEGIN OTP MESSAGE-----
Version: v0.2.15
Pad-ChkSum: 0c8e19fde996e683fdbd348d1052eec168ffe6f67a88bb1278d0d02e9341b87b
Pad-Offset: 210
mMIm7iVtUO6NbXbskMxtydI/A16UXEQUGTcIya/8Dja6PB3EC0MLdw==
-----END OTP MESSAGE-----