Created
June 3, 2025 18:03
-
-
Save curability4apish/a532d8c9432df4886d0a26f63a8a7534 to your computer and use it in GitHub Desktop.
Forked from https://github.com/Ginurx/chacha20-c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <time.h> | |
#include <assert.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <string.h> | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
struct chacha20_context | |
{ | |
uint32_t keystream32[16]; | |
size_t position; | |
uint8_t key[32]; | |
uint8_t nonce[12]; | |
uint64_t counter; | |
uint32_t state[16]; | |
}; | |
void chacha20_init_context(struct chacha20_context *ctx, uint8_t key[], uint8_t nounc[], uint64_t counter); | |
void chacha20_xor(struct chacha20_context *ctx, uint8_t *bytes, size_t n_bytes); | |
#ifdef __cplusplus | |
} | |
#endif | |
static uint32_t rotl32(uint32_t x, int n) | |
{ | |
return (x << n) | (x >> (32 - n)); | |
} | |
static uint32_t pack4(const uint8_t *a) | |
{ | |
uint32_t res = 0; | |
res |= (uint32_t)a[0] << 0 * 8; | |
res |= (uint32_t)a[1] << 1 * 8; | |
res |= (uint32_t)a[2] << 2 * 8; | |
res |= (uint32_t)a[3] << 3 * 8; | |
return res; | |
} | |
static void unpack4(uint32_t src, uint8_t *dst) { | |
dst[0] = (src >> 0 * 8) & 0xff; | |
dst[1] = (src >> 1 * 8) & 0xff; | |
dst[2] = (src >> 2 * 8) & 0xff; | |
dst[3] = (src >> 3 * 8) & 0xff; | |
} | |
static void chacha20_init_block(struct chacha20_context *ctx, uint8_t key[], uint8_t nonce[]) | |
{ | |
memcpy(ctx->key, key, sizeof(ctx->key)); | |
memcpy(ctx->nonce, nonce, sizeof(ctx->nonce)); | |
const uint8_t *magic_constant = (uint8_t*)"expand 32-byte k"; | |
ctx->state[0] = pack4(magic_constant + 0 * 4); | |
ctx->state[1] = pack4(magic_constant + 1 * 4); | |
ctx->state[2] = pack4(magic_constant + 2 * 4); | |
ctx->state[3] = pack4(magic_constant + 3 * 4); | |
ctx->state[4] = pack4(key + 0 * 4); | |
ctx->state[5] = pack4(key + 1 * 4); | |
ctx->state[6] = pack4(key + 2 * 4); | |
ctx->state[7] = pack4(key + 3 * 4); | |
ctx->state[8] = pack4(key + 4 * 4); | |
ctx->state[9] = pack4(key + 5 * 4); | |
ctx->state[10] = pack4(key + 6 * 4); | |
ctx->state[11] = pack4(key + 7 * 4); | |
// 64 bit counter initialized to zero by default. | |
ctx->state[12] = 0; | |
ctx->state[13] = pack4(nonce + 0 * 4); | |
ctx->state[14] = pack4(nonce + 1 * 4); | |
ctx->state[15] = pack4(nonce + 2 * 4); | |
memcpy(ctx->nonce, nonce, sizeof(ctx->nonce)); | |
} | |
static void chacha20_block_set_counter(struct chacha20_context *ctx, uint64_t counter) | |
{ | |
ctx->state[12] = (uint32_t)counter; | |
ctx->state[13] = pack4(ctx->nonce + 0 * 4) + (uint32_t)(counter >> 32); | |
} | |
static void chacha20_block_next(struct chacha20_context *ctx) { | |
// This is where the crazy voodoo magic happens. | |
// Mix the bytes a lot and hope that nobody finds out how to undo it. | |
for (int i = 0; i < 16; i++) ctx->keystream32[i] = ctx->state[i]; | |
#define CHACHA20_QUARTERROUND(x, a, b, c, d) \ | |
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); \ | |
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); \ | |
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); \ | |
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); | |
for (int i = 0; i < 10; i++) | |
{ | |
CHACHA20_QUARTERROUND(ctx->keystream32, 0, 4, 8, 12) | |
CHACHA20_QUARTERROUND(ctx->keystream32, 1, 5, 9, 13) | |
CHACHA20_QUARTERROUND(ctx->keystream32, 2, 6, 10, 14) | |
CHACHA20_QUARTERROUND(ctx->keystream32, 3, 7, 11, 15) | |
CHACHA20_QUARTERROUND(ctx->keystream32, 0, 5, 10, 15) | |
CHACHA20_QUARTERROUND(ctx->keystream32, 1, 6, 11, 12) | |
CHACHA20_QUARTERROUND(ctx->keystream32, 2, 7, 8, 13) | |
CHACHA20_QUARTERROUND(ctx->keystream32, 3, 4, 9, 14) | |
} | |
for (int i = 0; i < 16; i++) ctx->keystream32[i] += ctx->state[i]; | |
uint32_t *counter = ctx->state + 12; | |
// increment counter | |
counter[0]++; | |
if (0 == counter[0]) | |
{ | |
// wrap around occured, increment higher 32 bits of counter | |
counter[1]++; | |
// Limited to 2^64 blocks of 64 bytes each. | |
// If you want to process more than 1180591620717411303424 bytes | |
// you have other problems. | |
// We could keep counting with counter[2] and counter[3] (nonce), | |
// but then we risk reusing the nonce which is very bad. | |
assert(0 != counter[1]); | |
} | |
} | |
void chacha20_init_context(struct chacha20_context *ctx, uint8_t key[], uint8_t nonce[], uint64_t counter) | |
{ | |
memset(ctx, 0, sizeof(struct chacha20_context)); | |
chacha20_init_block(ctx, key, nonce); | |
chacha20_block_set_counter(ctx, counter); | |
ctx->counter = counter; | |
ctx->position = 64; | |
} | |
void chacha20_xor(struct chacha20_context *ctx, uint8_t *bytes, size_t n_bytes) | |
{ | |
uint8_t *keystream8 = (uint8_t*)ctx->keystream32; | |
for (size_t i = 0; i < n_bytes; i++) | |
{ | |
if (ctx->position >= 64) | |
{ | |
chacha20_block_next(ctx); | |
ctx->position = 0; | |
} | |
bytes[i] ^= keystream8[ctx->position]; | |
ctx->position++; | |
} | |
} | |
void generate_random_bytes(uint8_t *buffer, size_t length) { | |
for (size_t i = 0; i < length; ++i) { | |
buffer[i] = rand() % 256; // Generate random bytes in the range [0, 255] | |
} | |
} | |
int main() { | |
// Seed the random number generator | |
srand((unsigned int)time(NULL)); | |
// Key and nonce for ChaCha20 | |
uint8_t key[32]; // 256-bit key | |
uint8_t nonce[12]; // 96-bit nonce | |
// Generate random key and nonce | |
generate_random_bytes(key, sizeof(key)); | |
generate_random_bytes(nonce, sizeof(nonce)); | |
// Initialize the ChaCha20 context | |
struct chacha20_context ctx; | |
uint64_t counter = 0; // Start counter at 0 | |
chacha20_init_context(&ctx, key, nonce, counter); | |
// Define the size of random text to generate | |
size_t sizeb = 64; // Change this to generate more or fewer bytes | |
uint8_t buffer[sizeb]; | |
// Fill the random bytes array with random data | |
chacha20_xor(&ctx, buffer, sizeb); | |
// Print the random bytes as hexadecimal values | |
printf("Generated random bytes:\n"); | |
for (size_t i = 0; i < sizeb; i++) { | |
printf("%02x ", buffer[i]); | |
} | |
printf("\n"); | |
return 0; | |
} | |
/* Example result | |
Generated random bytes: | |
80 07 b3 2b a6 ea ab 54 61 c8 86 d5 61 2c 92 54 69 62 1c 06 0e 8e 50 70 fc 91 d7 1d 5b 66 bb 08 27 fc ac d2 56 28 5e 3e 2e d3 1e 8d a1 13 d3 db ea 12 51 10 81 88 89 6e d4 67 43 09 8b fe cc c6 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment