From 7bc527c769cf6bf2a3eea0775266efe6facfcac9 Mon Sep 17 00:00:00 2001 From: Lucas Gabriel Vuotto Date: Fri, 31 May 2024 10:59:58 +0000 Subject: [PATCH] initial import --- .gitignore | 1 + Makefile | 3 + Makefile.inc | 17 ++ README | 79 ++++++++++ aead.c | 39 +++++ aead.h | 28 ++++ aead_chacha20_poly1305.c | 114 ++++++++++++++ auth.c | 78 ++++++++++ auth.h | 34 ++++ auth_poly1305.c | 168 ++++++++++++++++++++ auth_poly1305.h | 23 +++ cipher.c | 110 +++++++++++++ cipher.h | 44 ++++++ cipher_chacha20.c | 199 ++++++++++++++++++++++++ cipher_chacha20.h | 29 ++++ ct.c | 29 ++++ impl_chacha20.c | 85 ++++++++++ impl_chacha20.h | 40 +++++ impl_poly1305.c | 243 +++++++++++++++++++++++++++++ impl_poly1305.h | 39 +++++ lib/Makefile | 7 + lib/shlib_version | 1 + lilcrypto.h | 106 +++++++++++++ util.c | 26 ++++ util.h | 109 +++++++++++++ wycheproof/Makefile | 25 +++ wycheproof/aead.pl | 69 +++++++++ wycheproof_aead.c | 326 +++++++++++++++++++++++++++++++++++++++ 28 files changed, 2071 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 Makefile.inc create mode 100644 README create mode 100644 aead.c create mode 100644 aead.h create mode 100644 aead_chacha20_poly1305.c create mode 100644 auth.c create mode 100644 auth.h create mode 100644 auth_poly1305.c create mode 100644 auth_poly1305.h create mode 100644 cipher.c create mode 100644 cipher.h create mode 100644 cipher_chacha20.c create mode 100644 cipher_chacha20.h create mode 100644 ct.c create mode 100644 impl_chacha20.c create mode 100644 impl_chacha20.h create mode 100644 impl_poly1305.c create mode 100644 impl_poly1305.h create mode 100644 lib/Makefile create mode 100644 lib/shlib_version create mode 100644 lilcrypto.h create mode 100644 util.c create mode 100644 util.h create mode 100644 wycheproof/Makefile create mode 100644 wycheproof/aead.pl create mode 100644 wycheproof_aead.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2416a67 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +obj/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1c0be37 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +SUBDIR= lib wycheproof + +.include diff --git a/Makefile.inc b/Makefile.inc new file mode 100644 index 0000000..f017bf0 --- /dev/null +++ b/Makefile.inc @@ -0,0 +1,17 @@ +LC_SRCS+= auth.c auth_poly1305.c +LC_SRCS+= cipher.c cipher_chacha20.c +LC_SRCS+= aead.c aead_chacha20_poly1305.c +LC_SRCS+= impl_chacha20.c impl_poly1305.c +LC_SRCS+= ct.c util.c + +CFLAGS+= -Wall +CFLAGS+= -Wextra +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wmissing-prototypes +CFLAGS+= -Wpointer-arith +CFLAGS+= -Wshadow +CFLAGS+= -Wstrict-prototypes +CFLAGS+= -Wunused +CFLAGS+= -Wno-unused-parameter + +.include diff --git a/README b/README new file mode 100644 index 0000000..f25ee59 --- /dev/null +++ b/README @@ -0,0 +1,79 @@ +lilcrypto +========= + +> They see me rollin', they hatin'. + +Experiment on rolling my own crypto. Kinda. I'm not creating any new +protocol, but implementing known algorithms and constructions. The main +focus is to understand how to implement the math behind the algorithms, +and to get to know the constructions better. + +Algorithms +========== + +Utilities +--------- + +- Constant-time operations + - [/] compare: returns `0` if match, non-`0` otherwise. The non-`0` + case might leak information. Would be better to return `0xffffffff` + if match, `0` otherwise. + +Authentication +-------------- + +- [x] Poly1305 +- [ ] SHA-512 (needed for Ed25519) + +Ciphers +------- + +- [x] ChaCha20 +- [ ] XChaCha20 + +AEAD +---- + +- [/] ChaCha20-Poly1305 (missing aead_open) +- [ ] XChaCha20-Poly1305 + +ECC +--- + +- Curve25519 + - [ ] Ed25519 (EdDSA) + - [ ] X25519 (ECDH) + +Nice-to-haves +============= + +Utilities +--------- + +- [ ] Portable Makefile +- [ ] NaCl interface +- [ ] signify interface + +Authentication +-------------- + +- [ ] GMAC + +Ciphers +------- + +- [ ] AES +- [ ] Camellia +- [ ] Salsa20 (no Wycheproof test vector suite) +- [ ] XSalsa20 (no Wycheproof test vector suite) + +AEAD +---- + +- [ ] AES-GCM +- [ ] Camellia-GCM +- [ ] Salsa20-Poly1305 (no Wycherproof test vector suite) +- [ ] XSalsa20-Poly1305 (no Wycherproof test vector suite) + +KDF? +---- diff --git a/aead.c b/aead.c new file mode 100644 index 0000000..f283dcc --- /dev/null +++ b/aead.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lilcrypto.h" +#include "aead.h" + + +int +lc_aead_seal(const struct lc_aead_impl *impl, const uint8_t *key, + size_t keylen, const uint8_t *iv, size_t ivlen, uint8_t *out, + size_t *outlen, const uint8_t *aad, size_t aadlen, const uint8_t *in, + size_t inlen) +{ + return impl->seal(key, keylen, iv, ivlen, out, outlen, aad, aadlen, in, + inlen); +} + +int +lc_aead_open(const struct lc_aead_impl *impl, const uint8_t *key, + size_t keylen, const uint8_t *iv, size_t ivlen, uint8_t *out, + size_t *outlen, const uint8_t *aad, size_t aadlen, const uint8_t *in, + size_t inlen) +{ + return impl->open(key, keylen, iv, ivlen, out, outlen, aad, aadlen, in, + inlen); +} diff --git a/aead.h b/aead.h new file mode 100644 index 0000000..9982fec --- /dev/null +++ b/aead.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + + +struct lc_aead_impl { + int (*seal)(const uint8_t *, size_t, const uint8_t *, size_t, + uint8_t *, size_t *, const uint8_t *, size_t, + const uint8_t *, size_t); + int (*open)(const uint8_t *, size_t, const uint8_t *, size_t, + uint8_t *, size_t *, const uint8_t *, size_t, + const uint8_t *, size_t); +}; diff --git a/aead_chacha20_poly1305.c b/aead_chacha20_poly1305.c new file mode 100644 index 0000000..c1def9f --- /dev/null +++ b/aead_chacha20_poly1305.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lilcrypto.h" +#include "aead.h" +#include "auth_poly1305.h" +#include "cipher_chacha20.h" +#include "impl_chacha20.h" +#include "impl_poly1305.h" + +#include "util.h" + + +/* + * Implements ChaCha20-Poly1305 according to RFC 8439. + */ + +static uint8_t zeropad[16]; + +static int +chacha20_poly1305_seal(const uint8_t *key, size_t keylen, const uint8_t *iv, + size_t ivlen, uint8_t *out, size_t *outlen, const uint8_t *aad, + size_t aadlen, const uint8_t *in, size_t inlen) +{ + struct chacha20_ctx cctx; + struct poly1305_ctx pctx; + uint8_t poly1305_key[LC_POLY1305_KEYLEN]; + uint8_t buf[sizeof(uint64_t) * 2]; + size_t i, olen; + + if (inlen > UINT64_MAX || aadlen > UINT64_MAX || + inlen > SIZE_MAX - LC_POLY1305_TAGLEN || + inlen > SIZE_MAX - CHACHA20_CHUNK + 1 || + (inlen + CHACHA20_CHUNK - 1) / CHACHA20_CHUNK > + CHACHA20_CTRMAX - 1) { + if (out == NULL) + *outlen = 0; + return 0; + } + + if (out == NULL) { + *outlen = inlen + LC_POLY1305_TAGLEN; + return 1; + } + + *outlen = 0; + + for (i = 0; i < LC_POLY1305_KEYLEN; i++) + poly1305_key[i] = 0; + if (!chacha20_x_init(&cctx, key, keylen, iv, ivlen) || + !chacha20_x_update(&cctx, poly1305_key, &olen, poly1305_key, + LC_POLY1305_KEYLEN)) + return 0; + for (i = 0; i < LC_POLY1305_KEYLEN / sizeof(uint32_t); i++) + store32le(&poly1305_key[i * 4], cctx.s[i]); + + if (!poly1305_init(&pctx, poly1305_key, LC_POLY1305_KEYLEN) || + !poly1305_update(&pctx, aad, aadlen)) + return 0; + if (aadlen % 16 != 0) + if (!poly1305_update(&pctx, zeropad, 16 - (aadlen % 16))) + return 0; + + if (!chacha20_x_init_from(&cctx, key, keylen, iv, ivlen, 1)) + return 0; + if (!chacha20_x_update(&cctx, out, &olen, in, inlen)) + return 0; + *outlen = olen; + if (!chacha20_x_final(&cctx, out + olen, &olen)) + return 0; + if (!poly1305_update(&pctx, out, inlen)) + return 0; + if (inlen % 16 != 0) + if (!poly1305_update(&pctx, zeropad, 16 - (inlen % 16))) + return 0; + + store64le(&buf[0], aadlen); + store64le(&buf[sizeof(uint64_t)], inlen); + if (!poly1305_update(&pctx, buf, sizeof(buf)) || + !poly1305_final(&pctx, out + inlen, &olen)) + return 0; + + lc_scrub(buf, sizeof(buf)); + lc_scrub(poly1305_key, sizeof(poly1305_key)); + + *outlen = inlen + LC_POLY1305_TAGLEN; + + return 1; +} + + +static struct lc_aead_impl chacha20_poly1305_impl = { + .seal = &chacha20_poly1305_seal, + .open = NULL, //&chacha20_poly1305_open, +}; + +const struct lc_aead_impl * +lc_aead_impl_chacha20_poly1305(void) +{ + return &chacha20_poly1305_impl; +} diff --git a/auth.c b/auth.c new file mode 100644 index 0000000..e7f4c0d --- /dev/null +++ b/auth.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "lilcrypto.h" +#include "auth.h" + + +int +lc_auth_init(struct lc_auth_ctx *ctx, const uint8_t *key, size_t keylen) +{ + return ctx->impl->init(ctx->arg, key, keylen); +} + +int +lc_auth_update(struct lc_auth_ctx *ctx, const uint8_t *in, size_t inlen) +{ + return ctx->impl->update(ctx->arg, in, inlen); +} + +int +lc_auth_final(struct lc_auth_ctx *ctx, uint8_t *out, size_t *outlen) +{ + return ctx->impl->final(ctx->arg, out, outlen); +} + +int +lc_auth(const struct lc_auth_impl *impl, const uint8_t *key, size_t keylen, + uint8_t *out, size_t *outlen, const uint8_t *in, size_t inlen) +{ + return impl->auth(key, keylen, out, outlen, in, inlen); +} + +struct lc_auth_ctx * +lc_auth_ctx_new(const struct lc_auth_impl *impl) +{ + struct lc_auth_ctx *ctx; + void *arg; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + if (impl->argsz > 0) { + arg = malloc(impl->argsz); + if (arg == NULL) { + free(ctx); + return NULL; + } + ctx->arg = arg; + } else + ctx->arg = NULL; + ctx->impl = impl; + + return ctx; +} + +void +lc_auth_ctx_free(struct lc_auth_ctx *ctx) +{ + if (ctx != NULL) + free(ctx->arg); + free(ctx); +} diff --git a/auth.h b/auth.h new file mode 100644 index 0000000..71e50f4 --- /dev/null +++ b/auth.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + + +struct lc_auth_impl { + int (*init)(void *, const uint8_t *, size_t); + int (*update)(void *, const uint8_t *, size_t); + int (*final)(void *, uint8_t *, size_t *); + int (*auth)(const uint8_t *, size_t, uint8_t *, size_t *, + const uint8_t *, size_t); + + size_t argsz; +}; + +struct lc_auth_ctx { + const struct lc_auth_impl *impl; + void *arg; +}; diff --git a/auth_poly1305.c b/auth_poly1305.c new file mode 100644 index 0000000..623022f --- /dev/null +++ b/auth_poly1305.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lilcrypto.h" +#include "auth.h" +#include "auth_poly1305.h" +#include "impl_poly1305.h" + +#include "util.h" + + +int +poly1305_init(void *arg, const uint8_t *key, size_t keylen) +{ + struct poly1305_ctx *ctx = arg; + size_t i; + uint32_t t0, t1, t2, t3; + + if (keylen != LC_POLY1305_KEYLEN) + return 0; + + ctx->h0 = 0; + ctx->h1 = 0; + ctx->h2 = 0; + ctx->h3 = 0; + ctx->h4 = 0; + + t0 = load32le(&key[0]); + t1 = load32le(&key[4]); + t2 = load32le(&key[8]); + t3 = load32le(&key[12]); + + ctx->r0 = t0 & 0x3ffffff; + ctx->r1 = ((t1 << 6) | (t0 >> 26)) & 0x3ffff03; + ctx->r2 = ((t2 << 12) | (t1 >> 20)) & 0x3ffc0ff; + ctx->r3 = ((t3 << 18) | (t2 >> 14)) & 0x3f03fff; + ctx->r4 = (t3 >> 8) & 0xfffff; + + ctx->x1 = 5 * ctx->r1; + ctx->x2 = 5 * ctx->r2; + ctx->x3 = 5 * ctx->r3; + ctx->x4 = 5 * ctx->r4; + + ctx->s0 = load32le(&key[16]); + ctx->s1 = load32le(&key[20]); + ctx->s2 = load32le(&key[24]); + ctx->s3 = load32le(&key[28]); + + ctx->mlen = 0; + for (i = 0; i < POLY1305_CHUNK; i++) + ctx->m[i] = 0; + + return 1; +} + +int +poly1305_update(void *arg, const uint8_t *in, size_t inlen) +{ + struct poly1305_ctx *ctx = arg; + size_t i; + + for (i = 0; i + ctx->mlen < POLY1305_CHUNK && i < inlen; i++) + ctx->m[i + ctx->mlen] = in[i]; + ctx->mlen += i; + in += i; + inlen -= i; + + if (ctx->mlen == POLY1305_CHUNK) { + poly1305_block(ctx, 1); + ctx->mlen = 0; + } + + if (inlen == 0) + return 1; + + while (inlen >= POLY1305_CHUNK) { + for (i = 0; i < POLY1305_CHUNK; i++) + ctx->m[i] = in[i]; + poly1305_block(ctx, 1); + + in += POLY1305_CHUNK; + inlen -= POLY1305_CHUNK; + } + + for (i = 0; i < inlen; i++) + ctx->m[i] = in[i]; + ctx->mlen = inlen; + + return 1; +} + +int +poly1305_final(void *arg, uint8_t *out, size_t *outlen) +{ + struct poly1305_ctx *ctx = arg; + uint32_t tag[POLY1305_TAGLEN_WORDS]; + size_t i; + + *outlen = LC_POLY1305_TAGLEN; + if (out == NULL) + return 1; + + i = ctx->mlen; + if (i > 0) { + if (i < POLY1305_CHUNK) { + ctx->m[i++] = 1; + for (; i < POLY1305_CHUNK; i++) + ctx->m[i] = 0; + poly1305_block(ctx, 0); + } else + poly1305_block(ctx, 1); + } + poly1305_reduce(ctx, tag); + + store32le(&out[0], tag[0]); + store32le(&out[4], tag[1]); + store32le(&out[8], tag[2]); + store32le(&out[12], tag[3]); + + lc_scrub(ctx, sizeof(*ctx)); + + return 1; +} + +static int +poly1305_auth(const uint8_t *key, size_t keylen, uint8_t *out, size_t *outlen, + const uint8_t *in, size_t inlen) +{ + struct poly1305_ctx ctx; + + if (out == NULL) { + *outlen = LC_POLY1305_TAGLEN; + return 1; + } + + return poly1305_init(&ctx, key, keylen) && + poly1305_update(&ctx, in, inlen) && + poly1305_final(&ctx, out, outlen); +} + + +static struct lc_auth_impl poly1305_impl = { + .init = &poly1305_init, + .update = &poly1305_update, + .final = &poly1305_final, + .auth = &poly1305_auth, + + .argsz = sizeof(struct poly1305_ctx), +}; + +const struct lc_auth_impl * +lc_auth_impl_poly1305(void) +{ + return &poly1305_impl; +} diff --git a/auth_poly1305.h b/auth_poly1305.h new file mode 100644 index 0000000..6c6af32 --- /dev/null +++ b/auth_poly1305.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + + +int poly1305_init(void *, const uint8_t *, size_t); +int poly1305_update(void *, const uint8_t *, size_t); +int poly1305_final(void *, uint8_t *, size_t *); diff --git a/cipher.c b/cipher.c new file mode 100644 index 0000000..127075b --- /dev/null +++ b/cipher.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "lilcrypto.h" +#include "cipher.h" + + +int +lc_cipher_encrypt_init(struct lc_cipher_ctx *ctx, const uint8_t *key, + size_t keylen, const uint8_t *iv, size_t ivlen) +{ + return ctx->impl->encrypt_init(ctx->arg, key, keylen, iv, ivlen); +} + +int +lc_cipher_encrypt_update(struct lc_cipher_ctx *ctx, uint8_t *out, + size_t *outlen, const uint8_t *in, size_t inlen) +{ + return ctx->impl->encrypt_update(ctx->arg, out, outlen, in, inlen); +} + +int +lc_cipher_encrypt_final(struct lc_cipher_ctx *ctx, uint8_t *out, + size_t *outlen) +{ + return ctx->impl->encrypt_final(ctx->arg, out, outlen); +} + +int +lc_cipher_encrypt(const struct lc_cipher_impl *impl, const uint8_t *key, + size_t keylen, const uint8_t *iv, size_t ivlen, uint8_t *out, + size_t *outlen, const uint8_t *in, size_t inlen) +{ + return impl->encrypt(key, keylen, iv, ivlen, out, outlen, in, inlen); +} + +int +lc_cipher_decrypt_init(struct lc_cipher_ctx *ctx, const uint8_t *key, + size_t keylen, const uint8_t *iv, size_t ivlen) +{ + return ctx->impl->decrypt_init(ctx->arg, key, keylen, iv, ivlen); +} + +int +lc_cipher_decrypt_update(struct lc_cipher_ctx *ctx, uint8_t *out, + size_t *outlen, const uint8_t *in, size_t inlen) +{ + return ctx->impl->decrypt_update(ctx->arg, out, outlen, in, inlen); +} + +int +lc_cipher_decrypt_final(struct lc_cipher_ctx *ctx, uint8_t *out, + size_t *outlen) +{ + return ctx->impl->decrypt_final(ctx->arg, out, outlen); +} + +int +lc_cipher_decrypt(const struct lc_cipher_impl *impl, const uint8_t *key, + size_t keylen, const uint8_t *iv, size_t ivlen, uint8_t *out, + size_t *outlen, const uint8_t *in, size_t inlen) +{ + return impl->decrypt(key, keylen, iv, ivlen, out, outlen, in, inlen); +} + +struct lc_cipher_ctx * +lc_cipher_ctx_new(const struct lc_cipher_impl *impl) +{ + struct lc_cipher_ctx *ctx; + void *arg; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + if (impl->argsz > 0) { + arg = malloc(impl->argsz); + if (arg == NULL) { + free(ctx); + return NULL; + } + ctx->arg = arg; + } else + ctx->arg = NULL; + ctx->impl = impl; + + return ctx; +} + +void +lc_cipher_ctx_free(struct lc_cipher_ctx *ctx) +{ + if (ctx != NULL) + free(ctx->arg); + free(ctx); +} diff --git a/cipher.h b/cipher.h new file mode 100644 index 0000000..73e8a8e --- /dev/null +++ b/cipher.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + + +struct lc_cipher_impl { + int (*encrypt_init)(void *, const uint8_t *, size_t, + const uint8_t *, size_t); + int (*encrypt_update)(void *, uint8_t *, size_t *, const uint8_t *, + size_t); + int (*encrypt_final)(void *, uint8_t *, size_t *); + int (*encrypt)(const uint8_t *, size_t, const uint8_t *, size_t, + uint8_t *, size_t *, const uint8_t *, size_t); + + int (*decrypt_init)(void *, const uint8_t *, size_t, + const uint8_t *, size_t); + int (*decrypt_update)(void *, uint8_t *, size_t *, const uint8_t *, + size_t); + int (*decrypt_final)(void *, uint8_t *, size_t *); + int (*decrypt)(const uint8_t *, size_t, const uint8_t *, size_t, + uint8_t *, size_t *, const uint8_t *, size_t); + + size_t argsz; +}; + +struct lc_cipher_ctx { + const struct lc_cipher_impl *impl; + void *arg; +}; diff --git a/cipher_chacha20.c b/cipher_chacha20.c new file mode 100644 index 0000000..31102a4 --- /dev/null +++ b/cipher_chacha20.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "lilcrypto.h" +#include "cipher.h" +#include "cipher_chacha20.h" +#include "impl_chacha20.h" + +#include "util.h" + + +int +chacha20_x_init_from(void *arg, const uint8_t *key, size_t keylen, + const uint8_t *iv, size_t ivlen, uint32_t counter) +{ + struct chacha20_ctx *ctx = arg; + size_t i; + + if (keylen != LC_CHACHA20_KEYLEN || ivlen != LC_CHACHA20_IVLEN) + return 0; + + for (i = 0; i < CHACHA20_CHUNK_WORDS; i++) + ctx->s[i] = 0; + for (i = 0; i < CHACHA20_KEY_WORDS; i++) + ctx->k[i] = load32le(&key[i * 4]); + ctx->c = counter; + for (i = 0; i < CHACHA20_NONCE_WORDS; i++) + ctx->n[i] = load32le(&iv[i * 4]); + ctx->blen = 0; + + return 1; +} + +int +chacha20_x_init(void *arg, const uint8_t *key, size_t keylen, + const uint8_t *iv, size_t ivlen) +{ + return chacha20_x_init_from(arg, key, keylen, iv, ivlen, 0); +} + +int +chacha20_x_update(void *arg, uint8_t *out, size_t *outlen, const uint8_t *in, + size_t inlen) +{ + struct chacha20_ctx *ctx = arg; + uint32_t h; + uint8_t s[4]; + size_t i, blocks, off, pad; + + *outlen = 0; + if (inlen > SIZE_MAX - (CHACHA20_CHUNK - 1) - ctx->blen) + return 0; + blocks = inlen + ctx->blen + CHACHA20_CHUNK - 1; + if (blocks / CHACHA20_CHUNK + ctx->c > CHACHA20_CTRMAX) + return 0; + + if (out == NULL) { + *outlen = inlen; + return 1; + } + + *outlen = inlen; + + if (ctx->blen == 0) + goto fullblock; + + off = ctx->blen % 4; + if (off != 0) { + store32le(s, ctx->s[ctx->blen / 4]); + for (i = 0; i + off < 4 && i < inlen; i++) + out[i] = in[i] ^ s[i + off]; + ctx->blen += i; + out += i; + in += i; + inlen -= i; + } + + pad = inlen % 4; + for (i = 0; i + ctx->blen < CHACHA20_CHUNK && i < inlen - pad; i += 4) { + h = load32le(&in[i * 4]); + h ^= ctx->s[(i + ctx->blen) / 4]; + store32le(&out[i * 4], h); + } + ctx->blen += i * 4; + out += i * 4; + in += i * 4; + inlen -= i * 4; + + fullblock: + while (inlen >= CHACHA20_CHUNK) { + chacha20_block(ctx); + ctx->c++; + + for (i = 0; i < CHACHA20_CHUNK_WORDS; i++) { + h = load32le(&in[i * 4]); + h ^= ctx->s[i]; + store32le(&out[i * 4], h); + } + out += CHACHA20_CHUNK; + in += CHACHA20_CHUNK; + inlen -= CHACHA20_CHUNK; + } + + chacha20_block(ctx); + ctx->c++; + ctx->blen = inlen; + + pad = inlen % 4; + for (i = 0; i < (inlen - pad) / 4; i++) { + h = load32le(&in[i * 4]); + h ^= ctx->s[i]; + store32le(&out[i * 4], h); + } + out += i * 4; + in += i * 4; + inlen -= i * 4; + + store32le(s, ctx->s[i]); + for (i = 0; i < pad; i++) + out[i] = in[i] ^ s[i]; + + return 1; +} + +int +chacha20_x_final(void *arg, uint8_t *out, size_t *outlen) +{ + struct chacha20_ctx *ctx = arg; + + *outlen = 0; + lc_scrub(ctx, sizeof(*ctx)); + + return 1; +} + +int +chacha20_x(const uint8_t *key, size_t keylen, const uint8_t *iv, size_t ivlen, + uint8_t *out, size_t *outlen, const uint8_t *in, size_t inlen) +{ + struct chacha20_ctx ctx; + size_t l0, l1; + int rc; + + *outlen = 0; + + if (inlen > SIZE_MAX - (CHACHA20_CHUNK - 1) || + (inlen + CHACHA20_CHUNK - 1) / CHACHA20_CHUNK > CHACHA20_CTRMAX) + return 0; + + if (out == NULL) { + *outlen = inlen; + return 1; + } + + rc = chacha20_x_init(&ctx, key, keylen, iv, ivlen) && + chacha20_x_update(&ctx, out, &l0, in, inlen) & + chacha20_x_final(&ctx, out + l0, &l1); + + if (rc) + *outlen = l0 + l1; + + return rc; +} + + +static struct lc_cipher_impl chacha20_impl = { + .encrypt_init = &chacha20_x_init, + .encrypt_update = &chacha20_x_update, + .encrypt_final = &chacha20_x_final, + .encrypt = &chacha20_x, + + .decrypt_init = &chacha20_x_init, + .decrypt_update = &chacha20_x_update, + .decrypt_final = &chacha20_x_final, + .decrypt = &chacha20_x, + + .argsz = sizeof(struct chacha20_ctx), +}; + +const struct lc_cipher_impl * +lc_cipher_impl_chacha20(void) +{ + return &chacha20_impl; +} diff --git a/cipher_chacha20.h b/cipher_chacha20.h new file mode 100644 index 0000000..3c8c1ac --- /dev/null +++ b/cipher_chacha20.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + + +int chacha20_x_init_from(void *, const uint8_t *, size_t, const uint8_t *, + size_t, uint32_t); +int chacha20_x_init(void *, const uint8_t *, size_t, const uint8_t *, + size_t); +int chacha20_x_update(void *, uint8_t *, size_t *, const uint8_t *, + size_t); +int chacha20_x_final(void *, uint8_t *, size_t *); +int chacha20_x(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t *, + size_t *, const uint8_t *, size_t); diff --git a/ct.c b/ct.c new file mode 100644 index 0000000..28e2354 --- /dev/null +++ b/ct.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lilcrypto.h" + + +uint32_t +lc_ct_cmp(const uint8_t *x, const uint8_t *y, size_t l) +{ + uint32_t r = 0; + + for (; l > 0; l--) + r |= *x++ ^ *y++; + + return r; +} diff --git a/impl_chacha20.c b/impl_chacha20.c new file mode 100644 index 0000000..0024340 --- /dev/null +++ b/impl_chacha20.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "impl_chacha20.h" +#include "util.h" + + +/* + * ChaCha20 implementation. + * + * ChaCha originally designed by Daniel J. Bernstein, "ChaCha, a variant of + * Salsa20", https://cr.yp.to/chacha/chacha-20080128.pdf . + */ + +/* "expand 32-byte k" */ +#define SIGMA0 UINT32_C(0x61707865) +#define SIGMA1 UINT32_C(0x3320646e) +#define SIGMA2 UINT32_C(0x79622d32) +#define SIGMA3 UINT32_C(0x6b206574) + +#define QUARTERROUND(a, b, c, d) do { \ + a += b; d ^= a; d = rotl32(d, 16); \ + c += d; b ^= c; b = rotl32(b, 12); \ + a += b; d ^= a; d = rotl32(d, 8); \ + c += d; b ^= c; b = rotl32(b, 7); \ + } while (0) + + +void +chacha20_block(struct chacha20_ctx *ctx) +{ + uint32_t x[CHACHA20_CHUNK_WORDS]; + size_t i; + + x[0] = SIGMA0; + x[1] = SIGMA1; + x[2] = SIGMA2; + x[3] = SIGMA3; + x[4] = ctx->k[0]; + x[5] = ctx->k[1]; + x[6] = ctx->k[2]; + x[7] = ctx->k[3]; + x[8] = ctx->k[4]; + x[9] = ctx->k[5]; + x[10] = ctx->k[6]; + x[11] = ctx->k[7]; + x[12] = ctx->c; + x[13] = ctx->n[0]; + x[14] = ctx->n[1]; + x[15] = ctx->n[2]; + + for (i = 0; i < CHACHA20_CHUNK_WORDS; i++) + ctx->s[i] = x[i]; + + for (i = 0; i < CHACHA20_ROUNDS; i++) { + QUARTERROUND(x[0], x[4], x[8], x[12]); + QUARTERROUND(x[1], x[5], x[9], x[13]); + QUARTERROUND(x[2], x[6], x[10], x[14]); + QUARTERROUND(x[3], x[7], x[11], x[15]); + + QUARTERROUND(x[0], x[5], x[10], x[15]); + QUARTERROUND(x[1], x[6], x[11], x[12]); + QUARTERROUND(x[2], x[7], x[8], x[13]); + QUARTERROUND(x[3], x[4], x[9], x[14]); + } + + for (i = 0; i < CHACHA20_CHUNK_WORDS; i++) + ctx->s[i] += x[i]; +} diff --git a/impl_chacha20.h b/impl_chacha20.h new file mode 100644 index 0000000..eb60df2 --- /dev/null +++ b/impl_chacha20.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "lilcrypto.h" + + +#define CHACHA20_CHUNK 64 +#define CHACHA20_CHUNK_WORDS (CHACHA20_CHUNK / sizeof(uint32_t)) +#define CHACHA20_CTRMAX 4294967295 /* 2^32 - 1 */ +#define CHACHA20_KEY_WORDS (LC_CHACHA20_KEYLEN / sizeof(uint32_t)) +#define CHACHA20_NONCE_WORDS (LC_CHACHA20_IVLEN / sizeof(uint32_t)) +#define CHACHA20_ROUNDS 10 + + +struct chacha20_ctx { + uint32_t s[CHACHA20_CHUNK_WORDS]; + uint32_t k[CHACHA20_KEY_WORDS]; + uint32_t c; + uint32_t n[CHACHA20_NONCE_WORDS]; + size_t blen; +}; + + +void chacha20_block(struct chacha20_ctx *); diff --git a/impl_poly1305.c b/impl_poly1305.c new file mode 100644 index 0000000..c6c445e --- /dev/null +++ b/impl_poly1305.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "impl_poly1305.h" +#include "util.h" + + +/* + * Poly1305 implementation. + * + * Poly1305 originally designed by Daniel J. Bernstein, "The Poly1305-AES + * message-authentication code", https://cr.yp.to/mac/poly1305-20050329.pdf . + * + * This implementation is written from scratch, but consulting poly1305-donna + * by Andrew Moon, https://github.com/floodyberry/poly1305-donna, released + * under MIT license. Similarities are to be expected. + */ + +/* + * Copyright 2011-2016 Andrew Moon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * To ease reduction modulo p = 2^130 - 5, work in base 2^130, as 2^130 = 5 mod + * p, allowing for easier operations. 2^130 splits evenly into 5 limbs of 26 + * bits. + * + * Addition is performed limb-wise: + * + * h = h4 h3 h2 h1 h0 + * c = c4 c3 c2 c1 c0 + * ----------------------------------- + * h+c = h4+c4 h3+c3 h2+c2 h1+c1 h0+c0 + * + * Carry won't be propagated at this step. + * + * Considering h = h + c, multiplication is performed as school multiplication + * / long multiplication: + * + * h = h4 h3 h2 h1 h0 + * r = r4 r3 r2 r1 r0 + * ----------------------------------------------------------- + * h4*r0 h3*r0 h2*r0 h1*r0 h0*r0 + * h4*r1 h3*r1 h2*r1 h1*r1 h0*r1 + * h4*r2 h3*r2 h2*r2 h1*r2 h0*r2 + * h4*r3 h3*r3 h2*r3 h1*r3 h0*r3 + * h4*r4 h3*r4 h2*r4 h1*r4 h0*r4 + * + * Each hn*rn fits in 53 bits. Carry won't be propagated at this step. Partial + * reduction modulo p starts here: + * + * 2^130 + * h = | h4 h3 h2 h1 h0 + * r = | r4 r3 r2 r1 r0 + * ------------------------------|------------------------------ + * | h4*r0 h3*r0 h2*r0 h1*r0 h0*r0 + * h4*r1 | h3*r1 h2*r1 h1*r1 h0*r1 + * h4*r2 h3*r2 | h2*r2 h1*r2 h0*r2 + * h4*r3 h3*r3 h2*r3 | h1*r3 h0*r3 + * h4*r4 h3*r4 h2*r4 h1*r4 | h0*r4 + * + * 2^130 + * h = | h4 h3 h2 h1 h0 + * r = | r4 r3 r2 r1 r0 + * --------|-------------------------------------- + * | h4*r0 h3*r0 h2*r0 h1*r0 h0*r0 + * | h3*r1 h2*r1 h1*r1 h0*r1 5*h4*r1 + * | h2*r2 h1*r2 h0*r2 5*h4*r2 5*h3*r2 + * | h1*r3 h0*r3 5*h4*r3 5*h3*r3 5*h2*r3 + * | h0*r4 5*h4*r4 5*h3*r4 5*h2*r4 5*h1*r4 + * --------|-------------------------------------- + * h*r = | t4 t3 t2 t1 t0 + * + * All the carry propagations are performed after this step. h0 is set t0 low + * 26 bits of t0; h1 thru h4 are set to tn + (tn-1 >> 26) to propagate the + * carry. t4 might overflow so it needs to be backpropagated to h0 and h1. h1 + * won't carry into h2: given the highest possible h, c, and r, + * + * h = 0xffffffffffffffffffffffffffffffff + * c = 0x1ffffffffffffffffffffffffffffffff + * r = 0x0ffffffc0ffffffc0ffffffc0fffffff + * + * the limbs and t4 before h0 and h1 second propagation are + * + * h4 = 0x257ffff + * h3 = 0x3a95fff + * h2 = 0x3fea57f + * h1 = 0x3fffa70 + * h0 = 0x2000002 + * t4 = 0x77fffffa57ffff + * + * which becomes + * + * h4 = 0x257ffff + * h3 = 0x3a95fff + * h2 = 0x3fea57f + * h1 = 0x3fffa95 + * h0 = 0x3fffff8 + * + * To perform the final reduction modulo p, observe that each hn is bound by + * 2^26, which means that h is bound by 2^130. Define minusp = 2^136 - p. + * - If h < p, minusp + h < 2^136. + * - If h >= p, then h = p + k with k in {0,1,2,3,4}, and minusp + h = + * 2^136 - p + p + k = 2^136 + k >= 2^136, and both minusp + h = k mod 2^136 + * and h = k mod p for all possible values of k. + * + * To avoid information leaking via side channels, define g = minusp + h, and + * select g if bit 136 is set, h otherwise. In particular, define a 32-bit + * mask = ~(g >> 136) + 1. + * - If bit 136 of g is 1, mask = ~1 + 1 = 0xffffffff. + * - If bit 136 of g is 0, mask = ~0 + 1 = 0. + * Then perform (h & ~mask) | (g & mask). + */ + +void +poly1305_block(struct poly1305_ctx *ctx, uint32_t hibit) +{ + uint64_t h0, h1, h2, h3, h4, t0, t1, t2, t3, t4; + uint32_t r0, r1, r2, r3, r4, x1, x2, x3, x4; + + h0 = ctx->h0; + h1 = ctx->h1; + h2 = ctx->h2; + h3 = ctx->h3; + h4 = ctx->h4; + r0 = ctx->r0; + r1 = ctx->r1; + r2 = ctx->r2; + r3 = ctx->r3; + r4 = ctx->r4; + x1 = ctx->x1; + x2 = ctx->x2; + x3 = ctx->x3; + x4 = ctx->x4; + + t0 = load32le(&ctx->m[0]); + t1 = load32le(&ctx->m[4]); + t2 = load32le(&ctx->m[8]); + t3 = load32le(&ctx->m[12]); + t4 = hibit; + + h0 += t0 & 0x3ffffff; + h1 += ((t1 << 6) | (t0 >> 26)) & 0x3ffffff; + h2 += ((t2 << 12) | (t1 >> 20)) & 0x3ffffff; + h3 += ((t3 << 18) | (t2 >> 14)) & 0x3ffffff; + h4 += (t4 << 24) | (t3 >> 8); + + t0 = h0 * r0 + h4 * x1 + h3 * x2 + h2 * x3 + h1 * x4; + t1 = h1 * r0 + h0 * r1 + h4 * x2 + h3 * x3 + h2 * x4; + t2 = h2 * r0 + h1 * r1 + h0 * r2 + h4 * x3 + h3 * x4; + t3 = h3 * r0 + h2 * r1 + h1 * r2 + h0 * r3 + h4 * x4; + t4 = h4 * r0 + h3 * r1 + h2 * r2 + h1 * r3 + h0 * r4; + + h0 = t0 & 0x3ffffff; + t1 += t0 >> 26; + h1 = t1 & 0x3ffffff; + t2 += t1 >> 26; + h2 = t2 & 0x3ffffff; + t3 += t2 >> 26; + h3 = t3 & 0x3ffffff; + t4 += t3 >> 26; + h4 = t4 & 0x3ffffff; + + h0 += 5 * (t4 >> 26); + h1 += h0 >> 26; + h0 &= 0x3ffffff; + + ctx->h0 = h0; + ctx->h1 = h1; + ctx->h2 = h2; + ctx->h3 = h3; + ctx->h4 = h4; +} + +void +poly1305_reduce(struct poly1305_ctx *ctx, uint32_t a[POLY1305_TAGLEN_WORDS]) +{ + uint64_t t0, t1, t2, t3, t4, g0, g1, g2, g3, g4; + uint32_t mask; + + t0 = (ctx->h0 | (ctx->h1 << 26)) & 0xffffffff; + t1 = ((ctx->h1 >> 6) | (ctx->h2 << 20)) & 0xffffffff; + t2 = ((ctx->h2 >> 12) | (ctx->h3 << 14)) & 0xffffffff; + t3 = ((ctx->h3 >> 18) | (ctx->h4 << 8)) & 0xffffffff; + t4 = ctx->h4 >> 24; + + g0 = t0 + 5; + g1 = t1 + (g0 >> 32); + g2 = t2 + (g1 >> 32); + g3 = t3 + (g2 >> 32); + g4 = t4 + (g3 >> 32) + 252; + + mask = ~(g4 >> 8) + 1; + + t0 = (t0 & ~mask) | (g0 & mask); + t1 = (t1 & ~mask) | (g1 & mask); + t2 = (t2 & ~mask) | (g2 & mask); + t3 = (t3 & ~mask) | (g3 & mask); + + t0 += ctx->s0; + t1 += ctx->s1 + (t0 >> 32); + t2 += ctx->s2 + (t1 >> 32); + t3 += ctx->s3 + (t2 >> 32); + + a[0] = t0 & 0xffffffff; + a[1] = t1 & 0xffffffff; + a[2] = t2 & 0xffffffff; + a[3] = t3 & 0xffffffff; +} diff --git a/impl_poly1305.h b/impl_poly1305.h new file mode 100644 index 0000000..eb166ce --- /dev/null +++ b/impl_poly1305.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "lilcrypto.h" + + +#define POLY1305_CHUNK 16 +#define POLY1305_TAGLEN_WORDS (LC_POLY1305_TAGLEN / sizeof(uint32_t)) + + +struct poly1305_ctx { + uint32_t h0, h1, h2, h3, h4; + uint32_t r0, r1, r2, r3, r4; + uint32_t x1, x2, x3, x4; + uint32_t s0, s1, s2, s3; + size_t mlen; + uint8_t m[POLY1305_CHUNK]; +}; + + +void poly1305_block(struct poly1305_ctx *, uint32_t); +void poly1305_reduce(struct poly1305_ctx *, + uint32_t [POLY1305_TAGLEN_WORDS]); diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..a83b9a7 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,7 @@ +.PATH: ${.CURDIR}/.. + +LIB= lilcrypto +NOMAN= noman +SRCS= ${LC_SRCS} + +.include diff --git a/lib/shlib_version b/lib/shlib_version new file mode 100644 index 0000000..d530fb3 --- /dev/null +++ b/lib/shlib_version @@ -0,0 +1 @@ +LIBlilcrypto_VERSION=0.0 diff --git a/lilcrypto.h b/lilcrypto.h new file mode 100644 index 0000000..2e6d57d --- /dev/null +++ b/lilcrypto.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + + +/* + * Constants. + */ + +/* Authentitcation. */ +#define LC_POLY1305_KEYLEN 32 +#define LC_POLY1305_TAGLEN 16 + +/* Ciphers. */ +#define LC_CHACHA20_KEYLEN 32 +#define LC_CHACHA20_IVLEN 12 + + +/* + * Constant-time operations. + */ + +uint32_t lc_ct_cmp(const uint8_t *, const uint8_t *, size_t); + + +/* + * Message authentication code. + */ + +struct lc_auth_ctx; +struct lc_auth_impl; + + +int lc_auth_init(struct lc_auth_ctx *, const uint8_t *, size_t); +int lc_auth_update(struct lc_auth_ctx *, const uint8_t *, size_t); +int lc_auth_final(struct lc_auth_ctx *, uint8_t *, size_t *); +int lc_auth(const struct lc_auth_impl *, const uint8_t *, size_t, + uint8_t *, size_t *, const uint8_t *, size_t); + +struct lc_auth_ctx *lc_auth_ctx_new(const struct lc_auth_impl *); +void lc_auth_ctx_free(struct lc_auth_ctx *); + +const struct lc_auth_impl *lc_auth_impl_poly1305(void); + + +/* + * Ciphers. + */ + +struct lc_cipher_ctx; +struct lc_cipher_impl; + + +int lc_cipher_encrypt_init(struct lc_cipher_ctx *, const uint8_t *, size_t, + const uint8_t *, size_t); +int lc_cipher_encrypt_update(struct lc_cipher_ctx *, uint8_t *, size_t *, + const uint8_t *, size_t); +int lc_cipher_encrypt_final(struct lc_cipher_ctx *, uint8_t *, size_t *); +int lc_cipher_encrypt(const struct lc_cipher_impl *, const uint8_t *, + size_t, const uint8_t *, size_t, uint8_t *, size_t *, + const uint8_t *, size_t); +int lc_cipher_decrypt_init(struct lc_cipher_ctx *, const uint8_t *, size_t, + const uint8_t *, size_t); +int lc_cipher_decrypt_update(struct lc_cipher_ctx *, uint8_t *, size_t *, + const uint8_t *, size_t); +int lc_cipher_decrypt_final(struct lc_cipher_ctx *, uint8_t *, size_t *); +int lc_cipher_decrypt(const struct lc_cipher_impl *, const uint8_t *, + size_t, const uint8_t *, size_t, uint8_t *, size_t *, + const uint8_t *, size_t); + +struct lc_cipher_ctx *lc_cipher_ctx_new(const struct lc_cipher_impl *); +void lc_cipher_ctx_free(struct lc_cipher_ctx *); + +const struct lc_cipher_impl *lc_cipher_impl_chacha20(void); + + +/* + * Authenticated encryption with additional data. + */ + +struct lc_aead_impl; + + +int lc_aead_seal(const struct lc_aead_impl *, const uint8_t *, size_t, + const uint8_t *, size_t, uint8_t *, size_t *, const uint8_t *, size_t, + const uint8_t *, size_t); +int lc_aead_open(const struct lc_aead_impl *, const uint8_t *, size_t, + const uint8_t *, size_t, uint8_t *, size_t *, const uint8_t *, size_t, + const uint8_t *, size_t); + +const struct lc_aead_impl *lc_aead_impl_chacha20_poly1305(void); diff --git a/util.c b/util.c new file mode 100644 index 0000000..1100225 --- /dev/null +++ b/util.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "util.h" + + +void +lc_scrub(void *b, size_t len) +{ + explicit_bzero(b, len); +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..e1de831 --- /dev/null +++ b/util.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + + +#define nelems(_a) (sizeof((_a)) / sizeof((_a)[0])) + + +/* + * External definitions. + */ + +void lc_scrub(void *, size_t); + + +/* + * Endianness. + */ + +static inline uint16_t +load16le(const uint8_t *x) +{ + return x[0] | (x[1] << 8); +} + +static inline uint32_t +load32le(const uint8_t *x) +{ + return x[0] | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); +} + +static inline uint64_t +load64le(const uint8_t *x) +{ + return load32le(x) | (((uint64_t)load32le(x + 4)) << 32); +} + +static inline void +store16le(uint8_t *x, uint64_t v) +{ + x[0] = v & 0xff; + x[1] = v >> 8; +} + +static inline void +store32le(uint8_t *x, uint32_t v) +{ + x[0] = v & 0xff; + x[1] = (v >> 8) & 0xff; + x[2] = (v >> 16) & 0xff; + x[3] = v >> 24; +} + +static inline void +store64le(uint8_t *x, uint64_t v) +{ + x[0] = v & 0xff; + x[1] = (v >> 8) & 0xff; + x[2] = (v >> 16) & 0xff; + x[3] = (v >> 24) & 0xff; + x[4] = (v >> 32) & 0xff; + x[5] = (v >> 40) & 0xff; + x[6] = (v >> 48) & 0xff; + x[7] = v >> 56; +} + + +/* + * rotr and rotl. + */ + +static inline uint32_t +rotl32(uint32_t x, uint32_t r) +{ + return (x << r) | (x >> (32 - r)); +} + +static inline uint64_t +rotl64(uint64_t x, uint64_t r) +{ + return (x << r) | (x >> (64 - r)); +} + +static inline uint32_t +rotr32(uint32_t x, uint32_t r) +{ + return rotl32(x, 32 - r); +} + +static inline uint64_t +rotr64(uint64_t x, uint64_t r) +{ + return rotl64(x, 64 - r); +} diff --git a/wycheproof/Makefile b/wycheproof/Makefile new file mode 100644 index 0000000..0c26648 --- /dev/null +++ b/wycheproof/Makefile @@ -0,0 +1,25 @@ +.PATH: ${.CURDIR}/.. + +AEAD= wycheproof_aead + +PROGS= ${AEAD} +NOMAN= noman + +SRCS_wycheproof_aead= wycheproof_aead.c + +LDADD+= ${.CURDIR}/../lib/obj/liblilcrypto.a + + +tests: all tests-aead + +tests-aead: +.ifndef WYCHEPROOF_DIR + @echo Undefined WYCHEPROOF_DIR; false +.endif +.for p in ${AEAD} + perl ${.CURDIR}/aead.pl -x ./${p} \ + ${WYCHEPROOF_DIR}/testvectors/chacha20_poly1305_test.json \ + ${WYCHEPROOF_DIR}/testvectors_v1/chacha20_poly1305_test.json +.endfor + +.include diff --git a/wycheproof/aead.pl b/wycheproof/aead.pl new file mode 100644 index 0000000..5cb45a9 --- /dev/null +++ b/wycheproof/aead.pl @@ -0,0 +1,69 @@ +#!/usr/bin/env perl +use v5.38;; +use strict; +use warnings; + +use Getopt::Std; +use JSON::PP; + +my $progname = $0 =~ s@.*/@@r; + +sub slurp ($fh) { local $/; <$fh> } + +sub usage () +{ + say STDERR "Usage: $progname -x runner json_file [json_files ...]"; + exit 1; +} + +sub main () +{ + my %opts; + my $rc = 0; + + getopts("x:", \%opts) && @ARGV > 0 or usage; + usage unless defined $opts{"x"}; + + for my $f (@ARGV) { + open(my $fh, "<", $f) or die "open failed: $!"; + + my $json = decode_json(slurp($fh)); + for my $testgroup ($json->{testGroups}->@*) { + for my $test ($testgroup->{tests}->@*) { + my @args; + + push(@args, $json->{algorithm}); + push(@args, "-a", $test->{aad}); + push(@args, "-c", $test->{ct}); + push(@args, "-I", $testgroup->{ivSize}); + push(@args, "-i", $test->{iv}); + push(@args, "-K", $testgroup->{keySize}); + push(@args, "-k", $test->{key}); + push(@args, "-m", $test->{msg}); + push(@args, "-T", $testgroup->{tagSize}); + push(@args, "-t", $test->{tag}); + + open(my $th, "-|", $opts{"x"}, @args) or die; + my $result = slurp($th); + close($th); + + chomp($result); + if ($result ne $test->{result}) { + $rc = 1; + say STDERR "case $test->{tcId}: ", + "expected $test->{result}: ", + "$test->{comment} [", + join(",", $test->{flags}->@*), + "]"; + } + } + } + + close($fh); + } + + say "ALL TESTS PASSED!" if $rc == 0; + return $rc; +} + +exit main; diff --git a/wycheproof_aead.c b/wycheproof_aead.c new file mode 100644 index 0000000..fc4819b --- /dev/null +++ b/wycheproof_aead.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2024 Lucas Gabriel Vuotto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lilcrypto.h" + + +#define nelems(_a) (sizeof((_a)) / sizeof((_a)[0])) + + +static inline uint8_t +hex2num(char s) +{ + return s >= 'A' ? 10 + (s >= 'a' ? s - 'a' : s - 'A') : s - '0'; +} + +static int +hexparse(const char *s, uint8_t *out, size_t *outlen) +{ + size_t l; + + l = strlen(s); + if (l % 2 != 0) + return 0; + + if (out == NULL) { + *outlen = l / 2; + return 1; + } + + *outlen = 0; + while (*s != '\0') { + if (!isxdigit(s[0]) || !isxdigit(s[1])) + return 0; + *out++ = (hex2num(s[0]) << 4) | hex2num(s[1]); + (*outlen)++; + s += 2; + } + + return 1; +} + +static void +hexdump(FILE *fp, const uint8_t *blob, size_t len) +{ + size_t i, off; + int pad; + + for (i = 0; len > (1 << (8 * i)); i++) + ; + pad = (i + 1) * 2; + + off = 0; + while (len >= 16) { + fprintf(fp, "%0*zx\t", pad, off); + + for (i = 0; i < 8; i++) + fprintf(fp, "%02x ", blob[i]); + for (; i < 16; i++) + fprintf(fp, " %02x", blob[i]); + + fprintf(fp, "\t|"); + for (i = 0; i < 16; i++) + fprintf(fp, "%c", isprint(blob[i]) ? blob[i] : '.'); + fprintf(fp, "|\n"); + + blob += 16; + off += 16; + len -= 16; + } + + if (len == 0) + goto out; + + fprintf(fp, "%0*zx\t", pad, off); + for (i = 0; i < len && i < 8; i++) + fprintf(fp, "%02x ", blob[i]); + for (; i < 8; i++) + fprintf(fp, " "); + for (; i < len && i < 16; i++) + fprintf(fp, " %02x", blob[i]); + for (; i < 16; i++) + fprintf(fp, " "); + + fprintf(fp, "\t|"); + for (i = 0; i < len; i++) + fprintf(fp, "%c", isprint(blob[i]) ? blob[i] : '.'); + fprintf(fp, "|\n"); + + out: + fprintf(fp, "%0*zx\n", pad, off + len); + fflush(fp); +} + +struct kwimpl { + const char *kw; + const struct lc_aead_impl *(*impl)(void); +}; + +static int +kwimpl_cmp(const void *k0, const void *h0) +{ + const struct kwimpl *h = h0; + const char *k = k0; + + return strcmp(k, h->kw); +} + +static const struct lc_aead_impl * +kw2impl(const char *s) +{ + static const struct kwimpl tbl[] = { + { "CHACHA20-POLY1305", &lc_aead_impl_chacha20_poly1305 }, + }; + struct kwimpl *match; + + match = bsearch(s, tbl, nelems(tbl), sizeof(struct kwimpl), + &kwimpl_cmp); + + return match != NULL ? match->impl() : NULL; +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n", getprogname()); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + const struct lc_aead_impl *impl; + uint8_t *aad, *ct, *iv, *key, *msg, *tag, *out; + const char *errstr; + size_t aadlen, ctlen, ivlen, keylen, msglen, taglen; + size_t ivlenarg, keylenarg, taglenarg; + size_t l, outlen; + int aflag, cflag, Iflag, iflag, Kflag, kflag, mflag, + Tflag, tflag; + int ch; + + if (argc < 2) + usage(); + + impl = kw2impl(argv[1]); + if (impl == NULL) + errx(1, "unsupported algorithm: %s", argv[1]); + + optind = 2; + aflag = cflag = Iflag = iflag = Kflag = kflag = mflag = Tflag = tflag = + 0; + while ((ch = getopt(argc, argv, "a:c:I:i:K:k:m:T:t:")) != -1) { + switch (ch) { + case 'a': + aflag = 1; + (void)hexparse(optarg, NULL, &aadlen); + if (aadlen != 0) { + aad = malloc(aadlen); + if (aad == NULL) + err(1, "out of memory"); + } else + aad = NULL; + if (!hexparse(optarg, aad, &l) || l != aadlen) + errx(1, "invalid hex string: %s", optarg); + break; + case 'c': + cflag = 1; + (void)hexparse(optarg, NULL, &ctlen); + if (ctlen != 0) { + ct = malloc(ctlen); + if (ct == NULL) + err(1, "out of memory"); + } else + ct = NULL; + if (!hexparse(optarg, ct, &l) || l != ctlen) + errx(1, "invalid hex string: %s", optarg); + break; + case 'I': + Iflag = 1; + ivlenarg = strtonum(optarg, 0, LLONG_MAX, &errstr); + if (errstr != NULL) + errx(1, "ivlen is %s: %s", errstr, optarg); + break; + case 'i': + iflag = 1; + (void)hexparse(optarg, NULL, &ivlen); + if (ivlen != 0) { + iv = malloc(ivlen); + if (iv == NULL) + err(1, "out of memory"); + } else + iv = NULL; + if (!hexparse(optarg, iv, &l) || l != ivlen) + errx(1, "invalid hex string: %s", optarg); + break; + case 'K': + Kflag = 1; + keylenarg = strtonum(optarg, 0, LLONG_MAX, &errstr); + if (errstr != NULL) + errx(1, "keylen is %s: %s", errstr, optarg); + if (keylenarg % 8 != 0) + errx(1, "unsupport K value: %zu", keylenarg); + break; + case 'k': + kflag = 1; + (void)hexparse(optarg, NULL, &keylen); + if (keylen != 0) { + key = malloc(keylen); + if (key == NULL) + err(1, "out of memory"); + } else + key = NULL; + if (!hexparse(optarg, key, &l) || l != keylen) + errx(1, "invalid hex string: %s", optarg); + break; + case 'm': + mflag = 1; + (void)hexparse(optarg, NULL, &msglen); + if (msglen != 0) { + msg = malloc(msglen); + if (msg == NULL) + err(1, "out of memory"); + } else + msg = NULL; + if (!hexparse(optarg, msg, &l) || l != msglen) + errx(1, "invalid hex string: %s", optarg); + break; + case 'T': + Tflag = 1; + taglenarg = strtonum(optarg, 0, LLONG_MAX, &errstr); + if (errstr != NULL) + errx(1, "taglen is %s: %s", errstr, optarg); + break; + case 't': + tflag = 1; + (void)hexparse(optarg, NULL, &taglen); + if (taglen != 0) { + tag = malloc(taglen); + if (tag == NULL) + err(1, "out of memory"); + } else + tag = NULL; + if (!hexparse(optarg, tag, &l) || l != taglen) + errx(1, "invalid hex string: %s", optarg); + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (!(aflag && cflag && Iflag && iflag && Kflag && kflag && mflag && + Tflag && tflag)) + errx(1, "missing required arguments"); + + if (!lc_aead_seal(impl, key, keylenarg, iv, ivlenarg, NULL, &outlen, + aad, aadlen, msg, msglen)) { + puts("invalid"); + return 1; + } + + ivlenarg /= 8; + keylenarg /= 8; + taglenarg /= 8; + + out = malloc(outlen); + if (out == NULL) + err(1, "out of memory"); + + if (!lc_aead_seal(impl, key, keylenarg, iv, ivlenarg, out, &outlen, + aad, aadlen, msg, msglen)) { + puts("invalid"); + return 1; + } + + if (ctlen != outlen - LC_POLY1305_TAGLEN || + lc_ct_cmp(out, ct, ctlen) != 0) { + fprintf(stderr, "ct (%zu, %zu)\n", ctlen, + outlen - LC_POLY1305_TAGLEN); + hexdump(stderr, msg, msglen); + fprintf(stderr, "\n"); + hexdump(stderr, ct, ctlen); + fprintf(stderr, "\n"); + hexdump(stderr, out, outlen - LC_POLY1305_TAGLEN); + fprintf(stderr, "\n"); + puts("invalid"); + return 1; + } + if (taglenarg != LC_POLY1305_TAGLEN || + lc_ct_cmp(out + ctlen, tag, LC_POLY1305_TAGLEN) != 0) { + fprintf(stderr, "tag (%zu, %zu)\n", taglenarg, + (size_t)LC_POLY1305_TAGLEN); + hexdump(stderr, tag, taglen); + fprintf(stderr, "\n"); + hexdump(stderr, out + ctlen, LC_POLY1305_TAGLEN); + fprintf(stderr, "\n"); + puts("invalid"); + return 1; + } + + puts("valid"); + return 0; +}