Compare commits

...

8 Commits

5 changed files with 317 additions and 278 deletions

View File

@@ -1,7 +1,7 @@
CC = gcc CC = gcc
CFLAGS = -Wall -Wextra -std=c99 CFLAGS = -Wall -Wextra -std=c99
LIBS = -lssl -lcrypto LIBS =
LIBS_STATIC = -static -lssl -lcrypto -ldl -lpthread LIBS_STATIC = -static
TARGET = otp TARGET = otp
SOURCE = otp.c SOURCE = otp.c
VERSION_SOURCE = src/version.c VERSION_SOURCE = src/version.c

View File

@@ -1,4 +1,4 @@
# OTP Cipher - One Time Pad Implementation r# 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.
@@ -25,10 +25,11 @@ Current version can be viewed with: `./otp --help` or by running the interactive
### Prerequisites ### Prerequisites
- GCC compiler - GCC compiler
- OpenSSL development libraries (`libssl-dev` on Ubuntu/Debian)
- Git (for version tracking) - Git (for version tracking)
- Make - Make
**Note: OpenSSL is no longer required! This implementation is now completely self-contained.**
### Build Commands ### Build Commands
Use the included build script for automatic versioning: Use the included build script for automatic versioning:
@@ -125,10 +126,11 @@ These files are excluded from git (.gitignore) and regenerated on each build.
## Security Features ## Security Features
- Uses `/dev/urandom` for cryptographically secure random number generation - Uses `/dev/urandom` for cryptographically secure random number generation
- Optional keyboard entropy mixing using HKDF (Hash-based Key Derivation Function) - Optional keyboard entropy mixing using simple XOR operations
- SHA-256 pad integrity verification - Custom 256-bit XOR checksum for pad identification (encrypted with pad data)
- Read-only pad files to prevent accidental modification - Read-only pad files to prevent accidental modification
- State tracking to prevent pad reuse - State tracking to prevent pad reuse
- **Zero external crypto dependencies** - completely self-contained implementation
## File Structure ## File Structure
@@ -197,3 +199,4 @@ 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

View File

@@ -50,6 +50,32 @@ increment_version() {
print_status "Current version: $LATEST_TAG" print_status "Current version: $LATEST_TAG"
print_status "New version: $NEW_VERSION" print_status "New version: $NEW_VERSION"
# Stage all changes
if git add . 2>/dev/null; then
print_success "Staged all changes"
else
print_warning "Failed to stage changes (maybe not a git repository)"
fi
# Prompt for commit message
echo ""
print_status "Please enter a meaningful commit message for version $NEW_VERSION:"
echo -n "> "
read -r COMMIT_MESSAGE
# Check if user provided a message
if [[ -z "$COMMIT_MESSAGE" ]]; then
print_warning "No commit message provided. Using default message."
COMMIT_MESSAGE="Version $NEW_VERSION - Automatic version increment"
fi
# Commit changes with user-provided message
if git commit -m "Version $NEW_VERSION - $COMMIT_MESSAGE" 2>/dev/null; then
print_success "Committed changes for version $NEW_VERSION"
else
print_warning "Failed to commit changes (maybe no changes to commit or not a git repository)"
fi
# Create new git tag # Create new git tag
if git tag "$NEW_VERSION" 2>/dev/null; then if git tag "$NEW_VERSION" 2>/dev/null; then
print_success "Created new version tag: $NEW_VERSION" print_success "Created new version tag: $NEW_VERSION"

BIN
otp

Binary file not shown.

516
otp.c
View File

@@ -12,14 +12,29 @@
#include <ctype.h> #include <ctype.h>
#include <termios.h> #include <termios.h>
#include <fcntl.h> #include <fcntl.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/kdf.h>
#include <openssl/hmac.h>
#include "src/version.h" #include "src/version.h"
// Custom base64 character set
static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int base64_decode_table[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
#define MAX_INPUT_SIZE 4096 #define MAX_INPUT_SIZE 4096
#define MAX_LINE_LENGTH 1024 #define MAX_LINE_LENGTH 1024
#define MAX_HASH_LENGTH 65 #define MAX_HASH_LENGTH 65
@@ -42,28 +57,28 @@ int decrypt_text(const char* pad_identifier);
int setup_raw_terminal(struct termios* original_termios); int setup_raw_terminal(struct termios* original_termios);
void restore_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); int collect_keyboard_entropy(unsigned char* entropy_buffer, size_t max_size, size_t* collected);
int hkdf_expand(const unsigned char* prk, size_t prk_len, void simple_entropy_mix(unsigned char* urandom_buffer, size_t buffer_size,
const unsigned char* info, size_t info_len, const unsigned char* entropy_data, size_t entropy_size);
unsigned char* okm, size_t okm_len);
// Directory management // Directory management
int ensure_pads_directory(void); int ensure_pads_directory(void);
void get_pad_path(const char* hash, char* pad_path, char* state_path); void get_pad_path(const char* chksum, char* pad_path, char* state_path);
// Utility functions // Utility functions
uint64_t parse_size_string(const char* size_str); 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);
int list_available_pads(void); int list_available_pads(void);
int show_pad_info(const char* hash); int show_pad_info(const char* chksum);
int get_user_choice(int min, int max); int get_user_choice(int min, int max);
void show_progress(uint64_t current, uint64_t total, time_t start_time); void show_progress(uint64_t current, uint64_t total, time_t start_time);
// File operations // File operations
int read_state_offset(const char* pad_hash, uint64_t* offset); int read_state_offset(const char* pad_chksum, uint64_t* offset);
int write_state_offset(const char* pad_hash, uint64_t offset); int write_state_offset(const char* pad_chksum, uint64_t offset);
int calculate_sha256(const char* filename, char* hash_hex); int calculate_checksum(const char* filename, char* checksum_hex);
char* base64_encode(const unsigned char* input, int length); void xor_checksum_256(const unsigned char* data, size_t len, unsigned char checksum[32]);
unsigned char* base64_decode(const char* input, int* output_length); char* custom_base64_encode(const unsigned char* input, int length);
unsigned char* custom_base64_decode(const char* input, int* output_length);
// Menu functions // Menu functions
void show_main_menu(void); void show_main_menu(void);
@@ -86,37 +101,46 @@ int interactive_mode(void) {
while (1) { while (1) {
show_main_menu(); show_main_menu();
int choice = get_user_choice(1, 6); char input[10];
if (fgets(input, sizeof(input), stdin)) {
char choice = toupper(input[0]);
switch (choice) { switch (choice) {
case 1: case 'G':
handle_generate_menu(); handle_generate_menu();
break; break;
case 2: case 'E':
handle_encrypt_menu(); handle_encrypt_menu();
break; break;
case 3: case 'D':
handle_decrypt_menu(); handle_decrypt_menu();
break; break;
case 4: case 'L':
list_available_pads(); list_available_pads();
break; break;
case 5: { case 'S': {
printf("Enter pad hash (or prefix): "); printf("Enter pad checksum (or prefix): ");
char input[MAX_HASH_LENGTH]; char input[MAX_HASH_LENGTH];
if (fgets(input, sizeof(input), stdin)) { if (fgets(input, sizeof(input), stdin)) {
input[strcspn(input, "\n")] = 0; input[strcspn(input, "\n")] = 0;
char* hash = find_pad_by_prefix(input); char* chksum = find_pad_by_prefix(input);
if (hash) { if (chksum) {
show_pad_info(hash); show_pad_info(chksum);
free(hash); free(chksum);
} }
} }
break; break;
} }
case 6: case 'X':
printf("Goodbye!\n"); printf("Goodbye!\n");
return 0; return 0;
default:
printf("Invalid option. Please select G, E, D, L, S, or X.\n");
continue;
}
} else {
printf("Error reading input. Please try again.\n");
continue;
} }
printf("\n"); printf("\n");
} }
@@ -138,14 +162,14 @@ int command_line_mode(int argc, char* argv[]) {
} }
else if (strcmp(argv[1], "encrypt") == 0) { else if (strcmp(argv[1], "encrypt") == 0) {
if (argc != 3) { if (argc != 3) {
printf("Usage: %s encrypt <pad_hash_or_prefix>\n", argv[0]); printf("Usage: %s encrypt <pad_chksum_or_prefix>\n", argv[0]);
return 1; return 1;
} }
return encrypt_text(argv[2]); return encrypt_text(argv[2]);
} }
else if (strcmp(argv[1], "decrypt") == 0) { else if (strcmp(argv[1], "decrypt") == 0) {
if (argc != 3) { if (argc != 3) {
printf("Usage: %s decrypt <pad_hash_or_prefix>\n", argv[0]); printf("Usage: %s decrypt <pad_chksum_or_prefix>\n", argv[0]);
return 1; return 1;
} }
return decrypt_text(argv[2]); return decrypt_text(argv[2]);
@@ -161,13 +185,13 @@ int command_line_mode(int argc, char* argv[]) {
void show_main_menu(void) { void show_main_menu(void) {
printf("=== Main Menu ===\n"); printf("=== Main Menu ===\n");
printf("1. Generate new pad\n"); printf("\033[4mG\033[0menerate new pad\n");
printf("2. Encrypt message\n"); printf("\033[4mE\033[0mncrypt message\n");
printf("3. Decrypt message\n"); printf("\033[4mD\033[0mecrypt message\n");
printf("4. List available pads\n"); printf("\033[4mL\033[0mist available pads\n");
printf("5. Show pad information\n"); printf("\033[4mS\033[0mhow pad information\n");
printf("6. Exit\n"); printf("E\033[4mx\033[0mit\n");
printf("\nSelect option (1-6): "); printf("\nSelect option: ");
} }
int handle_generate_menu(void) { int handle_generate_menu(void) {
@@ -218,7 +242,7 @@ int handle_encrypt_menu(void) {
return 1; return 1;
} }
printf("\nEnter pad selection (number, hash, or prefix): "); printf("\nEnter pad selection (number, chksum, or prefix): ");
char input[MAX_HASH_LENGTH]; char input[MAX_HASH_LENGTH];
if (!fgets(input, sizeof(input), stdin)) { if (!fgets(input, sizeof(input), stdin)) {
printf("Error: Failed to read input\n"); printf("Error: Failed to read input\n");
@@ -231,7 +255,7 @@ int handle_encrypt_menu(void) {
int handle_decrypt_menu(void) { int handle_decrypt_menu(void) {
printf("\n=== Decrypt Message ===\n"); printf("\n=== Decrypt Message ===\n");
return decrypt_text(NULL); // No pad selection needed - hash comes from message return decrypt_text(NULL); // No pad selection needed - chksum comes from message
} }
uint64_t parse_size_string(const char* size_str) { uint64_t parse_size_string(const char* size_str) {
@@ -292,7 +316,7 @@ char* find_pad_by_prefix(const char* prefix) {
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 hash + ".pad" if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { // 64 char chksum + ".pad"
current++; current++;
if (current == selection) { if (current == selection) {
matches[match_count] = malloc(65); matches[match_count] = malloc(65);
@@ -353,15 +377,15 @@ int list_available_pads(void) {
int count = 0; int count = 0;
printf("Available pads:\n"); printf("Available pads:\n");
printf("%-4s %-20s %-12s %-12s %-8s\n", "No.", "Hash (first 16 chars)", "Size", "Used", "% Used"); printf("%-4s %-20s %-12s %-12s %-8s\n", "No.", "ChkSum (first 16 chars)", "Size", "Used", "% Used");
printf("%-4s %-20s %-12s %-12s %-8s\n", "---", "-------------------", "----------", "----------", "------"); printf("%-4s %-20s %-12s %-12s %-8s\n", "---", "-------------------", "----------", "----------", "------");
while ((entry = readdir(dir)) != NULL) { while ((entry = readdir(dir)) != NULL) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) { if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) {
count++; count++;
char hash[65]; char chksum[65];
strncpy(hash, entry->d_name, 64); strncpy(chksum, entry->d_name, 64);
hash[64] = '\0'; chksum[64] = '\0';
// Get pad file size // Get pad file size
char full_path[300]; // Increased buffer size to accommodate longer paths char full_path[300]; // Increased buffer size to accommodate longer paths
@@ -370,7 +394,7 @@ int list_available_pads(void) {
if (stat(full_path, &st) == 0) { if (stat(full_path, &st) == 0) {
// Get used bytes from state // Get used bytes from state
uint64_t used_bytes; uint64_t used_bytes;
read_state_offset(hash, &used_bytes); read_state_offset(chksum, &used_bytes);
// Format sizes // Format sizes
char size_str[32], used_str[32]; char size_str[32], used_str[32];
@@ -400,7 +424,7 @@ int list_available_pads(void) {
// Calculate percentage // Calculate percentage
double percentage = (double)used_bytes / st.st_size * 100.0; double percentage = (double)used_bytes / st.st_size * 100.0;
printf("%-4d %-20.16s %-12s %-12s %.1f%%\n", count, hash, size_str, used_str, percentage); printf("%-4d %-20.16s %-12s %-12s %.1f%%\n", count, chksum, size_str, used_str, percentage);
} }
} }
} }
@@ -414,24 +438,24 @@ int list_available_pads(void) {
return count; return count;
} }
int show_pad_info(const char* hash) { int show_pad_info(const char* chksum) {
char pad_filename[MAX_HASH_LENGTH + 10]; char pad_filename[MAX_HASH_LENGTH + 10];
char state_filename[MAX_HASH_LENGTH + 10]; char state_filename[MAX_HASH_LENGTH + 10];
snprintf(pad_filename, sizeof(pad_filename), "%s.pad", hash); snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum);
snprintf(state_filename, sizeof(state_filename), "%s.state", hash); snprintf(state_filename, sizeof(state_filename), "%s.state", chksum);
struct stat st; struct stat st;
if (stat(pad_filename, &st) != 0) { if (stat(pad_filename, &st) != 0) {
printf("Pad not found: %s\n", hash); printf("Pad not found: %s\n", chksum);
return 1; return 1;
} }
uint64_t used_bytes; uint64_t used_bytes;
read_state_offset(hash, &used_bytes); read_state_offset(chksum, &used_bytes);
printf("=== Pad Information ===\n"); printf("=== Pad Information ===\n");
printf("Hash: %s\n", hash); printf("ChkSum: %s\n", chksum);
printf("File: %s\n", pad_filename); printf("File: %s\n", pad_filename);
double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0);
@@ -481,7 +505,7 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
char temp_filename[32]; char temp_filename[32];
char pad_filename[MAX_HASH_LENGTH + 10]; char pad_filename[MAX_HASH_LENGTH + 10];
char state_filename[MAX_HASH_LENGTH + 10]; char state_filename[MAX_HASH_LENGTH + 10];
char hash_hex[MAX_HASH_LENGTH]; char chksum_hex[MAX_HASH_LENGTH];
// Create temporary filename // Create temporary filename
snprintf(temp_filename, sizeof(temp_filename), "temp_%ld.pad", time(NULL)); snprintf(temp_filename, sizeof(temp_filename), "temp_%ld.pad", time(NULL));
@@ -544,19 +568,19 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
fclose(urandom); fclose(urandom);
fclose(pad_file); fclose(pad_file);
// Calculate SHA-256 of the pad file // Calculate XOR checksum of the pad file
if (calculate_sha256(temp_filename, hash_hex) != 0) { if (calculate_checksum(temp_filename, chksum_hex) != 0) {
printf("Error: Cannot calculate pad hash\n"); printf("Error: Cannot calculate pad checksum\n");
unlink(temp_filename); unlink(temp_filename);
return 1; return 1;
} }
// Rename file to its hash // Rename file to its chksum
snprintf(pad_filename, sizeof(pad_filename), "%s.pad", hash_hex); snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum_hex);
snprintf(state_filename, sizeof(state_filename), "%s.state", hash_hex); snprintf(state_filename, sizeof(state_filename), "%s.state", chksum_hex);
if (rename(temp_filename, pad_filename) != 0) { if (rename(temp_filename, pad_filename) != 0) {
printf("Error: Cannot rename pad file to hash-based name\n"); printf("Error: Cannot rename pad file to chksum-based name\n");
unlink(temp_filename); unlink(temp_filename);
return 1; return 1;
} }
@@ -567,7 +591,7 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
} }
// Initialize state file with offset 0 // Initialize state file with offset 0
if (write_state_offset(hash_hex, 0) != 0) { if (write_state_offset(chksum_hex, 0) != 0) {
printf("Error: Failed to create state file\n"); printf("Error: Failed to create state file\n");
unlink(pad_filename); unlink(pad_filename);
return 1; return 1;
@@ -575,7 +599,7 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0);
printf("Generated pad: %s (%.2f GB)\n", pad_filename, size_gb); printf("Generated pad: %s (%.2f GB)\n", pad_filename, size_gb);
printf("Pad hash: %s\n", hash_hex); printf("Pad chksum: %s\n", chksum_hex);
printf("State file: %s\n", state_filename); printf("State file: %s\n", state_filename);
printf("Pad file set to read-only\n"); printf("Pad file set to read-only\n");
@@ -591,7 +615,7 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use
char temp_filename[64]; char temp_filename[64];
char pad_path[MAX_HASH_LENGTH + 20]; char pad_path[MAX_HASH_LENGTH + 20];
char state_path[MAX_HASH_LENGTH + 20]; char state_path[MAX_HASH_LENGTH + 20];
char hash_hex[MAX_HASH_LENGTH]; char chksum_hex[MAX_HASH_LENGTH];
// Create temporary filename // Create temporary filename
snprintf(temp_filename, sizeof(temp_filename), "temp_%ld.pad", time(NULL)); snprintf(temp_filename, sizeof(temp_filename), "temp_%ld.pad", time(NULL));
@@ -671,35 +695,12 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use
MAX_ENTROPY_BUFFER - entropy_collected, &chunk_entropy); MAX_ENTROPY_BUFFER - entropy_collected, &chunk_entropy);
entropy_collected += chunk_entropy; entropy_collected += chunk_entropy;
if (entropy_collected > 1024) { // Have enough entropy to mix if (entropy_collected > 512) { // Have enough entropy to mix
// Create HKDF PRK (extract phase) // Copy urandom data to output buffer
unsigned char prk[32];
EVP_MD_CTX* hmac_ctx = EVP_MD_CTX_new();
EVP_PKEY* hmac_key = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL,
entropy_buffer, entropy_collected);
if (hmac_ctx && hmac_key) {
EVP_DigestSignInit(hmac_ctx, NULL, EVP_sha256(), NULL, hmac_key);
EVP_DigestSignUpdate(hmac_ctx, urandom_buffer, chunk_size);
size_t prk_len = sizeof(prk);
EVP_DigestSignFinal(hmac_ctx, prk, &prk_len);
// HKDF Expand phase
const char* info = "OTP-PAD-CHUNK";
if (hkdf_expand(prk, prk_len, (const unsigned char*)info, strlen(info),
output_buffer, chunk_size) == 0) {
// Successfully mixed entropy
} else {
// Fallback to urandom only
memcpy(output_buffer, urandom_buffer, chunk_size); memcpy(output_buffer, urandom_buffer, chunk_size);
}
EVP_PKEY_free(hmac_key); // Simple XOR mixing with keyboard entropy
EVP_MD_CTX_free(hmac_ctx); simple_entropy_mix(output_buffer, chunk_size, entropy_buffer, entropy_collected);
} else {
// Fallback to urandom only
memcpy(output_buffer, urandom_buffer, chunk_size);
}
// Reset entropy buffer for next chunk // Reset entropy buffer for next chunk
entropy_collected = 0; entropy_collected = 0;
@@ -751,15 +752,15 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use
fclose(urandom); fclose(urandom);
fclose(pad_file); fclose(pad_file);
// Calculate SHA-256 of the pad file // Calculate XOR checksum of the pad file
if (calculate_sha256(temp_filename, hash_hex) != 0) { if (calculate_checksum(temp_filename, chksum_hex) != 0) {
printf("Error: Cannot calculate pad hash\n"); printf("Error: Cannot calculate pad checksum\n");
unlink(temp_filename); unlink(temp_filename);
return 1; return 1;
} }
// Get final paths in pads directory // Get final paths in pads directory
get_pad_path(hash_hex, pad_path, state_path); get_pad_path(chksum_hex, pad_path, state_path);
if (rename(temp_filename, pad_path) != 0) { if (rename(temp_filename, pad_path) != 0) {
printf("Error: Cannot move pad file to pads directory\n"); printf("Error: Cannot move pad file to pads directory\n");
@@ -772,11 +773,11 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use
printf("Warning: Cannot set pad file to read-only\n"); printf("Warning: Cannot set pad file to read-only\n");
} }
// Initialize state file with offset 0 // Initialize state file with offset 32 (first 32 bytes used for checksum encryption)
FILE* state_file = fopen(state_path, "wb"); FILE* state_file = fopen(state_path, "wb");
if (state_file) { if (state_file) {
uint64_t zero = 0; uint64_t reserved_bytes = 32;
fwrite(&zero, sizeof(uint64_t), 1, state_file); fwrite(&reserved_bytes, sizeof(uint64_t), 1, state_file);
fclose(state_file); fclose(state_file);
} else { } else {
printf("Error: Failed to create state file\n"); printf("Error: Failed to create state file\n");
@@ -786,7 +787,7 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use
double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0);
printf("Generated pad: %s (%.2f GB)\n", pad_path, size_gb); printf("Generated pad: %s (%.2f GB)\n", pad_path, size_gb);
printf("Pad hash: %s\n", hash_hex); printf("Pad checksum: %s\n", chksum_hex);
printf("State file: %s\n", state_path); printf("State file: %s\n", state_path);
if (use_keyboard_entropy) { if (use_keyboard_entropy) {
printf("Enhanced with keyboard entropy!\n"); printf("Enhanced with keyboard entropy!\n");
@@ -797,37 +798,46 @@ int generate_pad_with_entropy(uint64_t size_bytes, int display_progress, int use
} }
int encrypt_text(const char* pad_identifier) { int encrypt_text(const char* pad_identifier) {
char* pad_hash = find_pad_by_prefix(pad_identifier); char* pad_chksum = find_pad_by_prefix(pad_identifier);
if (!pad_hash) { if (!pad_chksum) {
return 1; return 1;
} }
char input_text[MAX_INPUT_SIZE]; char input_text[MAX_INPUT_SIZE];
char hash_hex[MAX_HASH_LENGTH]; char chksum_hex[MAX_HASH_LENGTH];
uint64_t current_offset; uint64_t current_offset;
char pad_path[MAX_HASH_LENGTH + 20]; char pad_path[MAX_HASH_LENGTH + 20];
char state_path[MAX_HASH_LENGTH + 20]; char state_path[MAX_HASH_LENGTH + 20];
get_pad_path(pad_hash, pad_path, state_path); get_pad_path(pad_chksum, pad_path, state_path);
// Check if pad file exists // Check if pad file exists
if (access(pad_path, R_OK) != 0) { if (access(pad_path, R_OK) != 0) {
printf("Error: Pad file %s not found\n", pad_path); printf("Error: Pad file %s not found\n", pad_path);
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
// Read current offset // Read current offset
if (read_state_offset(pad_hash, &current_offset) != 0) { if (read_state_offset(pad_chksum, &current_offset) != 0) {
printf("Error: Cannot read state file\n"); printf("Error: Cannot read state file\n");
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
// Calculate SHA-256 of pad file // Ensure we never encrypt before offset 32 (reserved for checksum encryption)
if (calculate_sha256(pad_path, hash_hex) != 0) { if (current_offset < 32) {
printf("Error: Cannot calculate pad hash\n"); printf("Warning: State offset below reserved area, adjusting to 32\n");
free(pad_hash); 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; return 1;
} }
@@ -837,7 +847,7 @@ int encrypt_text(const char* pad_identifier) {
if (fgets(input_text, sizeof(input_text), stdin) == NULL) { if (fgets(input_text, sizeof(input_text), stdin) == NULL) {
printf("Error: Failed to read input\n"); printf("Error: Failed to read input\n");
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
@@ -850,7 +860,7 @@ int encrypt_text(const char* pad_identifier) {
if (input_len == 0) { if (input_len == 0) {
printf("Error: No input provided\n"); printf("Error: No input provided\n");
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
@@ -858,7 +868,7 @@ int encrypt_text(const char* pad_identifier) {
struct stat pad_stat; struct stat pad_stat;
if (stat(pad_path, &pad_stat) != 0) { if (stat(pad_path, &pad_stat) != 0) {
printf("Error: Cannot get pad file size\n"); printf("Error: Cannot get pad file size\n");
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
@@ -866,7 +876,7 @@ int encrypt_text(const char* pad_identifier) {
printf("Error: Not enough pad space remaining\n"); printf("Error: Not enough pad space remaining\n");
printf("Need: %lu bytes, Available: %lu bytes\n", printf("Need: %lu bytes, Available: %lu bytes\n",
input_len, (uint64_t)pad_stat.st_size - current_offset); input_len, (uint64_t)pad_stat.st_size - current_offset);
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
@@ -874,14 +884,14 @@ int encrypt_text(const char* pad_identifier) {
FILE* pad_file = fopen(pad_path, "rb"); FILE* pad_file = fopen(pad_path, "rb");
if (!pad_file) { if (!pad_file) {
printf("Error: Cannot open pad file\n"); printf("Error: Cannot open pad file\n");
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
if (fseek(pad_file, current_offset, SEEK_SET) != 0) { if (fseek(pad_file, current_offset, SEEK_SET) != 0) {
printf("Error: Cannot seek to offset in pad file\n"); printf("Error: Cannot seek to offset in pad file\n");
fclose(pad_file); fclose(pad_file);
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
@@ -890,7 +900,7 @@ int encrypt_text(const char* pad_identifier) {
printf("Error: Cannot read pad data\n"); printf("Error: Cannot read pad data\n");
free(pad_data); free(pad_data);
fclose(pad_file); fclose(pad_file);
free(pad_hash); free(pad_chksum);
return 1; return 1;
} }
fclose(pad_file); fclose(pad_file);
@@ -902,17 +912,17 @@ int encrypt_text(const char* pad_identifier) {
} }
// Encode as base64 // Encode as base64
char* base64_cipher = base64_encode(ciphertext, input_len); char* base64_cipher = custom_base64_encode(ciphertext, input_len);
// Update state offset // Update state offset
if (write_state_offset(pad_hash, current_offset + input_len) != 0) { if (write_state_offset(pad_chksum, current_offset + input_len) != 0) {
printf("Warning: Failed to update state file\n"); printf("Warning: Failed to update state file\n");
} }
// Output in ASCII armor format // Output in ASCII armor format
printf("\n-----BEGIN OTP MESSAGE-----\n"); printf("\n\n-----BEGIN OTP MESSAGE-----\n");
printf("Version: %s\n", get_version()); printf("Version: %s\n", get_version());
printf("Pad-Hash: %s\n", hash_hex); printf("Pad-ChkSum: %s\n", chksum_hex);
printf("Pad-Offset: %lu\n", current_offset); printf("Pad-Offset: %lu\n", current_offset);
printf("\n"); printf("\n");
@@ -922,24 +932,24 @@ int encrypt_text(const char* pad_identifier) {
printf("%.64s\n", base64_cipher + i); printf("%.64s\n", base64_cipher + i);
} }
printf("-----END OTP MESSAGE-----\n\n"); printf("-----END OTP MESSAGE-----\n\n\n");
// Cleanup // Cleanup
free(pad_data); free(pad_data);
free(ciphertext); free(ciphertext);
free(base64_cipher); free(base64_cipher);
free(pad_hash); free(pad_chksum);
return 0; return 0;
} }
int decrypt_text(const char* pad_identifier) { int decrypt_text(const char* pad_identifier) {
// For command line mode, pad_identifier is ignored - we'll get the hash from the message // For command line mode, pad_identifier is ignored - we'll get the chksum from the message
(void)pad_identifier; // Suppress unused parameter warning (void)pad_identifier; // Suppress unused parameter warning
char line[MAX_LINE_LENGTH]; char line[MAX_LINE_LENGTH];
char stored_hash[MAX_HASH_LENGTH]; char stored_chksum[MAX_HASH_LENGTH];
char current_hash[MAX_HASH_LENGTH]; char current_chksum[MAX_HASH_LENGTH];
uint64_t pad_offset; uint64_t pad_offset;
char base64_data[MAX_INPUT_SIZE * 2] = {0}; char base64_data[MAX_INPUT_SIZE * 2] = {0};
int in_data_section = 0; int in_data_section = 0;
@@ -962,9 +972,9 @@ int decrypt_text(const char* pad_identifier) {
if (!found_begin) continue; if (!found_begin) continue;
if (strncmp(line, "Pad-Hash: ", 10) == 0) { if (strncmp(line, "Pad-ChkSum: ", 12) == 0) {
strncpy(stored_hash, line + 10, 64); strncpy(stored_chksum, line + 12, 64);
stored_hash[64] = '\0'; stored_chksum[64] = '\0';
} }
else if (strncmp(line, "Pad-Offset: ", 12) == 0) { else if (strncmp(line, "Pad-Offset: ", 12) == 0) {
pad_offset = strtoull(line + 12, NULL, 10); pad_offset = strtoull(line + 12, NULL, 10);
@@ -982,29 +992,29 @@ int decrypt_text(const char* pad_identifier) {
return 1; return 1;
} }
// Now we have the pad hash from the message, construct filename // Now we have the pad chksum from the message, construct filename
char pad_path[MAX_HASH_LENGTH + 20]; char pad_path[MAX_HASH_LENGTH + 20];
char state_path[MAX_HASH_LENGTH + 20]; char state_path[MAX_HASH_LENGTH + 20];
get_pad_path(stored_hash, pad_path, state_path); get_pad_path(stored_chksum, pad_path, state_path);
// Check if we have this pad // Check if we have this pad
if (access(pad_path, R_OK) != 0) { if (access(pad_path, R_OK) != 0) {
printf("Error: Required pad not found: %s\n", stored_hash); printf("Error: Required pad not found: %s\n", stored_chksum);
printf("Available pads:\n"); printf("Available pads:\n");
list_available_pads(); list_available_pads();
return 1; return 1;
} }
// Verify pad integrity // Verify pad integrity
if (calculate_sha256(pad_path, current_hash) != 0) { if (calculate_checksum(pad_path, current_chksum) != 0) {
printf("Error: Cannot calculate current pad hash\n"); printf("Error: Cannot calculate current pad checksum\n");
return 1; return 1;
} }
if (strcmp(stored_hash, current_hash) != 0) { if (strcmp(stored_chksum, current_chksum) != 0) {
printf("Warning: Pad integrity check failed!\n"); printf("Warning: Pad integrity check failed!\n");
printf("Expected: %s\n", stored_hash); printf("Expected: %s\n", stored_chksum);
printf("Current: %s\n", current_hash); printf("Current: %s\n", current_chksum);
printf("Continue anyway? (y/N): "); printf("Continue anyway? (y/N): ");
fflush(stdout); fflush(stdout);
@@ -1020,7 +1030,7 @@ int decrypt_text(const char* pad_identifier) {
// Decode base64 // Decode base64
int ciphertext_len; int ciphertext_len;
unsigned char* ciphertext = base64_decode(base64_data, &ciphertext_len); unsigned char* ciphertext = custom_base64_decode(base64_data, &ciphertext_len);
if (!ciphertext) { if (!ciphertext) {
printf("Error: Invalid base64 data\n"); printf("Error: Invalid base64 data\n");
return 1; return 1;
@@ -1068,9 +1078,9 @@ int decrypt_text(const char* pad_identifier) {
return 0; return 0;
} }
int read_state_offset(const char* pad_hash, 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_hash); snprintf(state_filename, sizeof(state_filename), "%s/%s.state", PADS_DIR, pad_chksum);
FILE* state_file = fopen(state_filename, "rb"); FILE* state_file = fopen(state_filename, "rb");
if (!state_file) { if (!state_file) {
@@ -1088,9 +1098,9 @@ int read_state_offset(const char* pad_hash, uint64_t* offset) {
return 0; return 0;
} }
int write_state_offset(const char* pad_hash, uint64_t offset) { int write_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_hash); snprintf(state_filename, sizeof(state_filename), "%s/%s.state", PADS_DIR, pad_chksum);
FILE* state_file = fopen(state_filename, "wb"); FILE* state_file = fopen(state_filename, "wb");
if (!state_file) { if (!state_file) {
@@ -1106,52 +1116,53 @@ int write_state_offset(const char* pad_hash, uint64_t offset) {
return 0; return 0;
} }
int calculate_sha256(const char* filename, char* hash_hex) { int calculate_checksum(const char* filename, char* checksum_hex) {
FILE* file = fopen(filename, "rb"); FILE* file = fopen(filename, "rb");
if (!file) { if (!file) {
return 1; return 1;
} }
EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); unsigned char checksum[32];
if (!mdctx) {
fclose(file);
return 1;
}
if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1) {
EVP_MD_CTX_free(mdctx);
fclose(file);
return 1;
}
unsigned char buffer[64 * 1024]; // 64KB buffer for large files unsigned char buffer[64 * 1024]; // 64KB buffer for large files
size_t bytes_read; size_t bytes_read;
// Initialize checksum
memset(checksum, 0, 32);
size_t total_bytes = 0;
// Calculate XOR checksum of entire file
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
if (EVP_DigestUpdate(mdctx, buffer, bytes_read) != 1) { // Process this chunk with XOR checksum
EVP_MD_CTX_free(mdctx); for (size_t i = 0; i < bytes_read; i++) {
unsigned char bucket = (total_bytes + i) % 32;
checksum[bucket] ^= buffer[i] ^ (((total_bytes + i) >> 8) & 0xFF) ^
(((total_bytes + i) >> 16) & 0xFF) ^ (((total_bytes + i) >> 24) & 0xFF);
}
total_bytes += bytes_read;
}
fclose(file);
// Now encrypt the checksum with the first 32 bytes of the pad
fseek(file = fopen(filename, "rb"), 0, SEEK_SET);
unsigned char pad_key[32];
if (fread(pad_key, 1, 32, file) != 32) {
fclose(file); fclose(file);
return 1; return 1;
} }
}
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int hash_len;
if (EVP_DigestFinal_ex(mdctx, hash, &hash_len) != 1) {
EVP_MD_CTX_free(mdctx);
fclose(file);
return 1;
}
EVP_MD_CTX_free(mdctx);
fclose(file); fclose(file);
// Convert to hex string // XOR encrypt the checksum with pad data to create unique identifier
for (unsigned int i = 0; i < hash_len; i++) { unsigned char encrypted_checksum[32];
sprintf(hash_hex + (i * 2), "%02x", hash[i]); for (int i = 0; i < 32; i++) {
encrypted_checksum[i] = checksum[i] ^ pad_key[i];
} }
hash_hex[hash_len * 2] = '\0';
// Convert to hex string (64 characters)
for (int i = 0; i < 32; i++) {
sprintf(checksum_hex + (i * 2), "%02x", encrypted_checksum[i]);
}
checksum_hex[64] = '\0';
return 0; return 0;
} }
@@ -1226,49 +1237,6 @@ int collect_keyboard_entropy(unsigned char* entropy_buffer, size_t max_size, siz
return 0; return 0;
} }
int hkdf_expand(const unsigned char* prk, size_t prk_len,
const unsigned char* info, size_t info_len,
unsigned char* okm, size_t okm_len) {
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
if (!ctx) return 1;
unsigned char t[32]; // SHA-256 output size
unsigned char counter = 1;
size_t t_len = 32;
size_t pos = 0;
while (pos < okm_len) {
if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1) {
EVP_MD_CTX_free(ctx);
return 1;
}
if (pos > 0) {
EVP_DigestUpdate(ctx, t, t_len);
}
EVP_DigestUpdate(ctx, prk, prk_len);
if (info && info_len > 0) {
EVP_DigestUpdate(ctx, info, info_len);
}
EVP_DigestUpdate(ctx, &counter, 1);
unsigned int hash_len;
if (EVP_DigestFinal_ex(ctx, t, &hash_len) != 1) {
EVP_MD_CTX_free(ctx);
return 1;
}
size_t copy_len = (okm_len - pos < hash_len) ? okm_len - pos : hash_len;
memcpy(okm + pos, t, copy_len);
pos += copy_len;
counter++;
}
EVP_MD_CTX_free(ctx);
return 0;
}
// Directory management functions // Directory management functions
int ensure_pads_directory(void) { int ensure_pads_directory(void) {
@@ -1281,54 +1249,96 @@ int ensure_pads_directory(void) {
return 0; return 0;
} }
void get_pad_path(const char* hash, char* pad_path, char* state_path) { void get_pad_path(const char* chksum, char* pad_path, char* state_path) {
snprintf(pad_path, MAX_HASH_LENGTH + 20, "%s/%s.pad", PADS_DIR, hash); snprintf(pad_path, MAX_HASH_LENGTH + 20, "%s/%s.pad", PADS_DIR, chksum);
snprintf(state_path, MAX_HASH_LENGTH + 20, "%s/%s.state", PADS_DIR, hash); snprintf(state_path, MAX_HASH_LENGTH + 20, "%s/%s.state", PADS_DIR, chksum);
} }
char* base64_encode(const unsigned char* input, int length) {
BIO *bio, *b64;
BUF_MEM *buffer_ptr;
b64 = BIO_new(BIO_f_base64()); // Custom XOR checksum function
bio = BIO_new(BIO_s_mem()); void xor_checksum_256(const unsigned char* data, size_t len, unsigned char checksum[32]) {
bio = BIO_push(b64, bio); memset(checksum, 0, 32);
for (size_t i = 0; i < len; i++) {
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); unsigned char bucket = i % 32;
BIO_write(bio, input, length); checksum[bucket] ^= data[i] ^ ((i >> 8) & 0xFF) ^ ((i >> 16) & 0xFF) ^ ((i >> 24) & 0xFF);
BIO_flush(bio); }
BIO_get_mem_ptr(bio, &buffer_ptr);
char* result = malloc(buffer_ptr->length + 1);
memcpy(result, buffer_ptr->data, buffer_ptr->length);
result[buffer_ptr->length] = '\0';
BIO_free_all(bio);
return result;
} }
unsigned char* base64_decode(const char* input, int* output_length) { // Custom base64 encode function
BIO *bio, *b64; char* custom_base64_encode(const unsigned char* input, int length) {
int decode_len = strlen(input); int output_length = 4 * ((length + 2) / 3);
char* encoded = malloc(output_length + 1);
if (!encoded) return NULL;
unsigned char* buffer = malloc(decode_len); int i, j;
for (i = 0, j = 0; i < length;) {
uint32_t octet_a = i < length ? input[i++] : 0;
uint32_t octet_b = i < length ? input[i++] : 0;
uint32_t octet_c = i < length ? input[i++] : 0;
bio = BIO_new_mem_buf(input, -1); uint32_t triple = (octet_a << 16) + (octet_b << 8) + octet_c;
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); encoded[j++] = base64_chars[(triple >> 18) & 63];
*output_length = BIO_read(bio, buffer, decode_len); encoded[j++] = base64_chars[(triple >> 12) & 63];
encoded[j++] = base64_chars[(triple >> 6) & 63];
encoded[j++] = base64_chars[triple & 63];
}
BIO_free_all(bio); // Add padding
for (int pad = 0; pad < (3 - length % 3) % 3; pad++) {
encoded[output_length - 1 - pad] = '=';
}
if (*output_length <= 0) { encoded[output_length] = '\0';
free(buffer); return encoded;
}
// Custom base64 decode function
unsigned char* custom_base64_decode(const char* input, int* output_length) {
int input_length = strlen(input);
if (input_length % 4 != 0) return NULL;
*output_length = input_length / 4 * 3;
if (input[input_length - 1] == '=') (*output_length)--;
if (input[input_length - 2] == '=') (*output_length)--;
unsigned char* decoded = malloc(*output_length);
if (!decoded) return NULL;
int i, j;
for (i = 0, j = 0; i < input_length;) {
int sextet_a = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]];
int sextet_b = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]];
int sextet_c = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]];
int sextet_d = input[i] == '=' ? 0 & i++ : base64_decode_table[(unsigned char)input[i++]];
if (sextet_a == -1 || sextet_b == -1 || sextet_c == -1 || sextet_d == -1) {
free(decoded);
return NULL; return NULL;
} }
return buffer; uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d;
if (j < *output_length) decoded[j++] = (triple >> 16) & 255;
if (j < *output_length) decoded[j++] = (triple >> 8) & 255;
if (j < *output_length) decoded[j++] = triple & 255;
}
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;
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;
}
} }
void print_usage(const char* program_name) { void print_usage(const char* program_name) {
@@ -1337,9 +1347,9 @@ void print_usage(const char* program_name) {
printf("Usage:\n"); printf("Usage:\n");
printf(" %s - Interactive mode\n", program_name); printf(" %s - Interactive mode\n", program_name);
printf(" %s generate <size> - Generate new pad\n", program_name); printf(" %s generate <size> - Generate new pad\n", program_name);
printf(" %s encrypt <pad_hash_prefix> - Encrypt text\n", program_name); printf(" %s encrypt <pad_checksum_prefix> - Encrypt text\n", program_name);
printf(" %s decrypt <pad_hash_prefix> - Decrypt message\n", program_name); printf(" %s decrypt <pad_checksum_prefix> - Decrypt message\n", program_name);
printf(" %s list - List available pads\n", program_name); printf(" %s list - List available 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 hash, prefix, or number from list\n"); printf("Pad selection: Full chksum, prefix, or number from list\n");
} }