Welcome to little lamb

Code » limb » commit 4ba308e

Add chacha20.h & related fucntions

author Olivier Brunel
2023-04-07 16:06:26 UTC
committer Olivier Brunel
2023-05-20 18:06:36 UTC
parent b6e9da1b72f38ee56dc15f035355f18baa653f60

Add chacha20.h & related fucntions

Implements the ChaCha20 cipher for fast & secure encryption/decryption.

src/doc/chacha20.h.0.md +38 -0
src/doc/chacha20.h/chacha20_init.3.md +55 -0
src/liblimb/chacha20.h/chacha20.c +14 -0
src/liblimb/chacha20.h/chacha20_clear.c +12 -0
src/liblimb/chacha20.h/chacha20_crypt.c +69 -0
src/liblimb/chacha20.h/chacha20_init.c +25 -0
src/liblimb/include/limb/chacha20.h +27 -0

diff --git a/src/doc/chacha20.h.0.md b/src/doc/chacha20.h.0.md
new file mode 100644
index 0000000..ebf9f63
--- /dev/null
+++ b/src/doc/chacha20.h.0.md
@@ -0,0 +1,38 @@
+% limb manual
+% chacha20.h(0)
+
+# NAME
+
+chacha20.h - ChaCha20 encryption
+
+# SYNOPSIS
+
+    #include <limb/chacha20.h>
+
+# DESCRIPTION
+
+This header defines the required function to encrypt/decrypt messages using the
+ChaCha20 cipher.
+
+## Types
+
+The following types are defined :
+
+: *chacha20_ctx*
+:: An opaque structure to be given to the functions below.
+
+## Functions
+
+The following functions are defined :
+
+: [chacha20_init](3)
+:: To initialize a chacha20 context.
+
+: [chacha20_encrypt](3)
+:: To encrypt/decrypt a message.
+
+: [chacha20_clear](3)
+:: To clear a chacha20 context.
+
+: [chacha20](3)
+:: Convenience function to encrypt/decrypt a message at once.
diff --git a/src/doc/chacha20.h/chacha20_init.3.md b/src/doc/chacha20.h/chacha20_init.3.md
new file mode 100644
index 0000000..64f9e6f
--- /dev/null
+++ b/src/doc/chacha20.h/chacha20_init.3.md
@@ -0,0 +1,55 @@
+% limb manual
+% chacha20_init(3)
+
+# NAME
+
+chacha20\_init, chacha20\_encrypt, chacha20 - encrypt/decrypt a message using
+ChaCha20 cipher
+
+# SYNOPSIS
+
+    #include <limb/chacha20.h>
+
+```pre hl
+void chacha20_init(const void *<em>key</em>, const void *<em>nonce</em>, void *<em>ctx</em>)
+void chacha20_crypt(void *<em>dst</em>, const void *<em>msg</em>, size_t <em>mlen</em>, void *<em>ctx</em>)
+
+void chacha20_clear(void *<em>ctx</em>)
+void chacha20(void *<em>dst</em>, const void *<em>key</em>, const void *<em>nonce</em>, const void *<em>msg</em>, size_t <em>mlen</em>)
+```
+
+# DESCRIPTION
+
+The `chacha20_init`() function initializes the given chacha20 context `ctx` to
+encrypt/decrypt a message using the ChaCha20 cipher.
+
+This implementation is conform to [RFC 8439][rfc8439], as such the key pointed
+to by `key` must be 256bit/32 bytes long, and the nonce pointed to by `nonce`
+must be 96bit/12 bytes long.
+
+However, it also conforms to the [original description][chacha20] by Daniel J.
+Bernstein, as long as the given nonce has its first 32bit/4 bytes set to zero.
+
+In the former case, the message length shall not be more than 2^38 bytes. In
+the later case, the message length shall not be more than 2^70 bytes.
+
+[rfc8439] (https://datatracker.ietf.org/doc/html/rfc8439)
+[chacha20] (https://cr.yp.to/chacha/chacha-20080128.pdf)
+
+
+The `chacha20_crypt`() function processed the `mlen` bytes pointed to by `msg`,
+encrypting or decrypting them (it's the same operation : plain text will be
+encrypted, encrypted data will be decrypted back into plain text, assuming the
+same `key` and `nonce` are used of course.) into the memory pointed to by `dst`
+(which must be at least `mlen` bytes long).
+
+It is possible to use the same memory area for both `msg` and `dst`, and have it
+processed in-place. The function can be called as many times as needed.
+
+The `chacha20_clear`() function simply clears the memory behind the `ctx`
+context. In order to process another message, one can simply call
+`chacha20_init`() on a previously used context without the need to clear it
+first.
+
+The `chacha20`() function can be used as convenience if the entire message can
+be stored in a continuous memory area, to encrypt/decrypt it in a single call.
diff --git a/src/liblimb/chacha20.h/chacha20.c b/src/liblimb/chacha20.h/chacha20.c
new file mode 100644
index 0000000..af16628
--- /dev/null
+++ b/src/liblimb/chacha20.h/chacha20.c
@@ -0,0 +1,14 @@
+/* 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 <limb/chacha20.h>
+
+void
+chacha20(void *dst, const void *key, const void *nonce, const void *msg, size_t mlen)
+{
+    chacha20_ctx ctx;
+
+    chacha20_init(key, nonce, &ctx);
+    chacha20_crypt(dst, msg, mlen, &ctx);
+    chacha20_clear(&ctx);
+}
diff --git a/src/liblimb/chacha20.h/chacha20_clear.c b/src/liblimb/chacha20.h/chacha20_clear.c
new file mode 100644
index 0000000..8935203
--- /dev/null
+++ b/src/liblimb/chacha20.h/chacha20_clear.c
@@ -0,0 +1,12 @@
+/* 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 <limb/bytestr.h>
+#include <limb/chacha20.h>
+
+void
+chacha20_clear(void *ctx_)
+{
+    chacha20_ctx *ctx = ctx_;
+    byte_zero(ctx->in, CHACHA_BLOCK_SIZE);
+}
diff --git a/src/liblimb/chacha20.h/chacha20_crypt.c b/src/liblimb/chacha20.h/chacha20_crypt.c
new file mode 100644
index 0000000..6ca4c2e
--- /dev/null
+++ b/src/liblimb/chacha20.h/chacha20_crypt.c
@@ -0,0 +1,69 @@
+/* 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) {
+            memcpy(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;
+        }
+
+        memcpy(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;
+    }
+}
diff --git a/src/liblimb/chacha20.h/chacha20_init.c b/src/liblimb/chacha20.h/chacha20_init.c
new file mode 100644
index 0000000..080e349
--- /dev/null
+++ b/src/liblimb/chacha20.h/chacha20_init.c
@@ -0,0 +1,25 @@
+/* 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/u32.h>
+
+void
+chacha20_init(const void *key, const void *nonce, void *ctx_)
+{
+    chacha20_ctx *ctx = ctx_;
+
+    /* fresh start */
+    ctx->part = 0;
+    /* sigma */
+    memcpy(&ctx->in[0], "expand 32-byte k", 16);
+    /* key must be 32 bytes */
+    memcpy(&ctx->in[4], key, 32);
+    /* init counter */
+    ctx->in[12] = 0;
+    /* nonce is 96bit */
+    memcpy(&ctx->in[13], nonce, 12);
+
+    u32pa_le(ctx->in, CHACHA_U32_BLOCK_SIZE);
+}
diff --git a/src/liblimb/include/limb/chacha20.h b/src/liblimb/include/limb/chacha20.h
new file mode 100644
index 0000000..b88e0a7
--- /dev/null
+++ b/src/liblimb/include/limb/chacha20.h
@@ -0,0 +1,27 @@
+/* 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 */
+#ifndef LIMB_CHACHA20_H
+#define LIMB_CHACHA20_H
+
+#include <stddef.h> /* size_t */
+#include <limb/int.h>
+
+#define CHACHA_U32_BLOCK_SIZE      16
+#define CHACHA_BLOCK_SIZE          (sizeof(u32) * CHACHA_U32_BLOCK_SIZE)
+
+struct chacha20_ctx
+{
+    u32 in[CHACHA_U32_BLOCK_SIZE];
+    u8 part;
+};
+
+typedef struct chacha20_ctx chacha20_ctx;
+
+extern void chacha20_init(const void *key, const void *nonce, void *ctx);
+extern void chacha20_crypt(void *dst, const void *msg, size_t mlen, void *ctx);
+
+extern void chacha20_clear(void *ctx);
+extern void chacha20(void *dst, const void *key, const void *nonce, const void *msg, size_t mlen);
+
+#endif /* LIMB_CHACHA20_H */