diff options
Diffstat (limited to 'src/libcryptobox/chacha20/ref.c')
-rw-r--r-- | src/libcryptobox/chacha20/ref.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/src/libcryptobox/chacha20/ref.c b/src/libcryptobox/chacha20/ref.c new file mode 100644 index 0000000..ee646db --- /dev/null +++ b/src/libcryptobox/chacha20/ref.c @@ -0,0 +1,272 @@ +#include "config.h" +#include "chacha.h" +#include "cryptobox.h" + +#if defined(HAVE_INT32) +typedef uint32_t chacha_int32; +#else +typedef guint32 chacha_int32; +#endif + +/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ +static chacha_int32 +U8TO32(const unsigned char *p) +{ + return (((chacha_int32) (p[0])) | + ((chacha_int32) (p[1]) << 8) | + ((chacha_int32) (p[2]) << 16) | + ((chacha_int32) (p[3]) << 24)); +} + +/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ +static void +U32TO8(unsigned char *p, chacha_int32 v) +{ + p[0] = (v) &0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +/* 32 bit left rotate */ +static chacha_int32 +ROTL32(chacha_int32 x, int k) +{ + return ((x << k) | (x >> (32 - k))) & 0xffffffff; +} + +/* "expand 32-byte k", as 4 little endian 32-bit unsigned integers */ +static const chacha_int32 chacha_constants[4] = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574}; + +void chacha_blocks_ref(chacha_state_internal *state, const unsigned char *in, unsigned char *out, size_t bytes) +{ + chacha_int32 x[16], j[12]; + chacha_int32 t; + unsigned char *ctarget = out, tmp[64]; + size_t i, r; + + if (!bytes) return; + + j[0] = U8TO32(state->s + 0); + j[1] = U8TO32(state->s + 4); + j[2] = U8TO32(state->s + 8); + j[3] = U8TO32(state->s + 12); + j[4] = U8TO32(state->s + 16); + j[5] = U8TO32(state->s + 20); + j[6] = U8TO32(state->s + 24); + j[7] = U8TO32(state->s + 28); + j[8] = U8TO32(state->s + 32); + j[9] = U8TO32(state->s + 36); + j[10] = U8TO32(state->s + 40); + j[11] = U8TO32(state->s + 44); + + r = state->rounds; + + for (;;) { + if (bytes < 64) { + if (in) { + for (i = 0; i < bytes; i++) tmp[i] = in[i]; + in = tmp; + } + ctarget = out; + out = tmp; + } + + x[0] = chacha_constants[0]; + x[1] = chacha_constants[1]; + x[2] = chacha_constants[2]; + x[3] = chacha_constants[3]; + x[4] = j[0]; + x[5] = j[1]; + x[6] = j[2]; + x[7] = j[3]; + x[8] = j[4]; + x[9] = j[5]; + x[10] = j[6]; + x[11] = j[7]; + x[12] = j[8]; + x[13] = j[9]; + x[14] = j[10]; + x[15] = j[11]; + +#define quarter(a, b, c, d) \ + a += b; \ + t = d ^ a; \ + d = ROTL32(t, 16); \ + c += d; \ + t = b ^ c; \ + b = ROTL32(t, 12); \ + a += b; \ + t = d ^ a; \ + d = ROTL32(t, 8); \ + c += d; \ + t = b ^ c; \ + b = ROTL32(t, 7); + +#define doubleround() \ + quarter(x[0], x[4], x[8], x[12]) \ + quarter(x[1], x[5], x[9], x[13]) \ + quarter(x[2], x[6], x[10], x[14]) \ + quarter(x[3], x[7], x[11], x[15]) \ + quarter(x[0], x[5], x[10], x[15]) \ + quarter(x[1], x[6], x[11], x[12]) \ + quarter(x[2], x[7], x[8], x[13]) \ + quarter(x[3], x[4], x[9], x[14]) + + i = r; + do { + doubleround() + i -= 2; + } while (i); + + x[0] += chacha_constants[0]; + x[1] += chacha_constants[1]; + x[2] += chacha_constants[2]; + x[3] += chacha_constants[3]; + x[4] += j[0]; + x[5] += j[1]; + x[6] += j[2]; + x[7] += j[3]; + x[8] += j[4]; + x[9] += j[5]; + x[10] += j[6]; + x[11] += j[7]; + x[12] += j[8]; + x[13] += j[9]; + x[14] += j[10]; + x[15] += j[11]; + + if (in) { + U32TO8(out + 0, x[0] ^ U8TO32(in + 0)); + U32TO8(out + 4, x[1] ^ U8TO32(in + 4)); + U32TO8(out + 8, x[2] ^ U8TO32(in + 8)); + U32TO8(out + 12, x[3] ^ U8TO32(in + 12)); + U32TO8(out + 16, x[4] ^ U8TO32(in + 16)); + U32TO8(out + 20, x[5] ^ U8TO32(in + 20)); + U32TO8(out + 24, x[6] ^ U8TO32(in + 24)); + U32TO8(out + 28, x[7] ^ U8TO32(in + 28)); + U32TO8(out + 32, x[8] ^ U8TO32(in + 32)); + U32TO8(out + 36, x[9] ^ U8TO32(in + 36)); + U32TO8(out + 40, x[10] ^ U8TO32(in + 40)); + U32TO8(out + 44, x[11] ^ U8TO32(in + 44)); + U32TO8(out + 48, x[12] ^ U8TO32(in + 48)); + U32TO8(out + 52, x[13] ^ U8TO32(in + 52)); + U32TO8(out + 56, x[14] ^ U8TO32(in + 56)); + U32TO8(out + 60, x[15] ^ U8TO32(in + 60)); + in += 64; + } + else { + U32TO8(out + 0, x[0]); + U32TO8(out + 4, x[1]); + U32TO8(out + 8, x[2]); + U32TO8(out + 12, x[3]); + U32TO8(out + 16, x[4]); + U32TO8(out + 20, x[5]); + U32TO8(out + 24, x[6]); + U32TO8(out + 28, x[7]); + U32TO8(out + 32, x[8]); + U32TO8(out + 36, x[9]); + U32TO8(out + 40, x[10]); + U32TO8(out + 44, x[11]); + U32TO8(out + 48, x[12]); + U32TO8(out + 52, x[13]); + U32TO8(out + 56, x[14]); + U32TO8(out + 60, x[15]); + } + + /* increment the 64 bit counter, split in to two 32 bit halves */ + j[8]++; + if (!j[8]) + j[9]++; + + if (bytes <= 64) { + if (bytes < 64) + for (i = 0; i < bytes; i++) ctarget[i] = out[i]; + + /* store the counter back to the state */ + U32TO8(state->s + 32, j[8]); + U32TO8(state->s + 36, j[9]); + goto cleanup; + } + bytes -= 64; + out += 64; + } + +cleanup: + rspamd_explicit_memzero(j, sizeof(j)); +} + +void hchacha_ref(const unsigned char key[32], const unsigned char iv[16], unsigned char out[32], size_t rounds) +{ + chacha_int32 x[16]; + chacha_int32 t; + + x[0] = chacha_constants[0]; + x[1] = chacha_constants[1]; + x[2] = chacha_constants[2]; + x[3] = chacha_constants[3]; + x[4] = U8TO32(key + 0); + x[5] = U8TO32(key + 4); + x[6] = U8TO32(key + 8); + x[7] = U8TO32(key + 12); + x[8] = U8TO32(key + 16); + x[9] = U8TO32(key + 20); + x[10] = U8TO32(key + 24); + x[11] = U8TO32(key + 28); + x[12] = U8TO32(iv + 0); + x[13] = U8TO32(iv + 4); + x[14] = U8TO32(iv + 8); + x[15] = U8TO32(iv + 12); + + do { + doubleround() + rounds -= 2; + } while (rounds); + + /* indices for the chacha constant */ + U32TO8(out + 0, x[0]); + U32TO8(out + 4, x[1]); + U32TO8(out + 8, x[2]); + U32TO8(out + 12, x[3]); + + /* indices for the iv */ + U32TO8(out + 16, x[12]); + U32TO8(out + 20, x[13]); + U32TO8(out + 24, x[14]); + U32TO8(out + 28, x[15]); +} + +void chacha_clear_state_ref(chacha_state_internal *state) +{ + rspamd_explicit_memzero(state, 48); +} + +void chacha_ref(const chacha_key *key, const chacha_iv *iv, const unsigned char *in, unsigned char *out, size_t inlen, size_t rounds) +{ + chacha_state_internal state; + size_t i; + for (i = 0; i < 32; i++) + state.s[i + 0] = key->b[i]; + for (i = 0; i < 8; i++) + state.s[i + 32] = 0; + for (i = 0; i < 8; i++) + state.s[i + 40] = iv->b[i]; + state.rounds = rounds; + chacha_blocks_ref(&state, in, out, inlen); + chacha_clear_state_ref(&state); +} + +void xchacha_ref(const chacha_key *key, const chacha_iv24 *iv, const unsigned char *in, unsigned char *out, size_t inlen, size_t rounds) +{ + chacha_state_internal state; + size_t i; + hchacha_ref(key->b, iv->b, &state.s[0], rounds); + for (i = 0; i < 8; i++) + state.s[i + 32] = 0; + for (i = 0; i < 8; i++) + state.s[i + 40] = iv->b[i + 16]; + state.rounds = rounds; + chacha_blocks_ref(&state, in, out, inlen); + chacha_clear_state_ref(&state); +} |