From 977da58a3bc46f7ae07e925325d724d1bf896eaa Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Wed, 24 Dec 2025 10:00:27 -0500 Subject: [PATCH] Version v0.3.37 - Implement ChaCha20 nonce extension to support pads larger than 256GB --- src/entropy.c | 40 ++++- src/nostr_chacha20.c | 46 +++++- src/nostr_chacha20.h | 27 +++- tests/test_chacha20_extended | Bin 0 -> 20912 bytes tests/test_chacha20_extended.c | 263 +++++++++++++++++++++++++++++++++ 5 files changed, 366 insertions(+), 10 deletions(-) create mode 100755 tests/test_chacha20_extended create mode 100644 tests/test_chacha20_extended.c diff --git a/src/entropy.c b/src/entropy.c index 2cb1616..8d71b5d 100644 --- a/src/entropy.c +++ b/src/entropy.c @@ -219,8 +219,22 @@ int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_da unsigned char buffer[64 * 1024]; // 64KB chunks unsigned char keystream[64 * 1024]; uint64_t offset = 0; - uint32_t counter = 0; + uint32_t counter_low = 0; + uint32_t counter_high = 0; 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) { 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 - 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"); fclose(pad_file); 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; - 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 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) { 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("✓ %zu bytes of entropy distributed across entire pad\n", entropy_size); printf("✓ Pad restored to read-only mode\n"); diff --git a/src/nostr_chacha20.c b/src/nostr_chacha20.c index bb95d37..6262dde 100644 --- a/src/nostr_chacha20.c +++ b/src/nostr_chacha20.c @@ -129,8 +129,8 @@ int chacha20_block(const uint8_t key[32], uint32_t counter, return 0; } -int chacha20_encrypt(const uint8_t key[32], uint32_t counter, - const uint8_t nonce[12], const uint8_t* input, +int chacha20_encrypt(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], const uint8_t* input, uint8_t* output, size_t length) { uint8_t keystream[CHACHA20_BLOCK_SIZE]; size_t offset = 0; @@ -161,3 +161,45 @@ int chacha20_encrypt(const uint8_t key[32], uint32_t counter, 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; +} diff --git a/src/nostr_chacha20.h b/src/nostr_chacha20.h index 93a2f35..214e477 100644 --- a/src/nostr_chacha20.h +++ b/src/nostr_chacha20.h @@ -63,10 +63,10 @@ int chacha20_block(const uint8_t key[32], uint32_t counter, /** * ChaCha20 encryption/decryption - * + * * Encrypts or decrypts data using ChaCha20 stream cipher. * Since ChaCha20 is a stream cipher, encryption and decryption are the same operation. - * + * * @param key[in] 32-byte key * @param counter[in] Initial 32-bit counter value * @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 * @return 0 on success, negative on error */ -int chacha20_encrypt(const uint8_t key[32], uint32_t counter, - const uint8_t nonce[12], const uint8_t* input, +int chacha20_encrypt(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], const uint8_t* input, 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 diff --git a/tests/test_chacha20_extended b/tests/test_chacha20_extended new file mode 100755 index 0000000000000000000000000000000000000000..049e82d7ecdea6138e66d73930659bcd7817e8c7 GIT binary patch literal 20912 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 literal 0 HcmV?d00001 diff --git a/tests/test_chacha20_extended.c b/tests/test_chacha20_extended.c new file mode 100644 index 0000000..28bbac0 --- /dev/null +++ b/tests/test_chacha20_extended.c @@ -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 +#include +#include +#include +#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; + } +}