Compare commits

...

3 Commits

11 changed files with 462 additions and 63 deletions

View File

@@ -58,14 +58,14 @@ One-time pads can be trivially encrypted and decrypted using pencil and paper, m
### Download Pre-Built Binaries ### Download Pre-Built Binaries
**[Download Current Linux x86](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.33/otp-v0.3.33-linux-x86_64)** **[Download Current Linux x86](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.35/otp-v0.3.35-linux-x86_64)**
**[Download Current Raspberry Pi 64](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.33/otp-v0.3.33-linux-arm64)** **[Download Current Raspberry Pi 64](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.35/otp-v0.3.35-linux-arm64)**
After downloading: After downloading:
```bash ```bash
# Rename for convenience, then make executable # Rename for convenience, then make executable
mv otp-v0.3.33-linux-x86_64 otp mv otp-v0.3.35-linux-x86_64 otp
chmod +x otp chmod +x otp
# Run it # Run it
@@ -431,6 +431,28 @@ No. ChkSum (first 16 chars) Size Used % Used
# Select "S" for show pad info, enter checksum or prefix # Select "S" for show pad info, enter checksum or prefix
``` ```
## Important Notes
### Size Units: Decimal (SI) vs Binary (IEC)
**This program uses decimal (SI) units for all size specifications**, matching the behavior of most system tools like `ls -lh`, `df -h`, and file managers:
- **1 KB** = 1,000 bytes (not 1,024)
- **1 MB** = 1,000,000 bytes (not 1,048,576)
- **1 GB** = 1,000,000,000 bytes (not 1,073,741,824)
- **1 TB** = 1,000,000,000,000 bytes (not 1,099,511,627,776)
**Why decimal units?**
- Consistency with system tools (`ls`, `df`, file managers)
- Matches storage device marketing (a "1TB" USB drive has ~1,000,000,000,000 bytes)
- Avoids confusion when comparing sizes across different tools
- Industry standard for storage devices and file systems
**Example:** When you request a 100GB pad, the program creates exactly 100,000,000,000 bytes, which will display as "100GB" in `ls -lh` and your file manager.
**Note:** Some technical tools may use binary units (GiB, MiB) where 1 GiB = 1,024³ bytes. This program intentionally uses decimal units for user-friendliness and consistency with common tools.
## License ## License
This project includes automatic versioning system based on the Generic Automatic Version Increment System. This project includes automatic versioning system based on the Generic Automatic Version Increment System.

View File

@@ -82,7 +82,7 @@ int add_entropy_direct_xor(const char* pad_chksum, const unsigned char* entropy_
if (display_progress) { if (display_progress) {
printf("Adding entropy to pad using direct XOR...\n"); printf("Adding entropy to pad using direct XOR...\n");
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size); printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1000.0*1000.0*1000.0), pad_size);
printf("Entropy size: %zu bytes\n", entropy_size); printf("Entropy size: %zu bytes\n", entropy_size);
} }
@@ -212,15 +212,29 @@ int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_da
if (display_progress) { if (display_progress) {
printf("Adding entropy to pad using Chacha20...\n"); printf("Adding entropy to pad using Chacha20...\n");
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size); printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1000.0*1000.0*1000.0), pad_size);
} }
// Process pad in chunks // Process pad in chunks
unsigned char buffer[64 * 1024]; // 64KB chunks unsigned char buffer[64 * 1024]; // 64KB chunks
unsigned char keystream[64 * 1024]; unsigned char keystream[64 * 1024];
uint64_t offset = 0; uint64_t offset = 0;
uint32_t counter = 0; uint32_t counter_low = 0;
uint32_t counter_high = 0;
time_t start_time = time(NULL); time_t start_time = time(NULL);
// Use extended counter for pads larger than 256GB
// 256GB = 2^32 blocks * 64 bytes = 274,877,906,944 bytes
int use_extended = (pad_size > 274877906944ULL);
// For extended mode, use reduced 8-byte nonce
unsigned char nonce_reduced[8];
if (use_extended) {
memcpy(nonce_reduced, nonce + 4, 8);
if (display_progress) {
printf("Using extended counter mode for large pad (>256GB)\n");
}
}
while (offset < pad_size) { while (offset < pad_size) {
size_t chunk_size = sizeof(buffer); size_t chunk_size = sizeof(buffer);
@@ -237,7 +251,15 @@ int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_da
} }
// Generate keystream for this chunk // Generate keystream for this chunk
if (chacha20_encrypt(key, counter, nonce, buffer, keystream, chunk_size) != 0) { int chacha_result;
if (use_extended) {
chacha_result = chacha20_encrypt_extended(key, counter_low, counter_high,
nonce_reduced, buffer, keystream, chunk_size);
} else {
chacha_result = chacha20_encrypt(key, counter_low, nonce, buffer, keystream, chunk_size);
}
if (chacha_result != 0) {
printf("Error: Chacha20 keystream generation failed\n"); printf("Error: Chacha20 keystream generation failed\n");
fclose(pad_file); fclose(pad_file);
chmod(pad_path, S_IRUSR); chmod(pad_path, S_IRUSR);
@@ -265,7 +287,16 @@ int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_da
} }
offset += chunk_size; offset += chunk_size;
counter += (chunk_size + 63) / 64; // Round up for block count
// Update counters
uint32_t blocks = (chunk_size + 63) / 64; // Round up for block count
uint32_t old_counter_low = counter_low;
counter_low += blocks;
// Check for overflow and increment high counter
if (counter_low < old_counter_low) {
counter_high++;
}
// Show progress for large pads // Show progress for large pads
if (display_progress && offset % (64 * 1024 * 1024) == 0) { // Every 64MB if (display_progress && offset % (64 * 1024 * 1024) == 0) { // Every 64MB
@@ -282,7 +313,8 @@ int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_da
if (display_progress) { if (display_progress) {
show_progress(pad_size, pad_size, start_time); show_progress(pad_size, pad_size, start_time);
printf("\n✓ Entropy successfully added to pad using Chacha20\n"); printf("\n✓ Entropy successfully added to pad using Chacha20%s\n",
use_extended ? " (extended counter)" : "");
printf("✓ Pad integrity maintained\n"); printf("✓ Pad integrity maintained\n");
printf("✓ %zu bytes of entropy distributed across entire pad\n", entropy_size); printf("✓ %zu bytes of entropy distributed across entire pad\n", entropy_size);
printf("✓ Pad restored to read-only mode\n"); printf("✓ Pad restored to read-only mode\n");
@@ -593,8 +625,8 @@ int add_file_entropy_streaming(const char* pad_chksum, const char* file_path, si
if (display_progress) { if (display_progress) {
printf("Adding entropy to pad using streaming direct XOR...\n"); printf("Adding entropy to pad using streaming direct XOR...\n");
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size); printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1000.0*1000.0*1000.0), pad_size);
printf("Entropy file: %.2f GB (%zu bytes)\n", (double)file_size / (1024.0*1024.0*1024.0), file_size); printf("Entropy file: %.2f GB (%zu bytes)\n", (double)file_size / (1000.0*1000.0*1000.0), file_size);
} }
// Process in chunks // Process in chunks

View File

@@ -23,7 +23,7 @@
#include <ctype.h> #include <ctype.h>
// Version - Updated automatically by build.sh // Version - Updated automatically by build.sh
#define OTP_VERSION "v0.3.33" #define OTP_VERSION "v0.3.35"
// Constants // Constants
#define MAX_INPUT_SIZE 4096 #define MAX_INPUT_SIZE 4096

View File

@@ -129,8 +129,8 @@ int chacha20_block(const uint8_t key[32], uint32_t counter,
return 0; return 0;
} }
int chacha20_encrypt(const uint8_t key[32], uint32_t counter, int chacha20_encrypt(const uint8_t key[32], uint32_t counter,
const uint8_t nonce[12], const uint8_t* input, const uint8_t nonce[12], const uint8_t* input,
uint8_t* output, size_t length) { uint8_t* output, size_t length) {
uint8_t keystream[CHACHA20_BLOCK_SIZE]; uint8_t keystream[CHACHA20_BLOCK_SIZE];
size_t offset = 0; size_t offset = 0;
@@ -161,3 +161,45 @@ int chacha20_encrypt(const uint8_t key[32], uint32_t counter,
return 0; return 0;
} }
int chacha20_encrypt_extended(const uint8_t key[32], uint32_t counter_low,
uint32_t counter_high, const uint8_t nonce[8],
const uint8_t* input, uint8_t* output, size_t length) {
uint8_t keystream[CHACHA20_BLOCK_SIZE];
uint8_t extended_nonce[12];
size_t offset = 0;
while (length > 0) {
/* Build extended 12-byte nonce: [counter_high (4 bytes)][nonce (8 bytes)] */
u32_to_bytes_le(counter_high, extended_nonce);
memcpy(extended_nonce + 4, nonce, 8);
/* Generate keystream block using extended nonce */
int ret = chacha20_block(key, counter_low, extended_nonce, keystream);
if (ret != 0) {
return ret;
}
/* XOR with input to produce output */
size_t block_len = (length < CHACHA20_BLOCK_SIZE) ? length : CHACHA20_BLOCK_SIZE;
for (size_t i = 0; i < block_len; i++) {
output[offset + i] = input[offset + i] ^ keystream[i];
}
/* Move to next block */
offset += block_len;
length -= block_len;
counter_low++;
/* Check for counter_low overflow and increment counter_high */
if (counter_low == 0) {
counter_high++;
/* Check for counter_high overflow (extremely unlikely - > 1 exabyte) */
if (counter_high == 0) {
return -1; /* Extended counter wrapped around */
}
}
}
return 0;
}

View File

@@ -63,10 +63,10 @@ int chacha20_block(const uint8_t key[32], uint32_t counter,
/** /**
* ChaCha20 encryption/decryption * ChaCha20 encryption/decryption
* *
* Encrypts or decrypts data using ChaCha20 stream cipher. * Encrypts or decrypts data using ChaCha20 stream cipher.
* Since ChaCha20 is a stream cipher, encryption and decryption are the same operation. * Since ChaCha20 is a stream cipher, encryption and decryption are the same operation.
* *
* @param key[in] 32-byte key * @param key[in] 32-byte key
* @param counter[in] Initial 32-bit counter value * @param counter[in] Initial 32-bit counter value
* @param nonce[in] 12-byte nonce * @param nonce[in] 12-byte nonce
@@ -75,10 +75,29 @@ int chacha20_block(const uint8_t key[32], uint32_t counter,
* @param length[in] Length of input data in bytes * @param length[in] Length of input data in bytes
* @return 0 on success, negative on error * @return 0 on success, negative on error
*/ */
int chacha20_encrypt(const uint8_t key[32], uint32_t counter, int chacha20_encrypt(const uint8_t key[32], uint32_t counter,
const uint8_t nonce[12], const uint8_t* input, const uint8_t nonce[12], const uint8_t* input,
uint8_t* output, size_t length); uint8_t* output, size_t length);
/**
* ChaCha20 encryption/decryption with extended counter (64-bit)
*
* Extended version that supports files larger than 256GB by using
* part of the nonce as a high-order counter extension.
*
* @param key[in] 32-byte key
* @param counter_low[in] Initial 32-bit counter value (low bits)
* @param counter_high[in] Initial 32-bit counter value (high bits)
* @param nonce[in] 8-byte reduced nonce (instead of 12)
* @param input[in] Input data to encrypt/decrypt
* @param output[out] Output buffer (can be same as input)
* @param length[in] Length of input data in bytes
* @return 0 on success, negative on error
*/
int chacha20_encrypt_extended(const uint8_t key[32], uint32_t counter_low,
uint32_t counter_high, const uint8_t nonce[8],
const uint8_t* input, uint8_t* output, size_t length);
/* /*
* ============================================================================ * ============================================================================
* UTILITY FUNCTIONS * UTILITY FUNCTIONS

View File

@@ -43,9 +43,9 @@ int show_pad_info(const char* chksum) {
printf("ChkSum: %s\n", chksum); printf("ChkSum: %s\n", chksum);
printf("File: %s\n", pad_filename); printf("File: %s\n", pad_filename);
double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)st.st_size / (1000.0 * 1000.0 * 1000.0);
double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0); double used_gb = (double)used_bytes / (1000.0 * 1000.0 * 1000.0);
double remaining_gb = (double)(st.st_size - used_bytes) / (1024.0 * 1024.0 * 1024.0); double remaining_gb = (double)(st.st_size - used_bytes) / (1000.0 * 1000.0 * 1000.0);
printf("Total size: %.2f GB (%lu bytes)\n", size_gb, st.st_size); 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("Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes);
@@ -92,8 +92,8 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
// Use f_bavail (available to non-root users) for accurate space reporting // Use f_bavail (available to non-root users) for accurate space reporting
// This accounts for filesystem reserved space (e.g., 5% on ext4) // This accounts for filesystem reserved space (e.g., 5% on ext4)
uint64_t available_bytes = stat.f_bavail * stat.f_frsize; uint64_t available_bytes = stat.f_bavail * stat.f_frsize;
double available_gb = (double)available_bytes / (1024.0 * 1024.0 * 1024.0); double available_gb = (double)available_bytes / (1000.0 * 1000.0 * 1000.0);
double required_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); double required_gb = (double)size_bytes / (1000.0 * 1000.0 * 1000.0);
if (available_bytes < size_bytes) { if (available_bytes < size_bytes) {
printf("\n⚠ WARNING: Insufficient disk space!\n"); printf("\n⚠ WARNING: Insufficient disk space!\n");
@@ -138,7 +138,7 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
// Preallocate full file size using posix_fallocate for guaranteed space reservation // Preallocate full file size using posix_fallocate for guaranteed space reservation
// This actually allocates disk blocks (unlike ftruncate which creates sparse files) // This actually allocates disk blocks (unlike ftruncate which creates sparse files)
int fd = fileno(pad_file); int fd = fileno(pad_file);
double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)size_bytes / (1000.0 * 1000.0 * 1000.0);
if (display_progress) { if (display_progress) {
printf("Allocating %.2f GB on disk...\n", size_gb); printf("Allocating %.2f GB on disk...\n", size_gb);
@@ -265,7 +265,7 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
} }
if (display_progress) { if (display_progress) {
double final_size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); double final_size_gb = (double)size_bytes / (1000.0 * 1000.0 * 1000.0);
printf("Generated pad: %s (%.2f GB)\n", pad_path, final_size_gb); printf("Generated pad: %s (%.2f GB)\n", pad_path, final_size_gb);
} }
printf("Pad checksum: %s\n", chksum_hex); printf("Pad checksum: %s\n", chksum_hex);
@@ -434,25 +434,25 @@ char* select_pad_interactive(const char* title, const char* prompt, pad_filter_t
} }
// Format total size // Format total size
if (st.st_size < 1024) { if (st.st_size < 1000) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size);
} else if (st.st_size < 1024 * 1024) { } else if (st.st_size < 1000 * 1000) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1024.0); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1000.0);
} else if (st.st_size < 1024 * 1024 * 1024) { } else if (st.st_size < 1000 * 1000 * 1000) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1024.0 * 1024.0)); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1000.0 * 1000.0));
} else { } else {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1024.0 * 1024.0 * 1024.0)); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1000.0 * 1000.0 * 1000.0));
} }
// Format used size // Format used size
if (used_bytes < 1024) { if (used_bytes < 1000) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes);
} else if (used_bytes < 1024 * 1024) { } else if (used_bytes < 1000 * 1000) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1024.0); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1000.0);
} else if (used_bytes < 1024 * 1024 * 1024) { } else if (used_bytes < 1000 * 1000 * 1000) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1024.0 * 1024.0)); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1000.0 * 1000.0));
} else { } else {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1024.0 * 1024.0 * 1024.0)); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1000.0 * 1000.0 * 1000.0));
} }
// Calculate percentage // Calculate percentage
@@ -634,6 +634,27 @@ int handle_pads_menu(void) {
// Get list of pads from current directory // Get list of pads from current directory
const char* pads_dir = get_current_pads_dir(); const char* pads_dir = get_current_pads_dir();
// Display directory and space information
printf("Pads Directory: %s\n", pads_dir);
// Get filesystem space information
struct statvfs vfs_stat;
if (statvfs(pads_dir, &vfs_stat) == 0) {
uint64_t total_bytes = vfs_stat.f_blocks * vfs_stat.f_frsize;
uint64_t available_bytes = vfs_stat.f_bavail * vfs_stat.f_frsize;
uint64_t used_bytes = total_bytes - (vfs_stat.f_bfree * vfs_stat.f_frsize);
double total_gb = (double)total_bytes / (1000.0 * 1000.0 * 1000.0);
double available_gb = (double)available_bytes / (1000.0 * 1000.0 * 1000.0);
double used_gb = (double)used_bytes / (1000.0 * 1000.0 * 1000.0);
double used_percent = (double)used_bytes / total_bytes * 100.0;
printf("Drive Space: %.2f GB total, %.2f GB used (%.1f%%), %.2f GB available\n",
total_gb, used_gb, used_percent, available_gb);
}
printf("\n");
DIR* dir = opendir(pads_dir); DIR* dir = opendir(pads_dir);
if (!dir) { if (!dir) {
printf("Error: Cannot open pads directory %s\n", pads_dir); printf("Error: Cannot open pads directory %s\n", pads_dir);
@@ -669,25 +690,25 @@ int handle_pads_menu(void) {
read_state_offset(pads[pad_count].chksum, &used_bytes); read_state_offset(pads[pad_count].chksum, &used_bytes);
// Format total size // Format total size
if (st.st_size < 1024) { if (st.st_size < 1000) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%luB", st.st_size);
} else if (st.st_size < 1024 * 1024) { } else if (st.st_size < 1000 * 1000) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1024.0); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fKB", (double)st.st_size / 1000.0);
} else if (st.st_size < 1024 * 1024 * 1024) { } else if (st.st_size < 1000 * 1000 * 1000) {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1024.0 * 1024.0)); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.1fMB", (double)st.st_size / (1000.0 * 1000.0));
} else { } else {
snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1024.0 * 1024.0 * 1024.0)); snprintf(pads[pad_count].size_str, sizeof(pads[pad_count].size_str), "%.2fGB", (double)st.st_size / (1000.0 * 1000.0 * 1000.0));
} }
// Format used size // Format used size
if (used_bytes < 1024) { if (used_bytes < 1000) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%luB", used_bytes);
} else if (used_bytes < 1024 * 1024) { } else if (used_bytes < 1000 * 1000) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1024.0); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fKB", (double)used_bytes / 1000.0);
} else if (used_bytes < 1024 * 1024 * 1024) { } else if (used_bytes < 1000 * 1000 * 1000) {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1024.0 * 1024.0)); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.1fMB", (double)used_bytes / (1000.0 * 1000.0));
} else { } else {
snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1024.0 * 1024.0 * 1024.0)); snprintf(pads[pad_count].used_str, sizeof(pads[pad_count].used_str), "%.2fGB", (double)used_bytes / (1000.0 * 1000.0 * 1000.0));
} }
// Calculate percentage // Calculate percentage
@@ -999,9 +1020,9 @@ int handle_verify_pad(const char* chksum) {
printf("ChkSum: %s\n", chksum); printf("ChkSum: %s\n", chksum);
printf("File: %s\n", pad_filename); printf("File: %s\n", pad_filename);
double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)st.st_size / (1000.0 * 1000.0 * 1000.0);
double used_gb = (double)used_bytes / (1024.0 * 1024.0 * 1024.0); double used_gb = (double)used_bytes / (1000.0 * 1000.0 * 1000.0);
double remaining_gb = (double)(st.st_size - used_bytes) / (1024.0 * 1024.0 * 1024.0); double remaining_gb = (double)(st.st_size - used_bytes) / (1000.0 * 1000.0 * 1000.0);
printf("Total size: %.2f GB (%lu bytes)\n", size_gb, st.st_size); 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("Used: %.2f GB (%lu bytes)\n", used_gb, used_bytes);
@@ -1067,7 +1088,7 @@ int handle_delete_pad(const char* chksum) {
uint64_t used_bytes; uint64_t used_bytes;
read_state_offset(chksum, &used_bytes); read_state_offset(chksum, &used_bytes);
double size_gb = (double)st.st_size / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)st.st_size / (1000.0 * 1000.0 * 1000.0);
printf("\nPad to delete:\n"); printf("\nPad to delete:\n");
printf("Checksum: %s\n", chksum); printf("Checksum: %s\n", chksum);
printf("Size: %.2f GB\n", size_gb); printf("Size: %.2f GB\n", size_gb);
@@ -1255,7 +1276,7 @@ int handle_add_entropy_to_pad(const char* pad_chksum) {
target_bytes = (size_t)pad_stat.st_size; target_bytes = (size_t)pad_stat.st_size;
printf("\nHardware RNG selected - will enhance entire pad with hardware entropy\n"); printf("\nHardware RNG selected - will enhance entire pad with hardware entropy\n");
printf("Pad size: %.2f GB (%zu bytes)\n", printf("Pad size: %.2f GB (%zu bytes)\n",
(double)target_bytes / (1024.0 * 1024.0 * 1024.0), target_bytes); (double)target_bytes / (1000.0 * 1000.0 * 1000.0), target_bytes);
} else if (entropy_source == ENTROPY_SOURCE_FILE) { } else if (entropy_source == ENTROPY_SOURCE_FILE) {
// Special handling for file entropy - ask for file path first // Special handling for file entropy - ask for file path first
char file_path[512]; char file_path[512];
@@ -1277,7 +1298,7 @@ int handle_add_entropy_to_pad(const char* pad_chksum) {
printf("\nFile vs Pad Size Analysis:\n"); printf("\nFile vs Pad Size Analysis:\n");
printf(" Entropy file: %zu bytes\n", file_size); printf(" Entropy file: %zu bytes\n", file_size);
printf(" Target pad: %.2f GB (%lu bytes)\n", printf(" Target pad: %.2f GB (%lu bytes)\n",
(double)pad_size / (1024.0 * 1024.0 * 1024.0), pad_size); (double)pad_size / (1000.0 * 1000.0 * 1000.0), pad_size);
// Smart method selection based on file size vs pad size // Smart method selection based on file size vs pad size
if (file_size >= pad_size) { if (file_size >= pad_size) {
@@ -1461,10 +1482,10 @@ int handle_add_entropy_to_pad(const char* pad_chksum) {
printf("✓ Device test successful!\n"); printf("✓ Device test successful!\n");
printf(" Test collected: %zu bytes in %.1f seconds\n", test_collected, test_time); 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(" Speed: %.1f KB/s (%.1f MB/s)\n", bytes_per_second / 1000.0, bytes_per_second / (1000.0 * 1000.0));
printf("\nPad enhancement estimate:\n"); printf("\nPad enhancement estimate:\n");
printf(" Pad size: %.2f GB (%zu bytes)\n", (double)target_bytes / (1024.0 * 1024.0 * 1024.0), target_bytes); printf(" Pad size: %.2f GB (%zu bytes)\n", (double)target_bytes / (1000.0 * 1000.0 * 1000.0), target_bytes);
if (estimated_hours >= 1.0) { if (estimated_hours >= 1.0) {
printf(" Estimated time: %.1f hours\n", estimated_hours); printf(" Estimated time: %.1f hours\n", estimated_hours);

View File

@@ -153,7 +153,7 @@ int collect_truerng_entropy_streaming_from_device(const hardware_rng_device_t* d
if (display_progress) { if (display_progress) {
printf("Streaming entropy from %s to pad...\n", device->friendly_name); printf("Streaming entropy from %s to pad...\n", device->friendly_name);
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size); printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1000.0*1000.0*1000.0), pad_size);
printf("Enhancing entire pad with hardware entropy\n"); printf("Enhancing entire pad with hardware entropy\n");
} }

View File

@@ -152,7 +152,7 @@ int handle_generate_menu(void) {
return 1; return 1;
} }
double size_gb = (double)size / (1024.0 * 1024.0 * 1024.0); double size_gb = (double)size / (1000.0 * 1000.0 * 1000.0);
printf("Generating %.2f GB pad...\n", size_gb); printf("Generating %.2f GB pad...\n", size_gb);
printf("Note: Use 'Add entropy' in Pads menu to enhance randomness after creation.\n"); printf("Note: Use 'Add entropy' in Pads menu to enhance randomness after creation.\n");

View File

@@ -519,13 +519,13 @@ uint64_t parse_size_string(const char* size_str) {
} }
if (strcmp(unit, "K") == 0 || strcmp(unit, "KB") == 0) { if (strcmp(unit, "K") == 0 || strcmp(unit, "KB") == 0) {
multiplier = 1024ULL; multiplier = 1000ULL;
} else if (strcmp(unit, "M") == 0 || strcmp(unit, "MB") == 0) { } else if (strcmp(unit, "M") == 0 || strcmp(unit, "MB") == 0) {
multiplier = 1024ULL * 1024ULL; multiplier = 1000ULL * 1000ULL;
} else if (strcmp(unit, "G") == 0 || strcmp(unit, "GB") == 0) { } else if (strcmp(unit, "G") == 0 || strcmp(unit, "GB") == 0) {
multiplier = 1024ULL * 1024ULL * 1024ULL; multiplier = 1000ULL * 1000ULL * 1000ULL;
} else if (strcmp(unit, "T") == 0 || strcmp(unit, "TB") == 0) { } else if (strcmp(unit, "T") == 0 || strcmp(unit, "TB") == 0) {
multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL; multiplier = 1000ULL * 1000ULL * 1000ULL * 1000ULL;
} else { } else {
return 0; // Invalid unit return 0; // Invalid unit
} }

BIN
tests/test_chacha20_extended Executable file

Binary file not shown.

View File

@@ -0,0 +1,263 @@
/*
* test_chacha20_extended.c - Test ChaCha20 extended counter implementation
*
* This test verifies that the extended counter properly handles:
* 1. Counter overflow at 2^32 blocks (256GB boundary)
* 2. Correct keystream generation across the overflow boundary
* 3. No duplicate keystream blocks
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "../src/nostr_chacha20.h"
#define TEST_BLOCK_SIZE 64
#define BLOCKS_NEAR_OVERFLOW 10 // Test blocks around overflow point
// Test helper: Compare two blocks for equality
int blocks_equal(const uint8_t* block1, const uint8_t* block2, size_t len) {
return memcmp(block1, block2, len) == 0;
}
// Test 1: Verify extended counter handles overflow correctly
int test_counter_overflow() {
printf("Test 1: Counter overflow handling\n");
printf(" Testing counter transition from 0xFFFFFFFF to 0x00000000...\n");
uint8_t key[32];
uint8_t nonce[8];
uint8_t input[TEST_BLOCK_SIZE];
uint8_t output1[TEST_BLOCK_SIZE];
uint8_t output2[TEST_BLOCK_SIZE];
uint8_t output3[TEST_BLOCK_SIZE];
// Initialize test data
memset(key, 0xAA, 32);
memset(nonce, 0xBB, 8);
memset(input, 0, TEST_BLOCK_SIZE);
// Test at counter_low = 0xFFFFFFFE, counter_high = 0
uint32_t counter_low = 0xFFFFFFFE;
uint32_t counter_high = 0;
printf(" Block at counter_low=0xFFFFFFFE, counter_high=0...\n");
if (chacha20_encrypt_extended(key, counter_low, counter_high, nonce,
input, output1, TEST_BLOCK_SIZE) != 0) {
printf(" ❌ FAILED: Error at counter_low=0xFFFFFFFE\n");
return 1;
}
// Test at counter_low = 0xFFFFFFFF, counter_high = 0
counter_low = 0xFFFFFFFF;
printf(" Block at counter_low=0xFFFFFFFF, counter_high=0...\n");
if (chacha20_encrypt_extended(key, counter_low, counter_high, nonce,
input, output2, TEST_BLOCK_SIZE) != 0) {
printf(" ❌ FAILED: Error at counter_low=0xFFFFFFFF\n");
return 1;
}
// Test at counter_low = 0x00000000, counter_high = 1 (after overflow)
counter_low = 0x00000000;
counter_high = 1;
printf(" Block at counter_low=0x00000000, counter_high=1...\n");
if (chacha20_encrypt_extended(key, counter_low, counter_high, nonce,
input, output3, TEST_BLOCK_SIZE) != 0) {
printf(" ❌ FAILED: Error at counter_low=0x00000000, counter_high=1\n");
return 1;
}
// Verify all three blocks are different (no keystream reuse)
if (blocks_equal(output1, output2, TEST_BLOCK_SIZE)) {
printf(" ❌ FAILED: Blocks at 0xFFFFFFFE and 0xFFFFFFFF are identical!\n");
return 1;
}
if (blocks_equal(output2, output3, TEST_BLOCK_SIZE)) {
printf(" ❌ FAILED: Blocks at 0xFFFFFFFF,0 and 0x00000000,1 are identical!\n");
return 1;
}
if (blocks_equal(output1, output3, TEST_BLOCK_SIZE)) {
printf(" ❌ FAILED: Blocks at 0xFFFFFFFE,0 and 0x00000000,1 are identical!\n");
return 1;
}
printf(" ✓ All blocks are unique across overflow boundary\n");
printf(" ✓ PASSED\n\n");
return 0;
}
// Test 2: Simulate processing data that crosses 256GB boundary
int test_large_file_simulation() {
printf("Test 2: Large file simulation (256GB+ boundary)\n");
printf(" Simulating processing across 256GB boundary...\n");
uint8_t key[32];
uint8_t nonce[8];
uint8_t input[1024];
uint8_t output[1024];
// Initialize test data
memset(key, 0x55, 32);
memset(nonce, 0x77, 8);
for (int i = 0; i < 1024; i++) {
input[i] = i & 0xFF;
}
// Simulate being at 256GB - 512 bytes (just before overflow)
// 256GB = 2^32 blocks * 64 bytes = 274,877,906,944 bytes
// Block number at 256GB - 512 bytes = 2^32 - 8 blocks
uint32_t counter_low = 0xFFFFFFF8; // 2^32 - 8
uint32_t counter_high = 0;
printf(" Processing 1KB starting at block 0xFFFFFFF8 (256GB - 512 bytes)...\n");
// This should cross the overflow boundary
int result = chacha20_encrypt_extended(key, counter_low, counter_high, nonce,
input, output, 1024);
if (result != 0) {
printf(" ❌ FAILED: Error processing data across 256GB boundary\n");
return 1;
}
printf(" ✓ Successfully processed data across 256GB boundary\n");
printf(" ✓ PASSED\n\n");
return 0;
}
// Test 3: Verify extended vs standard ChaCha20 compatibility
int test_compatibility() {
printf("Test 3: Compatibility with standard ChaCha20\n");
printf(" Verifying extended mode matches standard mode when counter_high=0...\n");
uint8_t key[32];
uint8_t nonce_standard[12];
uint8_t nonce_reduced[8];
uint8_t input[TEST_BLOCK_SIZE];
uint8_t output_standard[TEST_BLOCK_SIZE];
uint8_t output_extended[TEST_BLOCK_SIZE];
// Initialize test data
memset(key, 0x33, 32);
memset(nonce_standard, 0x44, 12);
memcpy(nonce_reduced, nonce_standard + 4, 8); // Extract last 8 bytes
memset(input, 0, TEST_BLOCK_SIZE);
uint32_t counter = 42;
// Standard ChaCha20
if (chacha20_encrypt(key, counter, nonce_standard, input,
output_standard, TEST_BLOCK_SIZE) != 0) {
printf(" ❌ FAILED: Standard ChaCha20 error\n");
return 1;
}
// Extended ChaCha20 with counter_high=0 and matching nonce
// The extended version builds nonce as [counter_high][nonce_reduced]
// So we need to ensure the first 4 bytes of nonce_standard are 0
uint8_t nonce_standard_zero[12] = {0};
memcpy(nonce_standard_zero + 4, nonce_reduced, 8);
if (chacha20_encrypt(key, counter, nonce_standard_zero, input,
output_standard, TEST_BLOCK_SIZE) != 0) {
printf(" ❌ FAILED: Standard ChaCha20 error\n");
return 1;
}
if (chacha20_encrypt_extended(key, counter, 0, nonce_reduced, input,
output_extended, TEST_BLOCK_SIZE) != 0) {
printf(" ❌ FAILED: Extended ChaCha20 error\n");
return 1;
}
// Compare outputs
if (!blocks_equal(output_standard, output_extended, TEST_BLOCK_SIZE)) {
printf(" ❌ FAILED: Extended mode output differs from standard mode\n");
printf(" First 16 bytes of standard: ");
for (int i = 0; i < 16; i++) printf("%02x ", output_standard[i]);
printf("\n First 16 bytes of extended: ");
for (int i = 0; i < 16; i++) printf("%02x ", output_extended[i]);
printf("\n");
return 1;
}
printf(" ✓ Extended mode matches standard mode when counter_high=0\n");
printf(" ✓ PASSED\n\n");
return 0;
}
// Test 4: Stress test - verify no errors at extreme counter values
int test_extreme_values() {
printf("Test 4: Extreme counter values\n");
printf(" Testing at various extreme counter positions...\n");
uint8_t key[32];
uint8_t nonce[8];
uint8_t input[TEST_BLOCK_SIZE];
uint8_t output[TEST_BLOCK_SIZE];
memset(key, 0x99, 32);
memset(nonce, 0x66, 8);
memset(input, 0, TEST_BLOCK_SIZE);
// Test various extreme positions
struct {
uint32_t counter_low;
uint32_t counter_high;
const char* description;
} test_cases[] = {
{0x00000000, 0, "Start of first 256GB segment"},
{0xFFFFFFFF, 0, "End of first 256GB segment"},
{0x00000000, 1, "Start of second 256GB segment"},
{0xFFFFFFFF, 1, "End of second 256GB segment"},
{0x00000000, 0xFFFF, "Start of segment 65535"},
{0xFFFFFFFF, 0xFFFF, "End of segment 65535"},
};
for (size_t i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
printf(" Testing: %s (0x%08X, 0x%08X)...\n",
test_cases[i].description,
test_cases[i].counter_low,
test_cases[i].counter_high);
if (chacha20_encrypt_extended(key, test_cases[i].counter_low,
test_cases[i].counter_high, nonce,
input, output, TEST_BLOCK_SIZE) != 0) {
printf(" ❌ FAILED at %s\n", test_cases[i].description);
return 1;
}
}
printf(" ✓ All extreme values handled correctly\n");
printf(" ✓ PASSED\n\n");
return 0;
}
int main() {
printf("=================================================================\n");
printf("ChaCha20 Extended Counter Test Suite\n");
printf("=================================================================\n\n");
int failures = 0;
failures += test_counter_overflow();
failures += test_large_file_simulation();
failures += test_compatibility();
failures += test_extreme_values();
printf("=================================================================\n");
if (failures == 0) {
printf("✓ ALL TESTS PASSED\n");
printf("=================================================================\n");
printf("\nThe extended counter implementation is working correctly.\n");
printf("It can now handle pads larger than 256GB without overflow errors.\n");
return 0;
} else {
printf("❌ %d TEST(S) FAILED\n", failures);
printf("=================================================================\n");
return 1;
}
}