Compare commits

...

8 Commits

12 changed files with 627 additions and 155 deletions

178
README.md
View File

@@ -1,51 +1,8 @@
# OTP Cipher - One Time Pad Implementation # OTP Cipher - One Time Pad Implementation
## Quick Start
### Download Pre-Built Binaries
**[📥 Download Latest Release](https://git.laantungir.net/laantungir/otp/releases)**
Visit the releases page and download the appropriate binary for your system:
- `otp-vX.X.X-linux-x86_64` for Intel/AMD 64-bit systems
- `otp-vX.X.X-linux-arm64` for Raspberry Pi and ARM 64-bit systems
After downloading:
```bash
# Make executable and run
chmod +x otp-v*-linux-x86_64
./otp-v*-linux-x86_64
```
**Or use the local build:**
```bash
# After building from source
./build/otp-x86_64 # x86_64 systems
./build/otp-arm64 # ARM64 systems
```
### First Steps
1. **Generate your first pad:**
```bash
./build/otp-x86_64 generate 1GB
```
2. **Encrypt a message:**
```bash
./build/otp-x86_64 encrypt
# Follow the interactive prompts
```
3. **Decrypt a message:**
```bash
./build/otp-x86_64 decrypt
# Paste the encrypted message
```
## Introduction ## Introduction
A secure one-time pad (OTP) cipher implementation in C. A secure one-time pad (OTP) cipher implementation in C99.
## Why One-Time Pads ## Why One-Time Pads
@@ -83,8 +40,6 @@ To address this problem, we can use Nostr to share among devices the place in th
One-time pads can be trivially encrypted and decrypted using pencil and paper, making them accessible even without electronic devices. One-time pads can be trivially encrypted and decrypted using pencil and paper, making them accessible even without electronic devices.
## Features ## Features
- **Perfect Security**: Implements true one-time pad encryption with information-theoretic security - **Perfect Security**: Implements true one-time pad encryption with information-theoretic security
@@ -99,73 +54,102 @@ One-time pads can be trivially encrypted and decrypted using pencil and paper, m
- **Cross-Platform**: Works on Linux and other UNIX-like systems - **Cross-Platform**: Works on Linux and other UNIX-like systems
## Building ## Quick Start
### Download Pre-Built Binaries
**[Download Current Linux x86](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.38/otp-v0.3.38-linux-x86_64)**
**[Download Current Raspberry Pi 64](https://git.laantungir.net/laantungir/otp/releases/download/v0.3.38/otp-v0.3.38-linux-arm64)**
After downloading:
```bash
# Rename for convenience, then make executable
mv otp-v0.3.38-linux-x86_64 otp
chmod +x otp
# Run it
./otp
```
### First Steps
1. **Generate your first pad:**
```bash
./otp generate 1GB
```
2. **Encrypt a message:**
```bash
./otp encrypt
# Follow the interactive prompts
```
3. **Decrypt a message:**
```bash
./otp decrypt
# Paste the encrypted message
```
## Building from Source
### Prerequisites ### Prerequisites
- GCC compiler - GCC compiler
- Git (for version tracking)
- Make - Make
- Optional: ARM64 cross-compiler (`gcc-aarch64-linux-gnu`) for cross-compilation
### Build Commands ### Build Commands
Use the included build script for automatic versioning and cross-compilation:
```bash
# Build for current architecture (with auto-versioning)
./build.sh "commit message"
# Build commands
./build.sh build "commit message" # Build x86_64 and ARM64 (if cross-compiler available)
./build.sh clean # Clean build artifacts
./build.sh install # Install to system
./build.sh uninstall # Remove from system
```
The build script automatically:
- Increments patch version (v0.3.24 → v0.3.25)
- Creates git commit and tag
- Builds for x86_64 and ARM64 (if cross-compiler available)
- Outputs to `build/otp-x86_64` and `build/otp-arm64`
- Uploads binaries to Gitea releases (if `~/.gitea_token` exists)
### Traditional Make
You can also use make directly (without automatic versioning):
```bash ```bash
make # Build for current architecture make # Build for current architecture
make static # Static linking make static # Static linking (standalone binary)
make clean # Clean artifacts make clean # Clean build artifacts
make install # Install to /usr/local/bin/otp make install # Install to /usr/local/bin/otp
make uninstall # Remove from system make uninstall # Remove from system
``` ```
Output: `build/otp-$(ARCH)` (e.g., `build/otp-x86_64`) Output: `build/otp-$(ARCH)` (e.g., `build/otp-x86_64`)
## Usage After building, run with:
### Interactive Mode
```bash ```bash
./build/otp-x86_64 ./build/otp-x86_64
# or
./build/otp-arm64 # On ARM systems
``` ```
## Usage
The OTP Cipher operates in two modes:
**Interactive Mode**: Run without arguments to access a menu-driven interface. Best for exploring features, managing pads, and performing operations step-by-step with prompts and guidance.
**Command Line Mode**: Provide arguments to execute specific operations directly. Ideal for scripting, automation, and quick one-off tasks.
### Interactive Mode
Launch the menu-driven interface:
```bash
./otp
```
Navigate through menus to generate pads, encrypt/decrypt messages, manage pads, and configure settings.
### Command Line Mode ### Command Line Mode
Execute operations directly with arguments:
```bash ```bash
# Generate a new pad # Generate a new pad
./build/otp-x86_64 generate 1GB ./otp generate 1GB
# Encrypt text (interactive input) # Encrypt text (will prompt for input)
./build/otp-x86_64 encrypt <pad_hash_or_prefix> ./otp encrypt <pad_hash_or_prefix>
# Decrypt message (interactive input) # Decrypt message (will prompt for input)
./build/otp-x86_64 decrypt <pad_hash_or_prefix> ./otp decrypt <pad_hash_or_prefix>
# List available pads # List available pads
./build/otp-x86_64 list ./otp list
``` ```
## Version System ## Version System
@@ -447,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

@@ -155,6 +155,45 @@ update_source_version() {
else else
print_warning "src/main.h not found - skipping version update" print_warning "src/main.h not found - skipping version update"
fi fi
# Update README.md with direct download links
if [ -f "README.md" ]; then
print_status "Updating README.md with download links for $NEW_VERSION..."
# Create the new download section with direct download links
local NEW_DOWNLOAD_SECTION="### Download Pre-Built Binaries
**[Download Current Linux x86](https://git.laantungir.net/laantungir/otp/releases/download/${NEW_VERSION}/otp-${NEW_VERSION}-linux-x86_64)**
**[Download Current Raspberry Pi 64](https://git.laantungir.net/laantungir/otp/releases/download/${NEW_VERSION}/otp-${NEW_VERSION}-linux-arm64)**
After downloading:
\`\`\`bash
# Rename for convenience, then make executable
mv otp-${NEW_VERSION}-linux-x86_64 otp
chmod +x otp
# Run it
./otp
\`\`\`"
# Use awk to replace the section between "### Download Pre-Built Binaries" and "### First Steps"
awk -v new_section="$NEW_DOWNLOAD_SECTION" '
/^### Download Pre-Built Binaries/ {
print new_section
skip=1
next
}
/^### First Steps/ {
skip=0
}
!skip
' README.md > README.md.tmp && mv README.md.tmp README.md
print_success "Updated README.md with download links for $NEW_VERSION"
else
print_warning "README.md not found - skipping README update"
fi
} }
# Cross-platform build functions # Cross-platform build functions

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.30" #define OTP_VERSION "v0.3.38"
// 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);
@@ -89,18 +89,18 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
const char* pads_dir = get_current_pads_dir(); const char* pads_dir = get_current_pads_dir();
struct statvfs stat; struct statvfs stat;
if (statvfs(pads_dir, &stat) == 0) { if (statvfs(pads_dir, &stat) == 0) {
// Use f_bfree (total free blocks) instead of f_bavail (available to non-root) // Use f_bavail (available to non-root users) for accurate space reporting
// This gives the actual free space on the filesystem, which is more accurate // This accounts for filesystem reserved space (e.g., 5% on ext4)
// for removable media and user-owned directories uint64_t available_bytes = stat.f_bavail * stat.f_frsize;
uint64_t available_bytes = stat.f_bfree * stat.f_frsize; double available_gb = (double)available_bytes / (1000.0 * 1000.0 * 1000.0);
double available_gb = (double)available_bytes / (1024.0 * 1024.0 * 1024.0); double required_gb = (double)size_bytes / (1000.0 * 1000.0 * 1000.0);
double required_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.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");
printf(" Required: %.2f GB\n", required_gb); printf(" Required: %.2f GB (%lu bytes)\n", required_gb, size_bytes);
printf(" Available: %.2f GB\n", available_gb); printf(" Available: %.2f GB (%lu bytes)\n", available_gb, available_bytes);
printf(" Shortfall: %.2f GB\n", required_gb - available_gb); printf(" Shortfall: %.2f GB\n", required_gb - available_gb);
printf(" Location: %s\n", pads_dir);
printf("\nContinue anyway? (y/N): "); printf("\nContinue anyway? (y/N): ");
char response[10]; char response[10];
@@ -129,11 +129,54 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
FILE* pad_file = fopen(temp_filename, "wb"); FILE* pad_file = fopen(temp_filename, "wb");
if (!pad_file) { if (!pad_file) {
printf("Error: Cannot create temporary pad file %s\n", temp_filename); printf("Error: Cannot create temporary pad file '%s': %s (errno %d)\n",
temp_filename, strerror(errno), errno);
fclose(urandom); fclose(urandom);
return 1; return 1;
} }
// Preallocate full file size using posix_fallocate for guaranteed space reservation
// This actually allocates disk blocks (unlike ftruncate which creates sparse files)
int fd = fileno(pad_file);
double size_gb = (double)size_bytes / (1000.0 * 1000.0 * 1000.0);
if (display_progress) {
printf("Allocating %.2f GB on disk...\n", size_gb);
}
int alloc_result = posix_fallocate(fd, 0, (off_t)size_bytes);
if (alloc_result != 0) {
printf("Error: Failed to allocate %.2f GB temp file: %s (errno %d)\n",
size_gb, strerror(alloc_result), alloc_result);
printf(" Temp file: %s\n", temp_filename);
printf(" Location: %s\n", pads_dir);
if (alloc_result == ENOSPC) {
printf(" Cause: No space left on device\n");
printf(" This means the actual available space is less than reported\n");
} else if (alloc_result == EOPNOTSUPP) {
printf(" Cause: Filesystem doesn't support posix_fallocate\n");
printf(" Falling back to ftruncate (sparse file)...\n");
if (ftruncate(fd, (off_t)size_bytes) != 0) {
printf(" Fallback failed: %s\n", strerror(errno));
fclose(pad_file);
fclose(urandom);
unlink(temp_filename);
return 1;
}
} else {
printf(" Possible causes: quota limits, filesystem restrictions\n");
fclose(pad_file);
fclose(urandom);
unlink(temp_filename);
return 1;
}
}
if (display_progress && alloc_result == 0) {
printf("✓ Allocated %.2f GB on disk (guaranteed space)\n", size_gb);
}
unsigned char buffer[64 * 1024]; // 64KB buffer unsigned char buffer[64 * 1024]; // 64KB buffer
uint64_t bytes_written = 0; uint64_t bytes_written = 0;
time_t start_time = time(NULL); time_t start_time = time(NULL);
@@ -149,7 +192,8 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
} }
if (fread(buffer, 1, (size_t)chunk_size, urandom) != (size_t)chunk_size) { if (fread(buffer, 1, (size_t)chunk_size, urandom) != (size_t)chunk_size) {
printf("Error: Failed to read from /dev/urandom\n"); printf("Error: Failed to read %lu bytes from /dev/urandom at position %lu: %s (errno %d)\n",
chunk_size, bytes_written, strerror(errno), errno);
fclose(urandom); fclose(urandom);
fclose(pad_file); fclose(pad_file);
unlink(temp_filename); unlink(temp_filename);
@@ -157,7 +201,11 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
} }
if (fwrite(buffer, 1, (size_t)chunk_size, pad_file) != (size_t)chunk_size) { if (fwrite(buffer, 1, (size_t)chunk_size, pad_file) != (size_t)chunk_size) {
printf("Error: Failed to write to pad file\n"); printf("Error: fwrite failed for %lu bytes at position %lu/%lu (%.1f%%): %s (errno %d)\n",
chunk_size, bytes_written, size_bytes,
(double)bytes_written / size_bytes * 100.0, strerror(errno), errno);
printf(" Temp file: %s\n", temp_filename);
printf(" Disk space was checked - possible causes: fragmentation, I/O timeout, quota\n");
fclose(urandom); fclose(urandom);
fclose(pad_file); fclose(pad_file);
unlink(temp_filename); unlink(temp_filename);
@@ -216,8 +264,10 @@ int generate_pad(uint64_t size_bytes, int display_progress) {
return 1; return 1;
} }
double size_gb = (double)size_bytes / (1024.0 * 1024.0 * 1024.0); if (display_progress) {
printf("Generated pad: %s (%.2f GB)\n", pad_path, size_gb); 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("Pad checksum: %s\n", chksum_hex); printf("Pad checksum: %s\n", chksum_hex);
printf("State file: %s\n", state_path); printf("State file: %s\n", state_path);
printf("Pad file set to read-only\n"); printf("Pad file set to read-only\n");
@@ -384,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
@@ -584,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);
@@ -619,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
@@ -949,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);
@@ -1017,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);
@@ -1205,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];
@@ -1227,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) {
@@ -1411,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;
}
}