Files
otp/src/pads.c

1226 lines
44 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include <time.h>
#include <ctype.h>
#include <termios.h>
#include <fcntl.h>
#include <math.h>
#include "../include/otp.h"
// Extracted pad management functions from otp.c
int show_pad_info(const char* chksum) {
char pad_filename[MAX_HASH_LENGTH + 10];
char state_filename[MAX_HASH_LENGTH + 10];
snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum);
snprintf(state_filename, sizeof(state_filename), "%s.state", chksum);
struct stat st;
if (stat(pad_filename, &st) != 0) {
printf("Pad not found: %s\n", chksum);
return 1;
}
uint64_t used_bytes;
read_state_offset(chksum, &used_bytes);
print_centered_header("Pad Information", 0);
printf("ChkSum: %s\n", chksum);
printf("File: %s\n", pad_filename);
double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0);
double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0);
double remaining_gb = (double)(st.st_size - used_bytes) / (1024.0 * 1024.0 * 1024.0);
printf("Total size: %.2f GB (%lu bytes)\n", size_gb, st.st_size);
printf("Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes);
printf("Remaining: %.2f GB (%lu bytes)\n", remaining_gb, st.st_size - used_bytes);
printf("Usage: %.1f%%\n", (double)used_bytes / st.st_size * 100.0);
return 0;
}
// Ensure pads directory exists, create if necessary
int ensure_pads_directory(void) {
const char* pads_dir = get_current_pads_dir();
struct stat st = {0};
if (stat(pads_dir, &st) == -1) {
if (mkdir(pads_dir, 0755) != 0) {
perror("Failed to create pads directory");
return -1;
}
} else if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "Pads path exists but is not a directory\n");
return -1;
}
return 0;
}
// Construct pad and state file paths from checksum
void get_pad_path(const char* chksum, char* pad_path, char* state_path) {
const char* pads_dir = get_current_pads_dir();
snprintf(pad_path, 1024, "%s/%s.pad", pads_dir, chksum);
snprintf(state_path, 1024, "%s/%s.state", pads_dir, chksum);
}
int generate_pad(uint64_t size_bytes, int display_progress) {
// Ensure pads directory exists
if (ensure_pads_directory() != 0) {
printf("Error: Cannot create pads directory\n");
return 1;
}
char temp_filename[1024];
char pad_path[MAX_HASH_LENGTH + 20];
char state_path[MAX_HASH_LENGTH + 20];
char chksum_hex[MAX_HASH_LENGTH];
// Create temporary filename in the pads directory to avoid cross-filesystem issues
const char* pads_dir = get_current_pads_dir();
snprintf(temp_filename, sizeof(temp_filename), "%s/temp_%ld.pad", pads_dir, time(NULL));
FILE* urandom = fopen("/dev/urandom", "rb");
if (!urandom) {
printf("Error: Cannot open /dev/urandom\n");
return 1;
}
FILE* pad_file = fopen(temp_filename, "wb");
if (!pad_file) {
printf("Error: Cannot create temporary pad file %s\n", temp_filename);
fclose(urandom);
return 1;
}
unsigned char buffer[64 * 1024]; // 64KB buffer
uint64_t bytes_written = 0;
time_t start_time = time(NULL);
if (display_progress) {
printf("Generating pad...\n");
}
while (bytes_written < size_bytes) {
uint64_t chunk_size = sizeof(buffer);
if (size_bytes - bytes_written < chunk_size) {
chunk_size = size_bytes - bytes_written;
}
if (fread(buffer, 1, (size_t)chunk_size, urandom) != (size_t)chunk_size) {
printf("Error: Failed to read from /dev/urandom\n");
fclose(urandom);
fclose(pad_file);
unlink(temp_filename);
return 1;
}
if (fwrite(buffer, 1, (size_t)chunk_size, pad_file) != (size_t)chunk_size) {
printf("Error: Failed to write to pad file\n");
fclose(urandom);
fclose(pad_file);
unlink(temp_filename);
return 1;
}
bytes_written += chunk_size;
if (display_progress && bytes_written % PROGRESS_UPDATE_INTERVAL == 0) {
show_progress(bytes_written, size_bytes, start_time);
}
}
if (display_progress) {
show_progress(size_bytes, size_bytes, start_time);
printf("\n");
}
fclose(urandom);
fclose(pad_file);
// Calculate XOR checksum of the pad file
if (display_progress) {
printf("Calculating pad checksum...\n");
}
if (calculate_checksum_with_progress(temp_filename, chksum_hex, display_progress, size_bytes) != 0) {
printf("Error: Cannot calculate pad checksum\n");
unlink(temp_filename);
return 1;
}
// Get final paths in pads directory
get_pad_path(chksum_hex, pad_path, state_path);
// Rename temporary file to final name (atomic operation within same directory)
if (rename(temp_filename, pad_path) != 0) {
printf("Error: Cannot rename temporary pad file to final name\n");
unlink(temp_filename);
return 1;
}
// Set pad file to read-only
if (chmod(pad_path, S_IRUSR) != 0) {
printf("Warning: Cannot set pad file to read-only\n");
}
// Initialize state file with offset 32 (first 32 bytes reserved for checksum encryption)
FILE* state_file = fopen(state_path, "wb");
if (state_file) {
uint64_t reserved_bytes = 32;
fwrite(&reserved_bytes, sizeof(uint64_t), 1, state_file);
fclose(state_file);
} else {
printf("Error: Failed to create state file\n");
unlink(pad_path);
return 1;
}
double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0);
printf("Generated pad: %s (%.2f GB)\n", pad_path, size_gb);
printf("Pad checksum: %s\n", chksum_hex);
printf("State file: %s\n", state_path);
printf("Pad file set to read-only\n");
printf("Use 'Add entropy' in Pads menu to enhance randomness.\n");
// Pause before returning to menu to let user see the success message (only in interactive mode)
if (get_interactive_mode()) {
print_centered_header("Pad Generation Complete", 1);
}
return 0;
}
int read_state_offset(const char* pad_chksum, uint64_t* offset) {
char state_filename[1024];
const char* pads_dir = get_current_pads_dir();
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, pad_chksum);
FILE* state_file = fopen(state_filename, "rb");
if (!state_file) {
*offset = 0;
return 0;
}
if (fread(offset, sizeof(uint64_t), 1, state_file) != 1) {
fclose(state_file);
*offset = 0;
return 0;
}
fclose(state_file);
return 0;
}
int write_state_offset(const char* pad_chksum, uint64_t offset) {
char state_filename[1024];
const char* pads_dir = get_current_pads_dir();
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, pad_chksum);
FILE* state_file = fopen(state_filename, "wb");
if (!state_file) {
return 1;
}
if (fwrite(&offset, sizeof(uint64_t), 1, state_file) != 1) {
fclose(state_file);
return 1;
}
fclose(state_file);
return 0;
}
// Check if a pad is unused (0% usage)
int is_pad_unused(const char* pad_chksum) {
uint64_t used_bytes;
if (read_state_offset(pad_chksum, &used_bytes) != 0) {
return 0; // Error reading state, assume used
}
return (used_bytes <= 32); // Only reserved bytes used (32 bytes for checksum encryption)
}
// Safely rename pad files (pad and state) from old to new checksum
int rename_pad_files_safely(const char* old_chksum, const char* new_chksum) {
char old_pad_path[1024], new_pad_path[1024];
char old_state_path[1024], new_state_path[1024];
const char* pads_dir = get_current_pads_dir();
// Construct file paths
snprintf(old_pad_path, sizeof(old_pad_path), "%s/%s.pad", pads_dir, old_chksum);
snprintf(new_pad_path, sizeof(new_pad_path), "%s/%s.pad", pads_dir, new_chksum);
snprintf(old_state_path, sizeof(old_state_path), "%s/%s.state", pads_dir, old_chksum);
snprintf(new_state_path, sizeof(new_state_path), "%s/%s.state", pads_dir, new_chksum);
// Check if new files would conflict with existing files
if (access(new_pad_path, F_OK) == 0) {
printf("Error: New pad file already exists: %s\n", new_pad_path);
return 1; // Conflict
}
// Rename pad file
if (rename(old_pad_path, new_pad_path) != 0) {
printf("Error: Failed to rename pad file from %s to %s\n", old_pad_path, new_pad_path);
return 2; // Pad rename failed
}
// Rename state file (if it exists)
if (access(old_state_path, F_OK) == 0) {
if (rename(old_state_path, new_state_path) != 0) {
printf("Warning: Failed to rename state file, but pad file was renamed successfully\n");
// Try to rollback pad file rename
rename(new_pad_path, old_pad_path);
return 3; // State rename failed
}
}
return 0; // Success
}
// Unified pad selection function - extracts the best UI from handle_pads_menu()
char* select_pad_interactive(const char* title, const char* prompt, pad_filter_type_t filter_type, int allow_cancel) {
// Get list of pads from current directory
const char* pads_dir = get_current_pads_dir();
DIR* dir = opendir(pads_dir);
if (!dir) {
printf("Error: Cannot open pads directory %s\n", pads_dir);
return NULL;
}
// Structure to store pad information
struct PadInfo {
char chksum[65];
char size_str[32];
char used_str[32];
double percentage;
char location[256];
};
struct PadInfo pads[100]; // Support up to 100 pads
int pad_count = 0;
// Collect all pad information
struct dirent* entry;
while ((entry = readdir(dir)) != NULL && pad_count < 100) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) {
strncpy(pads[pad_count].chksum, entry->d_name, 64);
pads[pad_count].chksum[64] = '\0';
// Get pad file size and usage info
char full_path[1024];
snprintf(full_path, sizeof(full_path), "%s/%s", pads_dir, entry->d_name);
struct stat st;
if (stat(full_path, &st) == 0) {
// Get used bytes from state
uint64_t used_bytes;
read_state_offset(pads[pad_count].chksum, &used_bytes);
// Apply filter
if (filter_type == PAD_FILTER_UNUSED_ONLY && used_bytes > 32) {
continue; // Skip used pads when filtering for unused only
}
// Format total size
if (st.st_size < 1024) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size);
} else if (st.st_size < 1024 * 1024) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1024.0);
} else if (st.st_size < 1024 * 1024 * 1024) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1024.0 * 1024.0));
} else {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1024.0 * 1024.0 * 1024.0));
}
// Format used size
if (used_bytes < 1024) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes);
} else if (used_bytes < 1024 * 1024) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1024.0);
} else if (used_bytes < 1024 * 1024 * 1024) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1024.0 * 1024.0));
} else {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1024.0 * 1024.0 * 1024.0));
}
// Calculate percentage
pads[pad_count].percentage = (double)used_bytes / st.st_size * 100.0;
// Set location info using directory display
get_directory_display(full_path, pads[pad_count].location, sizeof(pads[pad_count].location));
pad_count++;
}
}
}
closedir(dir);
if (pad_count == 0) {
printf("\n%s\n", title);
if (filter_type == PAD_FILTER_UNUSED_ONLY) {
printf("No unused pads found.\n");
printf("Entropy can only be added to pads with 0%% usage (only reserved bytes used).\n");
} else {
printf("No pads found.\n");
}
return NULL;
}
// Calculate minimal unique prefixes for each pad
char prefixes[100][65];
int prefix_lengths[100];
for (int i = 0; i < pad_count; i++) {
prefix_lengths[i] = 1;
// Find minimal unique prefix
while (prefix_lengths[i] <= 64) {
int unique = 1;
// Check if current prefix is unique among all other pads
for (int j = 0; j < pad_count; j++) {
if (i != j && strncmp(pads[i].chksum, pads[j].chksum, prefix_lengths[i]) == 0) {
unique = 0;
break;
}
}
if (unique) {
break;
}
prefix_lengths[i]++;
}
// Store the minimal prefix
strncpy(prefixes[i], pads[i].chksum, prefix_lengths[i]);
prefixes[i][prefix_lengths[i]] = '\0';
}
// Display title and pads table
printf("\n%s\n", title);
printf("%-8s %-2s %-12s %-12s %-12s %-8s\n", "ChkSum", "D", "Dir", "Size", "Used", "% Used");
printf("%-8s %-2s %-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++) {
// 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
char checksum_8char[9];
strncpy(checksum_8char, pads[i].chksum, 8);
checksum_8char[8] = '\0';
printf("\033[4m%.*s\033[0m%s %-2s %-12s %-12s %-12s %.1f%%\n",
prefix_lengths[i], checksum_8char, // Underlined prefix
checksum_8char + prefix_lengths[i], // Rest of 8-char checksum
is_default ? "*" : "", // Default indicator
pads[i].location,
pads[i].size_str,
pads[i].used_str,
pads[i].percentage);
}
// Display prompt
printf("\n%s", prompt);
if (allow_cancel) {
printf(" (or 'x' to cancel)");
}
printf(": ");
char input[MAX_HASH_LENGTH];
if (!fgets(input, sizeof(input), stdin)) {
printf("Error: Failed to read input\n");
return NULL;
}
input[strcspn(input, "\n")] = 0;
// Handle empty input - select default pad if available
if (strlen(input) == 0) {
// Get current default pad path
char* current_default = get_default_pad_path();
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")) {
char default_checksum[65];
strncpy(default_checksum, filename, 64);
default_checksum[64] = '\0';
// Verify this default pad is in our current list
for (int i = 0; i < pad_count; i++) {
if (strncmp(pads[i].chksum, default_checksum, 64) == 0) {
free(current_default);
printf("Selected default pad: %.16s...\n\n", default_checksum);
return strdup(default_checksum);
}
}
}
free(current_default);
}
// No default pad or default pad not in current list
printf("No default pad available or default pad not in current list\n");
return NULL;
}
// Handle cancel
if (allow_cancel && (toupper(input[0]) == 'X' && strlen(input) == 1)) {
return NULL;
}
// Find matching pad by prefix only
int selected_pad = -1;
int match_count = 0;
// Try prefix matching only
for (int i = 0; i < pad_count; i++) {
if (strncmp(input, pads[i].chksum, strlen(input)) == 0) {
if (match_count == 0) {
selected_pad = i;
}
match_count++;
}
}
if (match_count == 0) {
printf("No pad found matching '%s'\n", input);
return NULL;
} else if (match_count > 1) {
printf("Ambiguous prefix. Multiple matches found.\n");
return NULL;
}
// Return selected pad checksum (caller must free)
return strdup(pads[selected_pad].chksum);
}
int handle_pads_menu(void) {
printf("\n");
print_centered_header("Pad Management", 0);
// Get list of pads from current directory
const char* pads_dir = get_current_pads_dir();
DIR* dir = opendir(pads_dir);
if (!dir) {
printf("Error: Cannot open pads directory %s\n", pads_dir);
return 1;
}
// Structure to store pad information
struct PadInfo {
char chksum[65];
char size_str[32];
char used_str[32];
double percentage;
char location[256]; // Store location info
};
struct PadInfo pads[100]; // Support up to 100 pads
int pad_count = 0;
// Collect all pad information
struct dirent* entry;
while ((entry = readdir(dir)) != NULL && pad_count < 100) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) {
strncpy(pads[pad_count].chksum, entry->d_name, 64);
pads[pad_count].chksum[64] = '\0';
// Get pad file size and usage info
char full_path[1024]; // Increased buffer size
snprintf(full_path, sizeof(full_path), "%s/%s", pads_dir, entry->d_name);
struct stat st;
if (stat(full_path, &st) == 0) {
// Get used bytes from state
uint64_t used_bytes;
read_state_offset(pads[pad_count].chksum, &used_bytes);
// Format total size
if (st.st_size < 1024) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size);
} else if (st.st_size < 1024 * 1024) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1024.0);
} else if (st.st_size < 1024 * 1024 * 1024) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1024.0 * 1024.0));
} else {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1024.0 * 1024.0 * 1024.0));
}
// Format used size
if (used_bytes < 1024) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes);
} else if (used_bytes < 1024 * 1024) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1024.0);
} else if (used_bytes < 1024 * 1024 * 1024) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1024.0 * 1024.0));
} else {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1024.0 * 1024.0 * 1024.0));
}
// Calculate percentage
pads[pad_count].percentage = (double)used_bytes / st.st_size * 100.0;
// Set location info using directory display
get_directory_display(full_path, pads[pad_count].location, sizeof(pads[pad_count].location));
pad_count++;
}
}
}
closedir(dir);
if (pad_count == 0) {
printf("No pads found.\n");
printf("\nOptions:\n");
printf(" \033[4mG\033[0menerate new pad\n");
printf(" E\033[4mx\033[0mit\n");
printf("\nSelect option: ");
char input[10];
if (fgets(input, sizeof(input), stdin)) {
char choice = toupper(input[0]);
if (choice == 'G') {
int result = handle_generate_menu();
if (result == 0) {
// After successful pad generation, return to pads menu
return handle_pads_menu();
}
return result;
}
}
return 0;
}
// Calculate minimal unique prefixes for each pad
char prefixes[100][65]; // Store the minimal prefix for each pad
int prefix_lengths[100]; // Length of minimal prefix for each pad
for (int i = 0; i < pad_count; i++) {
prefix_lengths[i] = 1;
// Find minimal unique prefix
while (prefix_lengths[i] <= 64) {
int unique = 1;
// Check if current prefix is unique among all other pads
for (int j = 0; j < pad_count; j++) {
if (i != j && strncmp(pads[i].chksum, pads[j].chksum, prefix_lengths[i]) == 0) {
unique = 0;
break;
}
}
if (unique) {
break;
}
prefix_lengths[i]++;
}
// Store the minimal prefix
strncpy(prefixes[i], pads[i].chksum, prefix_lengths[i]);
prefixes[i][prefix_lengths[i]] = '\0';
}
// Display pads with minimal prefixes underlined and default indicator
printf("\nAvailable pads:\n");
printf("%-8s %-2s %-12s %-12s %-12s %-8s\n", "ChkSum", "D", "Dir", "Size", "Used", "% Used");
printf("%-8s %-2s %-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++) {
// 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
char checksum_8char[9];
strncpy(checksum_8char, pads[i].chksum, 8);
checksum_8char[8] = '\0';
printf("\033[4m%.*s\033[0m%s %-2s %-12s %-12s %-12s %.1f%%\n",
prefix_lengths[i], checksum_8char, // Underlined prefix
checksum_8char + prefix_lengths[i], // Rest of 8-char checksum
is_default ? "*" : "", // Default indicator
pads[i].location, // Use the stored location info
pads[i].size_str,
pads[i].used_str,
pads[i].percentage);
}
printf("\nActions:\n");
printf(" \033[4mG\033[0menerate new pad\n");
printf(" \033[4mA\033[0mdd entropy to pad\n");
printf(" \033[4mV\033[0merify pad integrity\n");
printf(" \033[4mD\033[0melete pad\n");
printf(" \033[4mS\033[0met default pad\n");
printf(" E\033[4mx\033[0mit\n");
printf("\nSelect action: ");
char input[MAX_HASH_LENGTH];
if (!fgets(input, sizeof(input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
input[strcspn(input, "\n")] = 0;
// Handle actions
if (toupper(input[0]) == 'G') {
int result = handle_generate_menu();
if (result == 0) {
// After successful pad generation, return to pads menu
return handle_pads_menu();
}
return result;
} else if (toupper(input[0]) == 'A') {
// Add entropy to pad - use unified function with unused pads filter
char* selected_pad = select_pad_interactive("Select Unused Pad for Entropy Addition",
"Select unused pad (by prefix)",
PAD_FILTER_UNUSED_ONLY, 1);
if (!selected_pad) {
printf("Entropy addition cancelled.\n");
return handle_pads_menu();
}
// Add entropy to the selected unused pad
int result = handle_add_entropy_to_pad(selected_pad);
free(selected_pad);
if (result == 0) {
// Return to pads menu after successful entropy addition
return handle_pads_menu();
}
return result;
} else if (toupper(input[0]) == 'V') {
// Verify pad integrity - use unified function
char* selected_pad = select_pad_interactive("Select Pad for Verification",
"Select pad to verify (by prefix)",
PAD_FILTER_ALL, 1);
if (!selected_pad) {
printf("Pad verification cancelled.\n");
return handle_pads_menu();
}
// Verify the selected pad
handle_verify_pad(selected_pad);
free(selected_pad);
return handle_pads_menu(); // Always return to pads menu after verification
} else if (toupper(input[0]) == 'D') {
// Delete pad - use unified function
char* selected_pad = select_pad_interactive("Select Pad for Deletion",
"Select pad to delete (by prefix)",
PAD_FILTER_ALL, 1);
if (!selected_pad) {
printf("Pad deletion cancelled.\n");
return handle_pads_menu();
}
// Delete the selected pad
handle_delete_pad(selected_pad);
free(selected_pad);
return handle_pads_menu(); // Always return to pads menu after deletion attempt
} else if (toupper(input[0]) == 'S') {
// Set default pad - use unified function
char* selected_pad = select_pad_interactive("Select Default Pad",
"Select pad to set as default (by prefix)",
PAD_FILTER_ALL, 1);
if (!selected_pad) {
printf("Default pad selection cancelled.\n");
return handle_pads_menu();
}
// Construct the full absolute pad path and set as default
char new_default_path[1024];
const char* pads_dir = get_current_pads_dir();
if (pads_dir[0] == '/') {
// Already absolute path
int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", pads_dir, selected_pad);
if (ret >= (int)sizeof(new_default_path)) {
printf("Error: Path too long for default pad setting\n");
free(selected_pad);
return handle_pads_menu();
}
} else {
// Relative path - make it absolute
char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) {
int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s/%s.pad", current_dir, pads_dir, selected_pad);
if (ret >= (int)sizeof(new_default_path)) {
// Path was truncated, fall back to relative path
int ret2 = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", pads_dir, selected_pad);
if (ret2 >= (int)sizeof(new_default_path)) {
printf("Error: Path too long for default pad setting\n");
free(selected_pad);
return handle_pads_menu();
}
}
} else {
// Fallback to relative path
int ret = snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", pads_dir, selected_pad);
if (ret >= (int)sizeof(new_default_path)) {
printf("Error: Path too long for default pad setting\n");
free(selected_pad);
return handle_pads_menu();
}
}
}
if (set_default_pad_path(new_default_path) == 0) {
printf("Default pad set to: %.16s...\n", selected_pad);
printf("Full path: %s\n", new_default_path);
} else {
printf("Error: Failed to update default pad preference\n");
}
free(selected_pad);
return handle_pads_menu();
} else if (toupper(input[0]) == 'X') {
return 0; // Exit to main menu
} else {
printf("Invalid action. Please select G, A, S, or X.\n");
return handle_pads_menu();
}
}
// Update pad checksum after entropy addition
int update_pad_checksum_after_entropy(const char* old_chksum, char* new_chksum) {
char pad_path[1024];
const char* pads_dir = get_current_pads_dir();
snprintf(pad_path, sizeof(pad_path), "%s/%s.pad", pads_dir, old_chksum);
// Calculate new checksum of the modified pad
if (calculate_checksum(pad_path, new_chksum) != 0) {
printf("Error: Cannot calculate new pad checksum\n");
return 1;
}
// Check if checksum actually changed
if (strcmp(old_chksum, new_chksum) == 0) {
printf("Warning: Pad checksum unchanged after entropy addition\n");
return 2; // Checksum didn't change (unusual but not fatal)
}
// Rename pad files to use new checksum
if (rename_pad_files_safely(old_chksum, new_chksum) != 0) {
return 3; // Rename failed
}
// Update default pad preference if this was the default pad
char* current_default = get_default_pad_path();
if (current_default) {
// Check if the old pad was the default
if (strstr(current_default, old_chksum)) {
// Update to new checksum
char new_default_path[1024];
const char* pads_dir = get_current_pads_dir();
snprintf(new_default_path, sizeof(new_default_path), "%s/%s.pad", pads_dir, new_chksum);
if (set_default_pad_path(new_default_path) != 0) {
printf("Warning: Failed to update default pad preference\n");
} else {
printf("Updated default pad to new checksum: %.16s...\n", new_chksum);
}
}
free(current_default);
}
return 0; // Success
}
// Verify pad integrity by checking its checksum
int handle_verify_pad(const char* chksum) {
char pad_filename[MAX_HASH_LENGTH + 10];
char state_filename[MAX_HASH_LENGTH + 10];
char calculated_chksum[MAX_HASH_LENGTH];
snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum);
snprintf(state_filename, sizeof(state_filename), "%s.state", chksum);
struct stat st;
if (stat(pad_filename, &st) != 0) {
printf("Pad not found: %s\n", chksum);
return 1;
}
uint64_t used_bytes;
read_state_offset(chksum, &used_bytes);
print_centered_header("Pad Verification", 0);
printf("ChkSum: %s\n", chksum);
printf("File: %s\n", pad_filename);
double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0);
double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0);
double remaining_gb = (double)(st.st_size - used_bytes) / (1024.0 * 1024.0 * 1024.0);
printf("Total size: %.2f GB (%lu bytes)\n", size_gb, st.st_size);
printf("Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes);
printf("Remaining: %.2f GB (%lu bytes)\n", remaining_gb, st.st_size - used_bytes);
printf("Usage: %.1f%%\n", (double)used_bytes / st.st_size * 100.0);
// Calculate actual checksum
printf("\nCalculating checksum...\n");
if (calculate_checksum(pad_filename, calculated_chksum) != 0) {
printf("Error: Cannot calculate pad checksum\n");
return 1;
}
printf("Expected checksum: %s\n", chksum);
printf("Calculated checksum: %s\n", calculated_chksum);
if (strcmp(chksum, calculated_chksum) == 0) {
printf("\n✓ Pad integrity verified - checksum matches!\n");
printf("This pad is safe to use.\n");
return 0;
} else {
printf("\n✗ Pad integrity check FAILED - checksum mismatch!\n");
printf("This pad may be corrupted and should not be used.\n");
printf("Consider regenerating this pad.\n");
return 1;
}
}
// Delete a pad and its associated state file
int handle_delete_pad(const char* chksum) {
char pad_filename[MAX_HASH_LENGTH + 10];
char state_filename[MAX_HASH_LENGTH + 10];
snprintf(pad_filename, sizeof(pad_filename), "%s.pad", chksum);
snprintf(state_filename, sizeof(state_filename), "%s.state", chksum);
// Check if pad exists
if (access(pad_filename, F_OK) != 0) {
printf("Pad not found: %s\n", chksum);
return 1;
}
// Check if this is the default pad
char* current_default = get_default_pad_path();
int is_default = 0;
if (current_default) {
if (strstr(current_default, chksum)) {
is_default = 1;
}
free(current_default);
}
if (is_default) {
printf("Warning: This is the current default pad.\n");
printf("Deleting it will require setting a new default pad.\n");
}
// Get pad info for confirmation
struct stat st;
if (stat(pad_filename, &st) == 0) {
uint64_t used_bytes;
read_state_offset(chksum, &used_bytes);
double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0);
printf("\nPad to delete:\n");
printf("Checksum: %s\n", chksum);
printf("Size: %.2f GB\n", size_gb);
printf("Used: %.1f%%\n", (double)used_bytes / st.st_size * 100.0);
}
// First confirmation
printf("\nAre you sure you want to delete this pad? (y/N): ");
char input[10];
if (!fgets(input, sizeof(input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
if (toupper(input[0]) != 'Y') {
printf("Pad deletion cancelled.\n");
return 0;
}
// Second confirmation with checksum
printf("Type the first 8 characters of the checksum to confirm deletion: ");
if (!fgets(input, sizeof(input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
input[strcspn(input, "\n")] = 0;
if (strlen(input) < 8 || strncmp(input, chksum, 8) != 0) {
printf("Confirmation failed. Pad deletion cancelled.\n");
return 1;
}
// Delete state file first
if (access(state_filename, F_OK) == 0) {
if (unlink(state_filename) != 0) {
printf("Error: Failed to delete state file %s\n", state_filename);
return 1;
}
printf("Deleted state file: %s\n", state_filename);
}
// Delete pad file
if (unlink(pad_filename) != 0) {
printf("Error: Failed to delete pad file %s\n", pad_filename);
return 1;
}
printf("Deleted pad file: %s\n", pad_filename);
// Clear default pad preference if this was the default
if (is_default) {
if (set_default_pad_path("") != 0) {
printf("Warning: Failed to clear default pad preference\n");
} else {
printf("Cleared default pad preference (was this pad)\n");
}
}
printf("Pad deletion completed successfully.\n");
return 0;
}
int handle_add_entropy_to_pad(const char* pad_chksum) {
char header_text[128];
snprintf(header_text, sizeof(header_text), "Add Entropy to Pad: %.16s...", pad_chksum);
printf("\n");
print_centered_header(header_text, 0);
// Present entropy source selection menu with consistent formatting
printf("Select entropy source:\n");
printf(" \033[4mK\033[0meyboard entropy - Random typing for entropy collection\n");
printf(" \033[4mD\033[0mice/Coins/Cards - Manual input for high-quality entropy\n");
printf(" \033[4mH\033[0mardware RNG - Hardware random number generators\n");
printf(" \033[4mF\033[0mile - Load entropy from binary file\n");
printf(" \033[4mT\033[0mest RNG Speed - Test TrueRNG/SwiftRNG device performance\n");
printf(" E\033[4mx\033[0mit\n");
printf("\nSelect option: ");
char source_input[10];
if (!fgets(source_input, sizeof(source_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
char choice = toupper(source_input[0]);
entropy_source_t entropy_source;
switch (choice) {
case 'K':
entropy_source = ENTROPY_SOURCE_KEYBOARD;
break;
case 'D':
entropy_source = ENTROPY_SOURCE_DICE;
break;
case 'H':
// Hardware RNG selected - will handle after target_bytes selection
entropy_source = ENTROPY_SOURCE_TRUERNG;
break;
case 'F':
entropy_source = ENTROPY_SOURCE_FILE;
break;
case 'T':
// Test RNG speed - this doesn't collect entropy, just tests the device
// MOVED TO src/trng.c - commented out here
// return test_truerng_speed();
printf("Test RNG speed functionality moved to TRNG module\n");
return 1;
case 'X':
return 0; // Exit
default:
printf("Invalid choice. Please select K, D, H, F, T, or X.\n");
return 1;
}
size_t target_bytes;
// For TrueRNG, automatically use the full pad size
if (entropy_source == ENTROPY_SOURCE_TRUERNG) {
// Get the pad file size
char pad_path[1024];
char state_path[1024];
get_pad_path(pad_chksum, pad_path, state_path);
struct stat pad_stat;
if (stat(pad_path, &pad_stat) != 0) {
printf("Error: Cannot get pad file size\n");
return 1;
}
target_bytes = (size_t)pad_stat.st_size;
printf("\nTrueRNG selected - will enhance entire pad with hardware entropy\n");
printf("Pad size: %.2f GB (%zu bytes)\n",
(double)target_bytes / (1024.0 * 1024.0 * 1024.0), target_bytes);
} else {
// For other entropy sources, show the selection menu
printf("\nEntropy collection options:\n");
printf(" 1. Recommended (2048 bytes) - Optimal security\n");
printf(" 2. Minimum (1024 bytes) - Good security\n");
printf(" 3. Maximum (4096 bytes) - Maximum security\n");
printf(" 4. Custom amount\n");
printf("Enter choice (1-4): ");
char amount_input[10];
if (!fgets(amount_input, sizeof(amount_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
target_bytes = 2048; // Default
int amount_choice = atoi(amount_input);
switch (amount_choice) {
case 1:
target_bytes = 2048;
break;
case 2:
target_bytes = 1024;
break;
case 3:
target_bytes = 4096;
break;
case 4:
printf("Enter custom amount (512-8192 bytes): ");
char custom_input[32];
if (!fgets(custom_input, sizeof(custom_input), stdin)) {
printf("Error: Failed to read input\n");
return 1;
}
size_t custom_amount = (size_t)atoi(custom_input);
if (custom_amount < 512 || custom_amount > 8192) {
printf("Error: Invalid amount. Must be between 512 and 8192 bytes.\n");
return 1;
}
target_bytes = custom_amount;
break;
default:
target_bytes = 2048; // Default to recommended
break;
}
}
// For TrueRNG, detect all devices and present selection menu
if (entropy_source == ENTROPY_SOURCE_TRUERNG) {
// Use streaming collection with selected device
int result = collect_truerng_entropy_streaming_from_device(NULL, pad_chksum, target_bytes, 1, 1);
if (result != 0) {
printf("Error: TrueRNG streaming entropy collection failed\n");
return 1;
}
// Update checksum after entropy addition
printf("\n🔄 Updating pad checksum...\n");
char new_chksum[65];
int checksum_result = update_pad_checksum_after_entropy(pad_chksum, new_chksum);
if (checksum_result == 0) {
printf("✓ Pad checksum updated successfully\n");
printf(" Old checksum: %.16s...\n", pad_chksum);
printf(" New checksum: %.16s...\n", new_chksum);
printf("✓ Pad files renamed to new checksum\n");
} else if (checksum_result == 2) {
printf(" Checksum unchanged (unusual but not an error)\n");
} else {
printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n");
printf(" You may need to manually handle the checksum update\n");
return 1;
}
printf("\n🎉 SUCCESS! Your entire pad now has enhanced randomness!\n");
// Use enhanced pause mechanism instead of simple getchar
print_centered_header("Pad Enhancement Complete", 1);
return 0;
}
// For other entropy sources or smaller amounts, use traditional approach
printf("\nCollecting %zu bytes of entropy from selected source...\n", target_bytes);
// Allocate entropy buffer
unsigned char* entropy_buffer = malloc(MAX_ENTROPY_BUFFER);
if (!entropy_buffer) {
printf("Error: Cannot allocate entropy buffer\n");
return 1;
}
// Collect entropy using unified interface
size_t collected_bytes = 0;
int result = collect_entropy_by_source(entropy_source, entropy_buffer, target_bytes, &collected_bytes, 1);
if (result != 0) {
printf("Error: Entropy collection failed\n");
free(entropy_buffer);
return 1;
}
if (collected_bytes < 512) {
printf("Error: Insufficient entropy collected (%zu bytes)\n", collected_bytes);
free(entropy_buffer);
return 1;
}
printf("\nProcessing entropy and modifying pad...\n");
// Add entropy to pad
result = add_entropy_to_pad(pad_chksum, entropy_buffer, collected_bytes, 1);
// Clear entropy buffer for security
memset(entropy_buffer, 0, MAX_ENTROPY_BUFFER);
free(entropy_buffer);
if (result != 0) {
printf("Error: Failed to add entropy to pad\n");
return 1;
}
printf("\n🎉 SUCCESS! Your pad now has enhanced randomness!\n");
// Use enhanced pause mechanism instead of simple getchar
print_centered_header("Entropy Enhancement Complete", 1);
return 0;
}