commit dc6bad5a121973d940ea9efbf34737644d65d0cd
parent c9f625de9349f34b63cf277ed7d08c4a10bb8b73
Author: MikoĊaj Lenczewski <mikolaj@lenczewski.org>
Date: Sun, 4 Oct 2026 15:24:53 +0100
Add initial chacha20-poly1305 implementation
Diffstat:
11 files changed, 339 insertions(+), 5 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1 +1,2 @@
bin/
+obj/
diff --git a/aes.pdf b/aes.pdf
Binary files differ.
diff --git a/build.sh b/build.sh
@@ -3,13 +3,29 @@
set -ex
CC="${CC:-clang}"
-FLAGS="-std=c11 -O0 -g3 -Wall -Wextra -Wno-unused-parameter -Wno-format"
+AS="${AS:-clang}"
+AR="${AR:-llvm-ar}"
+RANLIB="${RANLIBL:-llvm-ranlib}"
-mkdir -p bin
+TARGET="${TARGET:-$($CC -dumpmachine)}"
+ARCH="${TARGET%%-*}"
+CFLAGS="-std=c11 -O0 -g3 -Wall -Wextra -Wno-unused-parameter -Wno-format"
+ASFLAGS="-g3"
+
+mkdir -p bin obj
+
+# build tls library
+$CC -o obj/tls_common.o -c tls_common.c $CFLAGS
+$AS -o obj/tls_native.o -c tls_$ARCH.S $ASFLAGS
+$AR rcs bin/libtls.a obj/tls_common.o obj/tls_native.o
+$RANLIB bin/libtls.a
+
+# build tls tests
for src in test/*.c; do
- $CC -o bin/$(basename -s .c $src) $src $FLAGS -I.
+ $CC -o bin/$(basename -s .c $src) $src bin/libtls.a $CFLAGS -I. -D_GNU_SOURCE=1
done
-$CC -o bin/tls_client tls_client.c $FLAGS
-$CC -o bin/tls_server tls_server.c $FLAGS
+# build example program
+$CC -o bin/tls_client tls_client.c $CFLAGS
+$CC -o bin/tls_server tls_server.c $CFLAGS
diff --git a/chacha20.c b/chacha20.c
@@ -0,0 +1,114 @@
+#include "chacha20.h"
+
+#include <endian.h>
+#include <string.h>
+
+struct chacha20_ctx {
+ alignas(CHACHA20_ALIGN)
+ chacha20_word_t vs[CHACHA20_BLOCK_WORDS];
+};
+
+void
+_chacha20_init(struct chacha20_ctx *ctx,
+ unsigned char key[static CHACHA20_KEY_SIZE],
+ uint32_t ctr,
+ unsigned char iv[static CHACHA20_IV_SIZE])
+{
+ ctx->vs[0] = 0x61707865;
+ ctx->vs[1] = 0x3320646e;
+ ctx->vs[2] = 0x79622d32;
+ ctx->vs[3] = 0x6b206574;
+
+ memcpy(&ctx->vs[4], key, CHACHA20_KEY_SIZE);
+
+ ctx->vs[12] = ctr;
+
+ memcpy(&ctx->vs[13], iv, CHACHA20_IV_SIZE);
+}
+
+#define CHACHA20_ROTL(v, n, b) (((v) << (n)) | ((v) >> ((b) - (n))))
+
+#define CHACHA20_QUARTERROUND(a, b, c, d) \
+{ \
+ a += b; d ^= a; d = CHACHA20_ROTL(d, 16, 32); \
+ c += d; b ^= c; b = CHACHA20_ROTL(b, 12, 32); \
+ a += b; d ^= a; d = CHACHA20_ROTL(d, 8, 32); \
+ c += d; b ^= c; b = CHACHA20_ROTL(b, 7, 32); \
+}
+
+static void
+_chacha20_inner_block(struct chacha20_ctx *ctx)
+{
+ CHACHA20_QUARTERROUND(ctx->vs[0], ctx->vs[4], ctx->vs[8], ctx->vs[12]);
+ CHACHA20_QUARTERROUND(ctx->vs[1], ctx->vs[5], ctx->vs[9], ctx->vs[13]);
+ CHACHA20_QUARTERROUND(ctx->vs[2], ctx->vs[6], ctx->vs[10], ctx->vs[14]);
+ CHACHA20_QUARTERROUND(ctx->vs[3], ctx->vs[7], ctx->vs[11], ctx->vs[15]);
+
+ CHACHA20_QUARTERROUND(ctx->vs[0], ctx->vs[5], ctx->vs[10], ctx->vs[15]);
+ CHACHA20_QUARTERROUND(ctx->vs[1], ctx->vs[6], ctx->vs[11], ctx->vs[12]);
+ CHACHA20_QUARTERROUND(ctx->vs[2], ctx->vs[7], ctx->vs[8], ctx->vs[13]);
+ CHACHA20_QUARTERROUND(ctx->vs[3], ctx->vs[4], ctx->vs[9], ctx->vs[14]);
+}
+
+static void
+_chacha20_block(struct chacha20_ctx *ctx,
+ unsigned char key[static CHACHA20_KEY_SIZE],
+ uint32_t ctr,
+ unsigned char iv[static CHACHA20_IV_SIZE])
+{
+ _chacha20_init(ctx, key, ctr, iv);
+
+ struct chacha20_ctx save;
+ memcpy(&save, ctx, sizeof *ctx);
+
+ for (size_t i = 0; i < CHACHA20_ROUNDS; i++) {
+ _chacha20_inner_block(ctx);
+ }
+
+ for (size_t i = 0; i < CHACHA20_BLOCK_WORDS; i++) {
+ save.vs[i] += ctx->vs[i];
+ ctx->vs[i] = htole32(save.vs[i]);
+ }
+}
+
+static void
+_chacha20_round(unsigned char key[static CHACHA20_KEY_SIZE],
+ uint32_t ctr,
+ unsigned char iv[static CHACHA20_IV_SIZE],
+ void const *plaintext, size_t len,
+ void *ciphertext)
+{
+ struct chacha20_ctx keystream;
+ _chacha20_block(&keystream, key, ctr, iv);
+
+ unsigned char const *in = plaintext, *end = in + len;
+ unsigned char *state = (void *) keystream.vs;
+ unsigned char *out = ciphertext;
+
+ while (in < end) {
+ *out++ = *in++ ^ *state++;
+ }
+}
+
+void
+chacha20(unsigned char key[static CHACHA20_KEY_SIZE],
+ uint32_t ctr,
+ unsigned char iv[static CHACHA20_IV_SIZE],
+ void const *buf, size_t len,
+ void *out)
+{
+ size_t blocks = len / CHACHA20_BLOCK_SIZE;
+ size_t tail = len - (blocks * CHACHA20_BLOCK_SIZE);
+ unsigned char const *ptr = buf;
+ unsigned char *dst = out;
+
+ size_t i;
+ for (i = 0; i < blocks; i++) {
+ _chacha20_round(key, ctr + i, iv, ptr, CHACHA20_BLOCK_SIZE, dst);
+
+ ptr += CHACHA20_BLOCK_SIZE;
+ dst += CHACHA20_BLOCK_SIZE;
+ }
+
+ _chacha20_round(key, ctr + i, iv, ptr, tail, dst);
+}
diff --git a/chacha20.h b/chacha20.h
@@ -0,0 +1,37 @@
+// chacha20-poly1305: https://www.rfc-editor.org/info/rfc8439/
+
+#ifndef CHACHA20_H
+#define CHACHA20_H
+
+#include <limits.h>
+#include <stdalign.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define CHACHA20_ALIGN 64
+#define CHACHA20_ROUNDS 10
+
+typedef uint32_t chacha20_word_t;
+
+#define CHACHA20_WORD_BITS 32
+#define CHACHA20_BLOCK_BITS 512
+#define CHACHA20_KEY_BITS 256
+#define CHACHA20_IV_BITS 96
+
+#define CHACHA20_BLOCK_WORDS (CHACHA20_BLOCK_BITS / CHACHA20_WORD_BITS)
+#define CHACHA20_BLOCK_SIZE (CHACHA20_BLOCK_BITS / CHAR_BIT)
+
+#define CHACHA20_KEY_WORDS (CHACHA20_KEY_BITS / CHACHA20_WORD_BITS)
+#define CHACHA20_KEY_SIZE (CHACHA20_KEY_BITS / CHAR_BIT)
+
+#define CHACHA20_IV_WORDS (CHACHA20_IV_BITS / CHACHA20_WORD_BITS)
+#define CHACHA20_IV_SIZE (CHACHA20_IV_BITS / CHAR_BIT)
+
+void
+chacha20(unsigned char key[static CHACHA20_KEY_SIZE],
+ uint32_t ctr,
+ unsigned char iv[static CHACHA20_IV_SIZE],
+ void const *buf, size_t len,
+ void *out);
+
+#endif /* CHACHA20_H */
diff --git a/hmac.c b/hmac.c
@@ -1,6 +1,7 @@
#include "hmac.h"
#include <assert.h>
+#include <string.h>
void
hmac_gen_iv_block(struct hmac_ctx *ctx,
diff --git a/poly1305.c b/poly1305.c
@@ -0,0 +1,38 @@
+#include "poly1305.h"
+
+static void
+_poly1305_clamp(unsigned char r[static 16])
+{
+ // r &= 0x0ffffffc 0ffffffc 0ffffffc 0fffffff
+ // r &= 0x0ffffffc 0x0ffffffc 0x0ffffffc 0x0fffffff
+ //
+ uint32_t *vs = (void *) r;
+}
+
+void
+poly1305_init(struct poly1305_ctx *ctx,
+ unsigned char const key[static POLY1305_KEY_SIZE])
+{
+}
+
+void
+poly1305_push(struct poly1305_ctx *ctx, void const *buf, size_t len)
+{
+}
+
+void
+poly1305_dgst(struct poly1305_ctx *ctx,
+ unsigned char digest[static POLY1305_DIGEST_SIZE])
+{
+}
+
+void
+poly1305(unsigned char const key[static POLY1305_KEY_SIZE],
+ void const *buf, size_t len,
+ unsigned char digest[static POLY1305_DIGEST_SIZE])
+{
+ struct poly1305_ctx ctx;
+ poly1305_init(&ctx, key);
+ poly1305_push(&ctx, buf, len);
+ poly1305_dgst(&ctx, digest);
+}
diff --git a/poly1305.h b/poly1305.h
@@ -0,0 +1,43 @@
+// chacha20-poly1305: https://www.rfc-editor.org/info/rfc8439/
+
+#ifndef POLY1305_H
+#define POLY1305_H
+
+#include <stdalign.h>
+#include <stdint.h>
+
+#define POLY1305_ALIGN 64
+
+#define POLY1305_KEY_BITS 256
+#define POLY1305_KEY_SIZE (POLY1305_KEY_BITS / CHAR_BIT)
+
+#define POLY1305_BLOCK_BITS 128
+#define POLY1305_BLOCK_SIZE (POLY1305_BLOCK_BITS / CHAR_BIT)
+
+#define POLY1305_DIGEST_BITS 128
+#define POLY1305_DIGEST_SIZE (POLY1305_DIGEST_BITS / CHAR_BIT)
+
+struct poly1305_ctx {
+ alignas(POLY1305_ALIGN)
+ unsigned char r[16];
+ unsigned char s[16];
+ unsigned char a[17];
+};
+
+void
+poly1305_init(struct poly1305_ctx *ctx,
+ unsigned char const key[static POLY1305_KEY_SIZE]);
+
+void
+poly1305_push(struct poly1305_ctx *ctx, void const *buf, size_t len);
+
+void
+poly1305_dgst(struct poly1305_ctx *ctx,
+ unsigned char digest[static POLY1305_DIGEST_SIZE]);
+
+void
+poly1305(unsigned char const key[static POLY1305_KEY_SIZE],
+ void const *buf, size_t len,
+ unsigned char digest[static POLY1305_DIGEST_SIZE]);
+
+#endif /* POLY1305_H */
diff --git a/test/test-chacha20.c b/test/test-chacha20.c
@@ -0,0 +1,72 @@
+#include "chacha20.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main(void)
+{
+ char msg[] = "Ladies and Gentlemen of the class of '99: "
+ "If I could offer you only one tip for the future, "
+ "sunscreen would be it.";
+
+ size_t len = strlen(msg);
+ unsigned char *enc = malloc(len);
+
+ uint8_t key[CHACHA20_KEY_SIZE] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ };
+
+ uint32_t ctr = 1;
+
+ uint8_t iv[CHACHA20_IV_SIZE] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x4a,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ chacha20(key, ctr, iv, msg, len, enc);
+
+ uint8_t expected[] = {
+ 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80,
+ 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
+ 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2,
+ 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
+ 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab,
+ 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
+ 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab,
+ 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
+ 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61,
+ 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
+ 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06,
+ 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
+ 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6,
+ 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
+ 0x87, 0x4d,
+ };
+
+ assert(sizeof expected == len);
+
+ printf("msg: \"%.*s\" (%lu bytes)\n", sizeof msg, msg, strlen(msg));
+
+ printf("enc: ");
+ for (size_t i = 0; i < len; i++) {
+ printf("%.2x", enc[i]);
+ }
+ printf("\n");
+
+ printf("exp: ");
+ for (size_t i = 0; i < len; i++) {
+ printf("%.2x", expected[i]);
+ }
+ printf("\n");
+
+ return memcmp(expected, enc, len);
+}
+
+#include "chacha20.c"
diff --git a/tls_common.c b/tls_common.c
@@ -0,0 +1,12 @@
+#define _GNU_SOURCE 1
+
+#include "hkdf.c"
+#include "hmac.c"
+#include "hmac-sha2.c"
+#include "sha.c"
+
+#include "poly1305.c"
+
+// TODO: replace with native assembly versions, using the correct SIMD
+// implementation based on isa extensions supported at runtime
+#include "chacha20.c"
diff --git a/tls_x86_64.S b/tls_x86_64.S