#include "chacha.h" #define ROTL32(v, n) (((v) << (n)) | ((v) >> (32 - (n)))) #define ROTATE(v, c) ROTL32((v), (c)) #define XOR(v, w) ((v) ^ (w)) #define PLUS(x, y) ((x) + (y)) #define U32TO8_LITTLE(p, v) \ { (p)[0] = ((v) ) & 0xff; (p)[1] = ((v) >> 8) & 0xff; \ (p)[2] = ((v) >> 16) & 0xff; (p)[3] = ((v) >> 24) & 0xff; } #define U8TO32_LITTLE(p) \ (((u32)((p)[0]) ) | ((u32)((p)[1]) << 8) | \ ((u32)((p)[2]) << 16) | ((u32)((p)[3]) << 24) ) #define QUARTERROUND(a,b,c,d) \ x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]),16); \ x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]),12); \ x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]), 8); \ x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]), 7); // sigma contains the ChaCha constants, which happen to be an ASCII string. static const uint8_t sigma[16] = { 'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k' }; void chacha20_encrypt(const u32 input[16], unsigned char output[64], int num_rounds) { u32 x[16]; int i; memcpy(x, input, sizeof(u32) * 16); for (i = num_rounds; i > 0; i -= 2) { QUARTERROUND( 0, 4, 8,12) QUARTERROUND( 1, 5, 9,13) QUARTERROUND( 2, 6,10,14) QUARTERROUND( 3, 7,11,15) QUARTERROUND( 0, 5,10,15) QUARTERROUND( 1, 6,11,12) QUARTERROUND( 2, 7, 8,13) QUARTERROUND( 3, 4, 9,14) } for (i = 0; i < 16; ++i) { x[i] = PLUS(x[i], input[i]); } for (i = 0; i < 16; ++i) { U32TO8_LITTLE(output + 4 * i, x[i]); } } void chacha20_encrypt_ctr(const uint8_t *in, uint8_t *out, size_t in_len, const uint8_t key[CHACHA_KEY_SIZE], const uint8_t nonce[CHACHA_NONCE_SIZE], uint32_t counter) { uint32_t input[16]; uint8_t buf[64]; size_t todo, i; input[0] = U8TO32_LITTLE(sigma + 0); input[1] = U8TO32_LITTLE(sigma + 4); input[2] = U8TO32_LITTLE(sigma + 8); input[3] = U8TO32_LITTLE(sigma + 12); input[4] = U8TO32_LITTLE(key + 0); input[5] = U8TO32_LITTLE(key + 4); input[6] = U8TO32_LITTLE(key + 8); input[7] = U8TO32_LITTLE(key + 12); input[8] = U8TO32_LITTLE(key + 16); input[9] = U8TO32_LITTLE(key + 20); input[10] = U8TO32_LITTLE(key + 24); input[11] = U8TO32_LITTLE(key + 28); input[12] = counter; input[13] = U8TO32_LITTLE(nonce + 0); input[14] = U8TO32_LITTLE(nonce + 4); input[15] = U8TO32_LITTLE(nonce + 8); while (in_len > 0) { todo = sizeof(buf); if (in_len < todo) { todo = in_len; } chacha20_encrypt(input, buf, 20); for (i = 0; i < todo; i++) { out[i] = in[i] ^ buf[i]; } out += todo; in += todo; in_len -= todo; input[12]++; } }