Compare commits

..

7 Commits

3 changed files with 263 additions and 27 deletions

View File

@@ -1,6 +1,47 @@
# 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.
## Introduction
A secure one-time pad (OTP) cipher implementation in C.
## Why One-Time Pads
Nostr and much of the web runs on public key cryptography. Public key cryptography is great, but it is vulnerable. Cryptographers know this, and they know what it takes to attack it, so what they do is just make the keys large enough such that the system is resistant to attack given computers as they are today.
There is one type of cryptography, however, that is invulnerable to any type of attack in our universe, and that is known as a one-time pad.
One-time pads rely directly on the laws of physics and what it means for a number to be truly random.
If you take your secret message and mix it with truly random numbers, and don't use those random numbers again, then that message is unbreakable by any computer, no matter how powerful, quantum or not, forever.
In fact, one-time pads are so powerful that if you have data encrypted by a one-time pad located in a distant galaxy, and that data is not kept anywhere else, then by destroying the pad used for encryption in your galaxy, the data is wiped from the universe and can never be recovered.
## Advantages and Limitations
### Limitations
1. The pad must be shared between the parties wanting to use it.
2. The pad must be as long or longer than what you want to encrypt, and it can't be used a second time.
### Modern Advantages
While in the past, pad length might have been a problem, readily available USB drives in the terabytes make size less of a problem for many uses.
We are also becoming very accustomed to YubiKey authenticators in the USB ports of our computers. A small USB drive in our devices can now easily contain a key of greater length than all the text messages we would expect to send over a lifetime.
### Multi-Device Coordination
One of the problems to address is the fact that to use an OTP across several devices means that they have to coordinate to know when they are encrypting new plaintext and where to start in the key. Reusing the same section of the pad, while not necessarily fatal, degrades the encryption from its status as "Information Theoretically Secure".
To address this problem, we can use Nostr to share among devices the place in the pad that was last left off.
### Additional Benefits
One-time pads can be trivially encrypted and decrypted using pencil and paper, making them accessible even without electronic devices.
## Features ## Features
@@ -13,15 +54,6 @@ A secure one-time pad (OTP) cipher implementation in C with automatic versioning
- **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
## Version Information
This project uses an automatic versioning system that:
- Automatically increments the patch version on each build
- Embeds build timestamp, git commit hash, and branch information
- Creates git tags for version tracking
- Generates version header files with detailed build metadata
Current version can be viewed with: `./otp --help` or by running the interactive mode.
## Building ## Building
@@ -31,7 +63,7 @@ Current version can be viewed with: `./otp --help` or by running the interactive
- 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

26
TODO.md Normal file
View File

@@ -0,0 +1,26 @@
# TODO
## Change technique for adding keyboard entropy.
## Some of the processing seems similar, so maybe code could be more compact.
## Command line otp -e should go to default pad, and then comment after the fact that it used the default pad.
## There is the problem of the location of the pad revealing metadata about how many messages have been sent in the past, or at least the size of the messsages.
One solution could be to start the pad at a random location, and then wrap around, so an attacker could never tell the size of the past text sent. This helps. But then you have to store the start location, which you could do within the header of the pad along with the pad?
Or, better yet, assume the offset is a very large size, and use the pad itself to encrypt the offset.
## Take a look at how the file header is being handled.
## We have three different decrypt file functions
## Preferences directory and files look off. Should probably have ~/.otp as the default directory, and then in there we can have otp.conf, pads/
## Setup for multiple USB drives
## Change back in pad menu to exit

210
otp.c
View File

@@ -131,6 +131,16 @@ void get_directory_display(const char* file_path, char* result, size_t result_si
void print_usage(const char* program_name); void print_usage(const char* program_name);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// MAIN
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// Load preferences first // Load preferences first
load_preferences(); load_preferences();
@@ -329,11 +339,11 @@ void show_main_menu(void) {
printf("\n\n\n\n=========================== Main Menu - OTP %s ===========================\n\n", get_version() ); printf("\n\n\n\n=========================== Main Menu - OTP %s ===========================\n\n", get_version() );
printf(" \033[4mT\033[0mext encrypt\n"); printf(" \033[4mT\033[0mext encrypt\n"); //TEXT ENCRYPT
printf(" \033[4mF\033[0mile encrypt\n"); printf(" \033[4mF\033[0mile encrypt\n"); //FILE ENCRYPT
printf(" \033[4mD\033[0mecrypt\n"); printf(" \033[4mD\033[0mecrypt\n"); //DECRYPT
printf(" \033[4mP\033[0mads\n"); printf(" \033[4mP\033[0mads\n"); //PADS
printf(" E\033[4mx\033[0mit\n"); printf(" E\033[4mx\033[0mit\n"); //EXIT
printf("\nSelect option: "); printf("\nSelect option: ");
} }
@@ -807,9 +817,27 @@ int list_available_pads(void) {
struct dirent* entry; struct dirent* entry;
int count = 0; int count = 0;
// Get current default pad path for comparison
char* current_default = get_default_pad_path();
char default_pad_checksum[65] = "";
if (current_default) {
// Extract checksum from default pad path
char* filename = strrchr(current_default, '/');
if (!filename) filename = current_default;
else filename++; // Skip the '/'
// Extract checksum (remove .pad extension)
if (strlen(filename) >= 68 && strstr(filename, ".pad")) {
strncpy(default_pad_checksum, filename, 64);
default_pad_checksum[64] = '\0';
}
free(current_default);
}
printf("\nAvailable pads:\n"); printf("\nAvailable pads:\n");
printf("%-4s %-20s %-12s %-12s %-8s\n", "No.", "ChkSum", "Size", "Used", "% Used"); printf("%-4s %-7s %-20s %-12s %-12s %-8s\n", "No.", "Default", "ChkSum", "Size", "Used", "% Used");
printf("%-4s %-20s %-12s %-12s %-8s\n", "---", "-------------------", "----------", "----------", "------"); printf("%-4s %-7s %-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) {
@@ -818,6 +846,10 @@ int list_available_pads(void) {
strncpy(chksum, entry->d_name, 64); strncpy(chksum, entry->d_name, 64);
chksum[64] = '\0'; chksum[64] = '\0';
// Check if this is the default pad
int is_default = (strlen(default_pad_checksum) > 0 &&
strncmp(chksum, default_pad_checksum, 64) == 0);
// Get pad file size // Get pad file size
char full_path[1024]; // Increased buffer size to accommodate longer paths char full_path[1024]; // Increased buffer size to accommodate longer paths
snprintf(full_path, sizeof(full_path), "%s/%s", current_pads_dir, entry->d_name); snprintf(full_path, sizeof(full_path), "%s/%s", current_pads_dir, entry->d_name);
@@ -855,7 +887,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, chksum, size_str, used_str, percentage); printf("%-4d %-7s %-20.16s %-12s %-12s %.1f%%\n", count, is_default ? "*" : "", chksum, size_str, used_str, percentage);
} }
} }
} }
@@ -2617,7 +2649,54 @@ int load_preferences(void) {
FILE* file = fopen(preferences_file, "r"); FILE* file = fopen(preferences_file, "r");
if (!file) { if (!file) {
return 0; // No preferences file, use defaults // No preferences file exists - create it and set first pad as default
// Create .otp directory if it doesn't exist
struct stat st = {0};
if (stat(preferences_dir, &st) == -1) {
if (mkdir(preferences_dir, 0755) != 0) {
return 1;
}
}
// Find the first available pad to set as default
DIR* dir = opendir(current_pads_dir);
if (dir) {
struct dirent* entry;
char first_pad_path[1024];
int found_pad = 0;
while ((entry = readdir(dir)) != NULL && !found_pad) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) {
// Found a pad file - construct full absolute path
char absolute_path[1024];
if (current_pads_dir[0] == '/') {
// Already absolute path
snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name);
} else {
// Relative path - make it absolute
char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) {
snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s/%s", current_dir, current_pads_dir, entry->d_name);
} else {
// Fallback to relative path
snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", current_pads_dir, entry->d_name);
}
}
strncpy(default_pad_path, first_pad_path, sizeof(default_pad_path) - 1);
default_pad_path[sizeof(default_pad_path) - 1] = '\0';
found_pad = 1;
}
}
closedir(dir);
// Create the preferences file with the default pad
if (found_pad) {
save_preferences();
}
}
return 0; // Successfully initialized
} }
char line[1024]; char line[1024];
@@ -2717,7 +2796,29 @@ char* get_default_pad_path(void) {
} }
int set_default_pad_path(const char* pad_path) { int set_default_pad_path(const char* pad_path) {
return set_preference("default_pad", pad_path); if (!pad_path) {
return set_preference("default_pad", NULL);
}
// Ensure we store the full absolute path
char absolute_path[1024];
if (pad_path[0] == '/') {
// Already absolute path
strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1);
absolute_path[sizeof(absolute_path) - 1] = '\0';
} else {
// Relative path - make it absolute
char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) {
snprintf(absolute_path, sizeof(absolute_path), "%s/%s", current_dir, pad_path);
} else {
// Fallback to using the path as-is if getcwd fails
strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1);
absolute_path[sizeof(absolute_path) - 1] = '\0';
}
}
return set_preference("default_pad", absolute_path);
} }
// OTP thumb drive detection function implementation // OTP thumb drive detection function implementation
@@ -2748,7 +2849,7 @@ int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) {
// Check if drive name starts with "OTP" // Check if drive name starts with "OTP"
if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue; if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue;
char user_mount_path[1024]; // Increased buffer size char user_mount_path[2048]; // Increased buffer size
// Verify buffer has enough space before concatenation // Verify buffer has enough space before concatenation
size_t mount_len = strlen(mount_path); size_t mount_len = strlen(mount_path);
size_t entry_len = strlen(user_entry->d_name); size_t entry_len = strlen(user_entry->d_name);
@@ -2780,7 +2881,7 @@ int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) {
// Check if drive name starts with "OTP" // Check if drive name starts with "OTP"
if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue; if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue;
char user_mount_path[512]; char user_mount_path[2048]; // Increased buffer size
snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name); snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name);
// Check if this is a readable directory // Check if this is a readable directory
@@ -3491,18 +3592,41 @@ int handle_pads_menu(void) {
prefixes[i][prefix_lengths[i]] = '\0'; prefixes[i][prefix_lengths[i]] = '\0';
} }
// Display pads with minimal prefixes underlined // Display pads with minimal prefixes underlined and default indicator
printf("\nAvailable pads:\n"); printf("\nAvailable pads:\n");
printf("%-8s %-12s %-12s %-12s %-8s\n", "ChkSum", "Dir", "Size", "Used", "% Used"); printf("%-7s %-8s %-12s %-12s %-12s %-8s\n", "Default", "ChkSum", "Dir", "Size", "Used", "% Used");
printf("%-8s %-12s %-12s %-12s %-8s\n", "--------", "------------", "----------", "----------", "------"); printf("%-7s %-8s %-12s %-12s %-12s %-8s\n", "-------", "--------", "------------", "----------", "----------", "------");
// Get current default pad path for comparison
char* current_default = get_default_pad_path();
char default_pad_checksum[65] = "";
if (current_default) {
// Extract checksum from default pad path
char* filename = strrchr(current_default, '/');
if (!filename) filename = current_default;
else filename++; // Skip the '/'
// Extract checksum (remove .pad extension)
if (strlen(filename) >= 68 && strstr(filename, ".pad")) {
strncpy(default_pad_checksum, filename, 64);
default_pad_checksum[64] = '\0';
}
free(current_default);
}
for (int i = 0; i < pad_count; i++) { for (int i = 0; i < pad_count; i++) {
// Check if this is the default pad
int is_default = (strlen(default_pad_checksum) > 0 &&
strncmp(pads[i].chksum, default_pad_checksum, 64) == 0);
// Display first 8 characters of checksum with prefix underlined // Display first 8 characters of checksum with prefix underlined
char checksum_8char[9]; char checksum_8char[9];
strncpy(checksum_8char, pads[i].chksum, 8); strncpy(checksum_8char, pads[i].chksum, 8);
checksum_8char[8] = '\0'; checksum_8char[8] = '\0';
printf("\033[4m%.*s\033[0m%s %-12s %-12s %-12s %.1f%%\n", printf("%-7s \033[4m%.*s\033[0m%s %-12s %-12s %-12s %.1f%%\n",
is_default ? "*" : "", // Default indicator
prefix_lengths[i], checksum_8char, // Underlined prefix prefix_lengths[i], checksum_8char, // Underlined prefix
checksum_8char + prefix_lengths[i], // Rest of 8-char checksum checksum_8char + prefix_lengths[i], // Rest of 8-char checksum
pads[i].location, // Use the stored location info pads[i].location, // Use the stored location info
@@ -3513,6 +3637,7 @@ int handle_pads_menu(void) {
printf("\nActions:\n"); printf("\nActions:\n");
printf(" \033[4mG\033[0menerate new pad\n"); printf(" \033[4mG\033[0menerate new pad\n");
printf(" \033[4mS\033[0met default pad\n");
printf(" \033[4mB\033[0mack to main menu\n"); printf(" \033[4mB\033[0mack to main menu\n");
printf("\nSelect pad (by prefix) or action: "); printf("\nSelect pad (by prefix) or action: ");
@@ -3531,6 +3656,59 @@ int handle_pads_menu(void) {
return handle_pads_menu(); return handle_pads_menu();
} }
return result; return result;
} else if (toupper(input[0]) == 'S') {
// Set default pad
printf("\nSelect pad to set as default (by prefix): ");
char pad_input[MAX_HASH_LENGTH];
if (!fgets(pad_input, sizeof(pad_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
pad_input[strcspn(pad_input, "\n")] = 0;
// Find matching pad by prefix using the same logic as pad selection
int matched_pad = -1;
int match_count = 0;
for (int i = 0; i < pad_count; i++) {
if (strncmp(pad_input, pads[i].chksum, strlen(pad_input)) == 0) {
matched_pad = i;
match_count++;
}
}
if (match_count == 0) {
printf("No pad found matching prefix '%s'\n", pad_input);
return handle_pads_menu();
} else if (match_count > 1) {
printf("Ambiguous prefix. Multiple matches found.\n");
return handle_pads_menu();
}
// Construct the full absolute pad path and set as default
char new_default_path[1024];
if (current_pads_dir[0] == '/') {
// Already absolute path
snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, pads[matched_pad].chksum);
} else {
// Relative path - make it absolute
char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) {
snprintf(new_default_path, sizeof(new_default_path), "%s/%s/%s.pad", current_dir, current_pads_dir, pads[matched_pad].chksum);
} else {
// Fallback to relative path
snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", current_pads_dir, pads[matched_pad].chksum);
}
}
if (set_default_pad_path(new_default_path) == 0) {
printf("Default pad set to: %.16s...\n", pads[matched_pad].chksum);
printf("Full path: %s\n", new_default_path);
} else {
printf("Error: Failed to update default pad preference\n");
}
return handle_pads_menu();
} else if (toupper(input[0]) == 'B') { } else if (toupper(input[0]) == 'B') {
return 0; // Back to main menu return 0; // Back to main menu
} }