Skip to content

Instantly share code, notes, and snippets.

@cleverca22
Last active January 5, 2025 01:11
Show Gist options
  • Save cleverca22/6255d1a637714c4147f2430bdd397506 to your computer and use it in GitHub Desktop.
Save cleverca22/6255d1a637714c4147f2430bdd397506 to your computer and use it in GitHub Desktop.
simple chacha20 implementation in c
/*
* Copyright (c) 2025 Michael Bishop
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Copyright (c) 2025 Michael Bishop
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <arpa/inet.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
// from https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant
#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
#define QR(a, b, c, d) ( \
a += b, d ^= a, d = ROTL(d, 16), \
c += d, b ^= c, b = ROTL(b, 12), \
a += b, d ^= a, d = ROTL(d, 8), \
c += d, b ^= c, b = ROTL(b, 7))
#define ROUNDS 20
void chacha_block(uint32_t out[16], uint32_t const in[16]) {
int i;
uint32_t x[16];
for (i = 0; i < 16; ++i)
x[i] = in[i];
// 10 loops × 2 rounds/loop = 20 rounds
for (i = 0; i < ROUNDS; i += 2) {
// Odd round
QR(x[0], x[4], x[ 8], x[12]); // column 1
QR(x[1], x[5], x[ 9], x[13]); // column 2
QR(x[2], x[6], x[10], x[14]); // column 3
QR(x[3], x[7], x[11], x[15]); // column 4
// Even round
QR(x[0], x[5], x[10], x[15]); // diagonal 1 (main diagonal)
QR(x[1], x[6], x[11], x[12]); // diagonal 2
QR(x[2], x[7], x[ 8], x[13]); // diagonal 3
QR(x[3], x[4], x[ 9], x[14]); // diagonal 4
}
for (i = 0; i < 16; ++i)
out[i] = x[i] + in[i];
}
void chacha_init(uint32_t state[16], const uint32_t key[8], const uint32_t counter, const uint32_t nonce[3]) {
state[0] = htonl('expa');
state[1] = htonl('nd 3');
state[2] = htonl('2-by');
state[3] = htonl('te k');
for (int i=0; i<8; i++) {
state[4+i] = key[i];
}
state[12] = counter;
state[13] = nonce[0];
state[14] = nonce[1];
state[15] = nonce[2];
}
void test1() {
uint32_t state[16] = { 0 };
uint32_t key[8] = { 0 };
uint32_t counter = 0;
uint32_t nonce[3] = { 0 };
uint8_t *key8 = (uint8_t*)&key;
for (int i=0; i<32; i++) {
key8[i] = i;
}
uint8_t *nonce8 = (uint8_t*)&nonce;
nonce8[3] = 9;
nonce8[7] = 0x4a;
counter = 1;
chacha_init(state, key, counter, nonce);
for (int i=0; i<16; i++) {
printf("%08x ", state[i]);
if (i%4 == 3) puts("");
}
uint32_t out[16];
chacha_block(out, state);
puts("doing rounds");
for (int i=0; i<16; i++) {
printf("%08x ", out[i]);
if (i%4 == 3) puts("");
}
}
void test2(uint32_t counter, const uint8_t *plaintext_block, int block_len) {
uint32_t state[16] = { 0 };
uint32_t key[8] = { 0 };
//uint32_t counter = 0;
uint32_t nonce[3] = { 0 };
uint8_t *key8 = (uint8_t*)&key;
for (int i=0; i<32; i++) {
key8[i] = i;
}
uint8_t *nonce8 = (uint8_t*)&nonce;
//nonce8[3] = 9;
nonce8[7] = 0x4a;
//counter = 1;
chacha_init(state, key, counter, nonce);
#if 1
puts("initial");
for (int i=0; i<16; i++) {
printf("%08x ", state[i]);
if (i%4 == 3) puts("");
}
#endif
uint32_t out[16];
chacha_block(out, state);
puts("doing rounds");
for (int i=0; i<16; i++) {
printf("%08x ", out[i]);
if (i%4 == 3) puts("");
}
uint8_t *keystream8 = (uint8_t*)out;
uint8_t ciphertext[64];
for (int i=0; i<block_len; i++) {
ciphertext[i] = keystream8[i] ^ plaintext_block[i];
printf("%02x ", ciphertext[i]);
if (i % 16 == 15) puts("");
}
}
void chacha20_process_stream(const uint32_t key[8], const uint32_t nonce[3], const uint8_t *msg, int msglen, uint8_t *ciphertext) {
uint32_t keystream[16];
uint8_t *keystream8 = (uint8_t*)keystream;
for (int i=0; i < msglen; i++) {
if ((i % 64) == 0) {
uint32_t state[16];
chacha_init(state, key, (i/64)+1, nonce);
chacha_block(keystream, state);
}
ciphertext[i] = msg[i] ^ keystream8[(i%64)];
}
}
void test3() {
// test vector from https://datatracker.ietf.org/doc/html/rfc7539#section-2.2
const char *plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
int length = strlen(plaintext);
printf("\nlength %d\n", length);
const uint32_t key[8] = { 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c };
const uint32_t nonce[3] = { 0, 0x4a000000, 0 };
uint8_t ciphertext[114];
chacha20_process_stream(key, nonce, plaintext, length, ciphertext);
#if 0
puts("ciphertext");
for (int i=0; i<length; i++) {
printf("%02x ", ciphertext[i]);
if ((i % 16) == 15) puts("");
}
#endif
uint8_t decrypted[115];
chacha20_process_stream(key, nonce, ciphertext, 114, decrypted);
decrypted[114] = 0;
printf("out: %s\n", decrypted);
}
int main(int argc, char **argv) {
//test1();
//const char *plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
//test2(1, plaintext, 64);
//test2(2, plaintext+64, 50);
test3();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment