Skip to content

Instantly share code, notes, and snippets.

@orlp
Created October 22, 2013 23:50
Show Gist options
  • Save orlp/7110176 to your computer and use it in GitHub Desktop.
Save orlp/7110176 to your computer and use it in GitHub Desktop.
#include <stdint.h>
#include <string.h>
#define ROTL32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define LOAD_LE32(x) \
(((x)[0] << 0) | \
((x)[1] << 8) | \
((x)[2] << 16) | \
((x)[3] << 24))
#define STORE_LE32(x, v) \
(x)[0] = (v) >> 0; \
(x)[1] = (v) >> 8; \
(x)[2] = (v) >> 16; \
(x)[3] = (v) >> 24
#define QUARTERROUND(a, b, c, d) \
x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = ROTL32(x[d], 16); \
x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = ROTL32(x[b], 12); \
x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = ROTL32(x[d], 8); \
x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = ROTL32(x[b], 7)
static void chacha_core(unsigned char *out,
const uint32_t keysetup[16], int rounds) {
uint32_t x[16];
int i;
for (i = 0; i < 16; ++i) {
x[i] = keysetup[i];
}
for (i = 0; i < rounds; 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) {
STORE_LE32(out + 4*i, x[i] + keysetup[i]);
}
}
static void chacha_keysetup(uint32_t keysetup[16],
const unsigned char *nonce,
const unsigned char *key, int keybits) {
static const unsigned char sigma[16] = "expand 32-byte k";
static const unsigned char tau[16] = "expand 16-byte k";
const unsigned char *constant;
int i;
for (i = 0; i < 4; ++i) {
keysetup[4+i] = LOAD_LE32(key + 4*i);
}
if (keybits == 256) {
key += 16;
constant = sigma;
} else {
constant = tau;
}
for (i = 0; i < 4; ++i) {
keysetup[8+i] = LOAD_LE32(key + 4*i);
}
for (i = 0; i < 4; ++i) {
keysetup[i] = LOAD_LE32(constant + 4*i);
}
keysetup[12] = 0;
keysetup[13] = 0;
keysetup[14] = LOAD_LE32(nonce + 0);
keysetup[15] = LOAD_LE32(nonce + 4);
}
static void chacha_stream(unsigned char *out, int outlen,
const unsigned char *nonce,
const unsigned char *key, int keybits, int rounds) {
uint32_t keysetup[16];
chacha_keysetup(keysetup, nonce, key, keybits);
while (outlen > 0) {
if (outlen >= 64) {
chacha_core(out, keysetup, rounds);
} else {
unsigned char core_output[64];
chacha_core(core_output, keysetup, rounds);
memcpy(out, core_output, outlen);
}
out += 64;
outlen -= 64;
keysetup[12]++;
keysetup[13] += (keysetup[12] == 0);
}
}
void chacha8_stream(unsigned char *out, int outlen,
const unsigned char *nonce,
const unsigned char *key, int keybits) {
chacha_stream(out, outlen, nonce, key, keybits, 8);
}
void chacha12_stream(unsigned char *out, int outlen,
const unsigned char *nonce,
const unsigned char *key, int keybits) {
chacha_stream(out, outlen, nonce, key, keybits, 12);
}
void chacha20_stream(unsigned char *out, int outlen,
const unsigned char *nonce,
const unsigned char *key, int keybits) {
chacha_stream(out, outlen, nonce, key, keybits, 20);
}
void chacha8_encrypt(unsigned char *out, const unsigned char *in, int inlen,
const unsigned char *nonce,
const unsigned char *key, int keybits) {
int i;
chacha_stream(out, inlen, nonce, key, keybits, 8);
for (i = 0; i < inlen; ++i) out[i] ^= in[i];
}
void chacha12_encrypt(unsigned char *out, const unsigned char *in, int inlen,
const unsigned char *nonce,
const unsigned char *key, int keybits) {
int i;
chacha_stream(out, inlen, nonce, key, keybits, 12);
for (i = 0; i < inlen; ++i) out[i] ^= in[i];
}
void chacha20_encrypt(unsigned char *out, const unsigned char *in, int inlen,
const unsigned char *nonce,
const unsigned char *key, int keybits) {
int i;
chacha_stream(out, inlen, nonce, key, keybits, 20);
for (i = 0; i < inlen; ++i) out[i] ^= in[i];
}
#undef ROTL32
#undef LOAD_LE32
#undef STORE_LE32
#undef QUARTERROUND
#ifndef CRYPTRAND_H
#define CRYPTRAND_H
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#else
#include <stdio.h>
#endif
inline int cryptrand(unsigned char *out, int outlen) {
#ifdef _WIN32
HCRYPTPROV prov;
if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
return 1;
}
if (!CryptGenRandom(prov, outlen, seed)) {
CryptReleaseContext(prov, 0);
return 1;
}
CryptReleaseContext(prov, 0);
#else
FILE *f = fopen("/dev/urandom", "rb");
if (f == NULL) {
return 1;
}
fread(seed, 1, outlen, f);
fclose(f);
#endif
return 0;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment