author | Olivier Brunel
<jjk@jjacky.com> 2023-04-07 16:06:26 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2023-05-20 18:06:36 UTC |
parent | b6e9da1b72f38ee56dc15f035355f18baa653f60 |
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 */