⬢ DragonFlyBSD Kernel Audit
DF-3388 / chachapoly_user.c
← back to finding ↓ download raw
/*
 * chacha20poly1305 buffer-only AEAD, userspace port of
 * sys/crypto/chachapoly.c (buffer functions only, no mbuf).
 */
#include <sys/types.h>
#include <sys/endian.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <crypto/chacha20/chacha.h>
#include <crypto/poly1305/poly1305.h>

#define CHACHA20POLY1305_KEY_SIZE	32
#define CHACHA20POLY1305_AUTHTAG_SIZE	16
#define CHACHA20POLY1305_NONCE_SIZE	12

struct cp_ctx {
	struct chacha_ctx	chacha;
	poly1305_state		poly;
	uint8_t			tag[16];
	size_t			ad_len;
	size_t			data_len;
	int			flags;
};

#define PADDING_SIZE		16
#define PADDING_LEN(len) \
	((PADDING_SIZE - ((len) & (PADDING_SIZE - 1))) & (PADDING_SIZE - 1))
static const uint8_t pad0[PADDING_SIZE] = { 0 };

static void
cp_init(struct cp_ctx *ctx, int enc, const uint8_t nonce[12], const uint8_t key[32])
{
	uint8_t poly_key[32];
	uint8_t nonce64[CHACHA_NONCELEN], counter64[CHACHA_CTRLEN];

	bzero(ctx, sizeof(*ctx));
	bzero(poly_key, sizeof(poly_key));
	bzero(nonce64, sizeof(nonce64));
	bzero(counter64, sizeof(counter64));

	memcpy(counter64 + 4, nonce, 4);
	memcpy(nonce64, nonce + 4, 8);

	chacha_keysetup(&ctx->chacha, key, 32 * 8);
	chacha_ivsetup(&ctx->chacha, nonce64, counter64);
	chacha_encrypt_bytes(&ctx->chacha, poly_key, poly_key, sizeof(poly_key));
	poly1305_init(&ctx->poly, poly_key);
	ctx->flags = 2 | (enc ? 1 : 0);
}

static void
cp_update_ad(struct cp_ctx *ctx, const uint8_t *ad, size_t ad_len)
{
	if (ad_len == 0 || ad == NULL)
		return;
	poly1305_update(&ctx->poly, ad, ad_len);
	ctx->ad_len += ad_len;
}

static void
cp_final(struct cp_ctx *ctx, uint8_t *dst, const uint8_t *src, size_t len)
{
	uint64_t lens[2];
	uint8_t tag[16];

	if (ctx->ad_len > 0 && (ctx->flags & 4) == 0) {
		poly1305_update(&ctx->poly, pad0, PADDING_LEN(ctx->ad_len));
		ctx->flags |= 4;
	}
	if (len > 0) {
		if (ctx->flags & 1) {
			chacha_encrypt_bytes(&ctx->chacha, src, dst, len);
			poly1305_update(&ctx->poly, dst, len);
		} else {
			poly1305_update(&ctx->poly, src, len);
			chacha_encrypt_bytes(&ctx->chacha, src, dst, len);
		}
		poly1305_update(&ctx->poly, pad0, PADDING_LEN(len));
		ctx->data_len += len;
	}
	lens[0] = htole64(ctx->ad_len);
	lens[1] = htole64(ctx->data_len);
	poly1305_update(&ctx->poly, (uint8_t *)lens, sizeof(lens));
	poly1305_finish(&ctx->poly, tag);
	memcpy(ctx->tag, tag, 16);
}

void
cp_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len,
	   const uint8_t *ad, size_t ad_len,
	   const uint8_t nonce[12], const uint8_t key[32])
{
	struct cp_ctx ctx;
	size_t len;

	cp_init(&ctx, 1, nonce, key);
	cp_update_ad(&ctx, ad, ad_len);
	len = rounddown2(src_len, CHACHA_BLOCKLEN);
	if (len) {
		/* complete blocks */
		poly1305_update(&ctx.poly, pad0, PADDING_LEN(ctx.ad_len));
		ctx.flags |= 4;
		chacha_encrypt_bytes(&ctx.chacha, src, dst, len);
		poly1305_update(&ctx.poly, dst, len);
		ctx.data_len += len;
	}
	cp_final(&ctx, dst + len, src + len, src_len - len);
	memcpy(dst + src_len, ctx.tag, 16);
}