/* This file is part of limb https://lila.oss/limb
* Copyright (C) 2023 Olivier Brunel jjk@jjacky.com */
/* SPDX-License-Identifier: GPL-2.0-only */
#include <string.h>
#include <limb/chacha20.h>
#include <limb/memxor.h>
#include <limb/u32.h>
#define ROUNDS 20
#define LR(a,b) a = (((a) << (b)) | ((a) >> ((-(b) & 31))))
#define QUARTERROUND(out,a,b,c,d) \
out[a] += out[b]; out[d] ^= out[a]; LR(out[d], 16); \
out[c] += out[d]; out[b] ^= out[c]; LR(out[b], 12); \
out[a] += out[b]; out[d] ^= out[a]; LR(out[d], 8); \
out[c] += out[d]; out[b] ^= out[c]; LR(out[b], 7)
static void
block(u32 out[CHACHA_U32_BLOCK_SIZE], const u32 in[CHACHA_U32_BLOCK_SIZE])
{
memcpy(out, in, CHACHA_BLOCK_SIZE);
for (int i = 0; i < ROUNDS; i += 2) {
QUARTERROUND(out, 0, 4, 8, 12);
QUARTERROUND(out, 1, 5, 9, 13);
QUARTERROUND(out, 2, 6, 10, 14);
QUARTERROUND(out, 3, 7, 11, 15);
QUARTERROUND(out, 0, 5, 10, 15);
QUARTERROUND(out, 1, 6, 11, 12);
QUARTERROUND(out, 2, 7, 8, 13);
QUARTERROUND(out, 3, 4, 9, 14);
}
for (int i = 0; i < CHACHA_U32_BLOCK_SIZE; ++i)
out[i] += in[i];
u32pa_le(out, CHACHA_U32_BLOCK_SIZE);
}
void
chacha20_crypt(void *dst_, const void *msg_, size_t mlen, void *ctx_)
{
chacha20_ctx *ctx = ctx_;
u8 *dst = dst_;
const u8 *msg = msg_;
u32 output[CHACHA_U32_BLOCK_SIZE];
if (!mlen) return;
for (;;) {
size_t l = CHACHA_BLOCK_SIZE - ctx->part;
block(output, ctx->in);
if (mlen <= l) {
memmove(dst, msg, mlen);
memxor(dst, (u8 *) output + ctx->part, mlen);
ctx->part = (ctx->part + mlen) % CHACHA_BLOCK_SIZE;
if (!ctx->part)
if (!++ctx->in[12]) ++ctx->in[13];
return;
}
memmove(dst, msg, l);
memxor(dst, (u8 *) output + ctx->part, l);
mlen -= l;
dst += l;
msg += l;
if (!++ctx->in[12]) ++ctx->in[13];
ctx->part = 0;
}
}