1582 lines
59 KiB
C
1582 lines
59 KiB
C
#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 <errno.h>
|
||
#include "../include/otp.h"
|
||
|
||
|
||
// Extracted pad management functions from otp.c
|
||
|
||
int show_pad_info(const char* chksum) {
|
||
char pad_filename[1024];
|
||
char state_filename[1024];
|
||
|
||
// Use full paths with pads directory
|
||
const char* pads_dir = get_current_pads_dir();
|
||
snprintf(pad_filename, sizeof(pad_filename), "%s/%s.pad", pads_dir, chksum);
|
||
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, 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;
|
||
}
|
||
|
||
// Check available disk space before starting
|
||
const char* pads_dir = get_current_pads_dir();
|
||
struct statvfs stat;
|
||
if (statvfs(pads_dir, &stat) == 0) {
|
||
uint64_t available_bytes = stat.f_bavail * stat.f_frsize;
|
||
double available_gb = (double)available_bytes / (1024.0 * 1024.0 * 1024.0);
|
||
double required_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0);
|
||
|
||
if (available_bytes < size_bytes) {
|
||
printf("\n⚠ WARNING: Insufficient disk space!\n");
|
||
printf(" Required: %.2f GB\n", required_gb);
|
||
printf(" Available: %.2f GB\n", available_gb);
|
||
printf(" Shortfall: %.2f GB\n", required_gb - available_gb);
|
||
printf("\nContinue anyway? (y/N): ");
|
||
|
||
char response[10];
|
||
if (!fgets(response, sizeof(response), stdin) ||
|
||
(toupper(response[0]) != 'Y')) {
|
||
printf("Pad generation cancelled.\n");
|
||
return 1;
|
||
}
|
||
printf("\n");
|
||
}
|
||
}
|
||
|
||
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
|
||
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[1024];
|
||
char state_filename[1024];
|
||
char calculated_chksum[MAX_HASH_LENGTH];
|
||
|
||
// Use full paths with pads directory
|
||
const char* pads_dir = get_current_pads_dir();
|
||
snprintf(pad_filename, sizeof(pad_filename), "%s/%s.pad", pads_dir, chksum);
|
||
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, 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[1024];
|
||
char state_filename[1024];
|
||
|
||
// Use full paths with pads directory
|
||
const char* pads_dir = get_current_pads_dir();
|
||
snprintf(pad_filename, sizeof(pad_filename), "%s/%s.pad", pads_dir, chksum);
|
||
snprintf(state_filename, sizeof(state_filename), "%s/%s.state", pads_dir, 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;
|
||
}
|
||
|
||
// Helper function to temporarily make pad writable and store original permissions
|
||
static int make_pad_temporarily_writable(const char* pad_path, mode_t* original_mode) {
|
||
struct stat st;
|
||
|
||
// Get current permissions
|
||
if (stat(pad_path, &st) != 0) {
|
||
printf("Error: Cannot get pad file permissions: %s\n", strerror(errno));
|
||
return 1;
|
||
}
|
||
|
||
// Store original permissions
|
||
*original_mode = st.st_mode;
|
||
|
||
// Check if already writable
|
||
if (st.st_mode & S_IWUSR) {
|
||
return 0; // Already writable, no change needed
|
||
}
|
||
|
||
// Make writable by adding write permission for owner
|
||
mode_t new_mode = st.st_mode | S_IWUSR;
|
||
if (chmod(pad_path, new_mode) != 0) {
|
||
printf("Error: Cannot make pad file writable: %s\n", strerror(errno));
|
||
return 1;
|
||
}
|
||
|
||
printf("✓ Temporarily made pad writable for entropy addition\n");
|
||
return 0;
|
||
}
|
||
|
||
// Helper function to restore original pad permissions
|
||
static int restore_pad_permissions(const char* pad_path, mode_t original_mode) {
|
||
struct stat st;
|
||
|
||
// Get current permissions to check if they changed
|
||
if (stat(pad_path, &st) != 0) {
|
||
printf("Warning: Cannot check current pad permissions: %s\n", strerror(errno));
|
||
return 1;
|
||
}
|
||
|
||
// Only restore if permissions are different from original
|
||
if (st.st_mode != original_mode) {
|
||
if (chmod(pad_path, original_mode) != 0) {
|
||
printf("Warning: Cannot restore original pad permissions: %s\n", strerror(errno));
|
||
return 1;
|
||
}
|
||
|
||
// Check if we restored to read-only
|
||
if (!(original_mode & S_IWUSR)) {
|
||
printf("✓ Restored pad to read-only protection\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;
|
||
|
||
// Declare variables that may be used later
|
||
char pad_path[1024] = "";
|
||
char state_path[1024] = "";
|
||
|
||
// For TrueRNG, automatically use the full pad size
|
||
if (entropy_source == ENTROPY_SOURCE_TRUERNG) {
|
||
// Get the pad file size
|
||
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("\nHardware RNG 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 if (entropy_source == ENTROPY_SOURCE_FILE) {
|
||
// Special handling for file entropy - ask for file path first
|
||
char file_path[512];
|
||
size_t file_size;
|
||
|
||
if (get_file_entropy_info(file_path, sizeof(file_path), &file_size, 1) != 0) {
|
||
return 1;
|
||
}
|
||
|
||
// Get pad size for comparison
|
||
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;
|
||
}
|
||
uint64_t pad_size = pad_stat.st_size;
|
||
|
||
printf("\nFile vs Pad Size Analysis:\n");
|
||
printf(" Entropy file: %zu bytes\n", file_size);
|
||
printf(" Target pad: %.2f GB (%lu bytes)\n",
|
||
(double)pad_size / (1024.0 * 1024.0 * 1024.0), pad_size);
|
||
|
||
// Smart method selection based on file size vs pad size
|
||
if (file_size >= pad_size) {
|
||
printf("✓ Using Streaming Direct XOR method (file ≥ pad size)\n");
|
||
printf(" Method: Streaming XOR - entropy file will be distributed across entire pad\n");
|
||
printf(" Processing: File will be streamed in chunks (no memory limit)\n");
|
||
|
||
// Store original permissions and make pad temporarily writable
|
||
mode_t original_mode;
|
||
if (make_pad_temporarily_writable(pad_path, &original_mode) != 0) {
|
||
printf("Error: Cannot make pad file writable for entropy addition\n");
|
||
return 1;
|
||
}
|
||
|
||
// Use streaming method for large files
|
||
int result = add_file_entropy_streaming(pad_chksum, file_path, file_size, 1);
|
||
|
||
if (result != 0) {
|
||
printf("Error: Failed to add file entropy to pad\n");
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
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");
|
||
|
||
// Restore permissions on the new pad file
|
||
char new_pad_path[1024];
|
||
const char* pads_dir = get_current_pads_dir();
|
||
snprintf(new_pad_path, sizeof(new_pad_path), "%s/%s.pad", pads_dir, new_chksum);
|
||
restore_pad_permissions(new_pad_path, original_mode);
|
||
} else if (checksum_result == 2) {
|
||
printf("ℹ Checksum unchanged (unusual but not an error)\n");
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
} else {
|
||
printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n");
|
||
printf(" You may need to manually handle the checksum update\n");
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
return 1;
|
||
}
|
||
|
||
printf("\n🎉 SUCCESS! Your pad now has enhanced randomness from the entropy file!\n");
|
||
print_centered_header("Entropy Enhancement Complete", 1);
|
||
|
||
return 0; // Success - exit early, don't continue to buffer-based method
|
||
} else {
|
||
printf("✓ Using ChaCha20 method (file < pad size)\n");
|
||
printf(" Method: ChaCha20 - entropy will be expanded to fill entire pad\n");
|
||
target_bytes = file_size; // Use entire file, ChaCha20 will expand it
|
||
}
|
||
|
||
printf(" Target entropy: %zu bytes\n", 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. Custom amount\n");
|
||
printf("Enter choice (1-3): ");
|
||
|
||
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:
|
||
printf("Enter custom amount (512+ 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) {
|
||
printf("Error: Invalid amount. Must be at least 512 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) {
|
||
// Detect available hardware RNG devices
|
||
hardware_rng_device_t devices[10];
|
||
int num_devices_found = 0;
|
||
|
||
if (detect_all_hardware_rng_devices(devices, 10, &num_devices_found) != 0) {
|
||
printf("Error: Failed to detect hardware RNG devices\n");
|
||
return 1;
|
||
}
|
||
|
||
if (num_devices_found == 0) {
|
||
printf("No hardware RNG devices found.\n");
|
||
printf("\nSupported devices:\n");
|
||
printf(" - TrueRNG Original (VID: %s, PID: %s)\n", TRUERNG_VID, TRUERNG_ORIGINAL_PID);
|
||
printf(" - TrueRNG Pro (VID: %s, PID: %s)\n", TRUERNG_VID, TRUERNG_PRO_PID);
|
||
printf(" - TrueRNG Pro V2 (VID: %s, PID: %s)\n", TRUERNG_VID, TRUERNG_PRO_V2_PID);
|
||
printf("\nPlease connect a TrueRNG or SwiftRNG device and try again.\n");
|
||
return 1;
|
||
}
|
||
|
||
// Select device interactively
|
||
hardware_rng_device_t selected_device;
|
||
if (select_hardware_rng_device_interactive(devices, num_devices_found, &selected_device) != 0) {
|
||
printf("Device selection cancelled.\n");
|
||
return 1;
|
||
}
|
||
|
||
// Test device speed and estimate completion time
|
||
printf("\nTesting %s connection and speed...\n", selected_device.friendly_name);
|
||
printf("Device: %s (Type: %d)\n", selected_device.port_path, selected_device.device_type);
|
||
|
||
// Test with smaller amount (10KB) to avoid hanging on slow/unresponsive devices
|
||
const size_t test_bytes = 10 * 1024; // 10KB test (reduced from 100KB)
|
||
unsigned char* test_buffer = malloc(test_bytes);
|
||
if (!test_buffer) {
|
||
printf("Error: Cannot allocate test buffer\n");
|
||
return 1;
|
||
}
|
||
|
||
size_t test_collected = 0;
|
||
time_t test_start = time(NULL);
|
||
|
||
// Use non-blocking test to avoid hanging
|
||
int test_result = collect_truerng_entropy_from_device(&selected_device, test_buffer, test_bytes, &test_collected, 0);
|
||
|
||
time_t test_end = time(NULL);
|
||
double test_time = difftime(test_end, test_start);
|
||
|
||
free(test_buffer);
|
||
|
||
if (test_result != 0) {
|
||
printf("Error: Device test failed - cannot establish connection\n");
|
||
printf("This may be due to:\n");
|
||
printf(" - Device not properly connected\n");
|
||
printf(" - Incorrect device type identification\n");
|
||
printf(" - Serial port configuration issues\n");
|
||
printf(" - Device requires different baud rate or settings\n");
|
||
return 1;
|
||
}
|
||
|
||
if (test_collected == 0) {
|
||
printf("Error: Device returned no data - check device connection and type\n");
|
||
return 1;
|
||
}
|
||
|
||
if (test_time < 1.0) {
|
||
test_time = 1.0; // Minimum 1 second to avoid division by zero
|
||
}
|
||
|
||
// Calculate speed and estimate completion time
|
||
double bytes_per_second = test_collected / test_time;
|
||
double estimated_seconds = target_bytes / bytes_per_second;
|
||
double estimated_minutes = estimated_seconds / 60.0;
|
||
double estimated_hours = estimated_minutes / 60.0;
|
||
|
||
printf("✓ Device test successful!\n");
|
||
printf(" Test collected: %zu bytes in %.1f seconds\n", test_collected, test_time);
|
||
printf(" Speed: %.1f KB/s (%.1f MB/s)\n", bytes_per_second / 1024.0, bytes_per_second / (1024.0 * 1024.0));
|
||
|
||
printf("\nPad enhancement estimate:\n");
|
||
printf(" Pad size: %.2f GB (%zu bytes)\n", (double)target_bytes / (1024.0 * 1024.0 * 1024.0), target_bytes);
|
||
|
||
if (estimated_hours >= 1.0) {
|
||
printf(" Estimated time: %.1f hours\n", estimated_hours);
|
||
} else if (estimated_minutes >= 1.0) {
|
||
printf(" Estimated time: %.1f minutes\n", estimated_minutes);
|
||
} else {
|
||
printf(" Estimated time: %.1f seconds\n", estimated_seconds);
|
||
}
|
||
|
||
// Store original permissions and make pad temporarily writable
|
||
mode_t original_mode;
|
||
if (make_pad_temporarily_writable(pad_path, &original_mode) != 0) {
|
||
// If we can't make it writable, check if it's a filesystem issue
|
||
if (access(pad_path, F_OK) == 0 && access(pad_path, W_OK) != 0) {
|
||
printf("\nError: Cannot make pad file writable: %s\n", pad_path);
|
||
printf("Reason: %s\n", strerror(errno));
|
||
|
||
if (errno == EROFS) {
|
||
printf("The filesystem appears to be read-only.\n");
|
||
printf("This commonly occurs with:\n");
|
||
printf(" - USB drives mounted read-only\n");
|
||
printf(" - CD-ROM/DVD drives\n");
|
||
printf(" - Network filesystems with read-only access\n");
|
||
} else if (errno == EACCES) {
|
||
printf("Permission denied. Check file permissions.\n");
|
||
}
|
||
|
||
printf("\nTo fix this issue:\n");
|
||
printf("1. Remount the drive read-write: sudo mount -o remount,rw %s\n", pad_path);
|
||
printf("2. Copy the pad to local storage, enhance it, then copy back\n");
|
||
printf("3. Check file permissions: ls -la '%s'\n", pad_path);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
// Ask user for confirmation
|
||
printf("\n⚠ This will modify the entire pad file and update its checksum.\n");
|
||
printf("The process cannot be interrupted once started.\n");
|
||
printf("\nDo you want to continue with hardware entropy enhancement? (y/N): ");
|
||
|
||
char confirm_input[10];
|
||
if (!fgets(confirm_input, sizeof(confirm_input), stdin)) {
|
||
printf("Error: Failed to read input\n");
|
||
return 1;
|
||
}
|
||
|
||
if (toupper(confirm_input[0]) != 'Y') {
|
||
printf("Hardware entropy enhancement cancelled.\n");
|
||
return 0;
|
||
}
|
||
|
||
printf("\nStarting hardware entropy enhancement...\n");
|
||
|
||
// Use streaming collection with selected device
|
||
int result = collect_truerng_entropy_streaming_from_device(&selected_device, pad_chksum, target_bytes, 1, 1);
|
||
|
||
if (result != 0) {
|
||
printf("Error: TrueRNG streaming entropy collection failed\n");
|
||
// Restore original permissions before returning
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
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");
|
||
|
||
// Restore permissions on the new pad file
|
||
char new_pad_path[1024];
|
||
const char* pads_dir = get_current_pads_dir();
|
||
snprintf(new_pad_path, sizeof(new_pad_path), "%s/%s.pad", pads_dir, new_chksum);
|
||
restore_pad_permissions(new_pad_path, original_mode);
|
||
} else if (checksum_result == 2) {
|
||
printf("ℹ Checksum unchanged (unusual but not an error)\n");
|
||
// Restore original permissions
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
} else {
|
||
printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n");
|
||
printf(" You may need to manually handle the checksum update\n");
|
||
// Restore original permissions before returning
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
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");
|
||
|
||
// Get pad path and manage permissions for traditional entropy addition
|
||
if (strlen(pad_path) == 0) {
|
||
get_pad_path(pad_chksum, pad_path, state_path);
|
||
}
|
||
|
||
// Store original permissions and make pad temporarily writable
|
||
mode_t original_mode;
|
||
if (make_pad_temporarily_writable(pad_path, &original_mode) != 0) {
|
||
printf("Error: Cannot make pad file writable for entropy addition\n");
|
||
// Clear entropy buffer for security
|
||
memset(entropy_buffer, 0, MAX_ENTROPY_BUFFER);
|
||
free(entropy_buffer);
|
||
return 1;
|
||
}
|
||
|
||
// 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");
|
||
// Restore original permissions before returning
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
return 1;
|
||
}
|
||
|
||
// Update checksum after entropy addition for traditional methods
|
||
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");
|
||
|
||
// Restore permissions on the new pad file
|
||
char new_pad_path[1024];
|
||
const char* pads_dir = get_current_pads_dir();
|
||
snprintf(new_pad_path, sizeof(new_pad_path), "%s/%s.pad", pads_dir, new_chksum);
|
||
restore_pad_permissions(new_pad_path, original_mode);
|
||
} else if (checksum_result == 2) {
|
||
printf("ℹ Checksum unchanged (unusual but not an error)\n");
|
||
// Restore original permissions
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
} else {
|
||
printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n");
|
||
printf(" You may need to manually handle the checksum update\n");
|
||
// Restore original permissions before returning
|
||
restore_pad_permissions(pad_path, original_mode);
|
||
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;
|
||
} |