diff --git a/aead.c b/aead.c index 28289da..53857f6 100644 --- a/aead.c +++ b/aead.c @@ -14,21 +14,96 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + #include "internal.h" int -lc_aead_seal(const struct lc_aead_impl *impl, uint8_t *out, size_t *outlen, - void *initparams, const uint8_t *aad, size_t aadlen, const uint8_t *in, - size_t inlen) +lc_aead_seal_init(struct lc_aead_ctx *ctx, void *initparams) { - return impl->seal(out, outlen, initparams, aad, aadlen, in, inlen); + return ctx->impl->seal_init(ctx->arg, initparams); +} + +int +lc_aead_seal_update(struct lc_aead_ctx *ctx, uint8_t *out, size_t *outlen, + const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) +{ + return ctx->impl->seal_update(ctx->arg, out, outlen, aad, aadlen, in, + inlen); +} + +int +lc_aead_seal_final(struct lc_aead_ctx *ctx, uint8_t *out, size_t *outlen, + uint8_t *tag, size_t *taglen) +{ + return ctx->impl->seal_final(ctx->arg, out, outlen, tag, taglen); +} + +int +lc_aead_seal(const struct lc_aead_impl *impl, uint8_t *out, size_t *outlen, + uint8_t *tag, size_t *taglen, void *initparams, const uint8_t *aad, + size_t aadlen, const uint8_t *in, size_t inlen) +{ + return impl->seal(out, outlen, tag, taglen, initparams, aad, aadlen, + in, inlen); +} + +int +lc_aead_open_init(struct lc_aead_ctx *ctx, void *initparams) +{ + return ctx->impl->open_init(ctx->arg, initparams); +} + +int +lc_aead_open_update(struct lc_aead_ctx *ctx, uint8_t *out, size_t *outlen, + const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) +{ + return ctx->impl->open_update(ctx->arg, out, outlen, aad, aadlen, in, + inlen); +} + +int +lc_aead_open_final(struct lc_aead_ctx *ctx, uint8_t *out, size_t *outlen, + const uint8_t *tag, size_t taglen) +{ + return ctx->impl->open_final(ctx->arg, out, outlen, tag, taglen); } int lc_aead_open(const struct lc_aead_impl *impl, uint8_t *out, size_t *outlen, - void *initparams, const uint8_t *aad, size_t aadlen, const uint8_t *in, - size_t inlen) + void *initparams, const uint8_t *tag, size_t taglen, const uint8_t *aad, + size_t aadlen, const uint8_t *in, size_t inlen) { - return impl->open(out, outlen, initparams, aad, aadlen, in, inlen); + return impl->open(out, outlen, initparams, tag, taglen, aad, aadlen, + in, inlen); +} + +struct lc_aead_ctx * +lc_aead_ctx_new(const struct lc_aead_impl *impl) +{ + struct lc_aead_ctx *ctx; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + if (impl->argsz > 0) { + ctx->arg = malloc(impl->argsz); + if (ctx->arg == NULL) { + free(ctx); + return NULL; + } + } else + ctx->arg = NULL; + ctx->impl = impl; + + return ctx; +} + +void +lc_aead_ctx_free(struct lc_aead_ctx *ctx) +{ + if (ctx != NULL) + free(ctx->arg); + free(ctx); } diff --git a/aead_chacha20_poly1305.c b/aead_chacha20_poly1305.c index b9e2e2d..872eeac 100644 --- a/aead_chacha20_poly1305.c +++ b/aead_chacha20_poly1305.c @@ -14,6 +14,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include + #include "internal.h" #include "util.h" @@ -23,115 +26,260 @@ * according to draft-irtf-cfrg-xchacha-03. */ -static int -poly1305_keysetup(struct lc_cipher_ctx *cctx, - uint8_t akey[LC_POLY1305_KEYLEN], void *initparams) -{ - size_t akeylen; +enum aead_mode { + AEAD_SEAL, + AEAD_OPEN, +}; - return lc_cipher_encrypt(cctx->impl, akey, &akeylen, initparams, - zerobuf, LC_POLY1305_KEYLEN) && akeylen == LC_POLY1305_KEYLEN; -} static int -chacha20_poly1305_seal(uint8_t *out, size_t *outlen, void *initparams, - const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) +chacha20_poly1305_anycrypt_init(void *arg, void *initparams, enum aead_mode m) { struct lc_chacha20_poly1305_params *params = initparams; - struct lc_cipher_ctx *cctx = NULL; - struct lc_auth_ctx *actx = NULL; + struct chacha20_poly1305_state *state = arg; struct lc_chacha20_params cparams; struct lc_poly1305_params aparams; - uint8_t buf[sizeof(uint64_t) * 2]; - size_t i, olen; - int ret = 0; + size_t olen; - *outlen = 0; - /* inlen and aadlen are capped by design; enough space of tag. */ - if (inlen > UINT64_MAX || aadlen > UINT64_MAX || - inlen > SIZE_MAX - LC_POLY1305_TAGLEN) - return 0; - /* Counter 0 is used for deriving Poly1305 key. */ - if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || - (inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN > - CHACHA20_CTRMAX - 1) + if (params->cipher->impl != lc_cipher_impl_chacha20() || + params->auth->impl != lc_auth_impl_poly1305()) return 0; - if (out == NULL) { - *outlen = inlen + LC_POLY1305_TAGLEN; - return 1; - } + state->auth = params->auth; + state->cipher = params->cipher; + state->aadlen = state->ctlen = 0; + state->aaddone = 0; - cctx = lc_cipher_ctx_new(lc_cipher_impl_chacha20()); - if (cctx == NULL) - goto cleanup; - actx = lc_auth_ctx_new(lc_auth_impl_poly1305()); - if (actx == NULL) - goto cleanup; - - for (i = 0; i < sizeof(params->key); i++) - cparams.key[i] = params->key[i]; - for (i = 0; i < sizeof(params->nonce); i++) - cparams.nonce[i] = params->nonce[i]; + memcpy(cparams.key, params->key, sizeof(params->key)); + memcpy(cparams.nonce, params->nonce, sizeof(params->nonce)); cparams.counter = 0; - if (!poly1305_keysetup(cctx, aparams.key, &cparams)) - goto cleanup; - - if (!lc_auth_init(actx, &aparams) || - !lc_auth_update(actx, aad, aadlen)) - goto cleanup; - if (aadlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (aadlen % 16))) - goto cleanup; + if (!lc_cipher_encrypt(state->cipher->impl, aparams.key, &olen, + &cparams, zerobuf, LC_POLY1305_KEYLEN)) + return 0; + if (!lc_auth_init(state->auth, &aparams)) + return 0; cparams.counter = 1; - if (!lc_cipher_encrypt(cctx->impl, out, outlen, &cparams, in, inlen)) - goto cleanup; - if (!lc_auth_update(actx, out, inlen)) - goto cleanup; - if (inlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (inlen % 16))) - goto cleanup; + switch (m) { + case AEAD_SEAL: + if (!lc_cipher_encrypt_init(state->cipher, &cparams)) + return 0; + break; + case AEAD_OPEN: + if (!lc_cipher_decrypt_init(state->cipher, &cparams)) + return 0; + break; + default: + return 0; + } - store64le(&buf[0], aadlen); - store64le(&buf[sizeof(uint64_t)], inlen); - if (!lc_auth_update(actx, buf, sizeof(buf)) || - !lc_auth_final(actx, out + inlen, &olen)) - goto cleanup; - *outlen += olen; - if (*outlen != inlen + LC_POLY1305_TAGLEN) - goto cleanup; - ret = 1; - - cleanup: - lc_scrub(buf, sizeof(buf)); - lc_scrub(&aparams, sizeof(aparams)); - lc_scrub(&cparams, sizeof(cparams)); - lc_auth_ctx_free(actx); - lc_cipher_ctx_free(cctx); - - return ret; + return 1; } static int -xchacha20_poly1305_seal(uint8_t *out, size_t *outlen, void *initparams, - const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) +xchacha20_poly1305_anycrypt_init(void *arg, void *initparams, enum aead_mode m) { struct lc_xchacha20_poly1305_params *params = initparams; - struct lc_cipher_ctx *cctx = NULL; - struct lc_auth_ctx *actx = NULL; + struct chacha20_poly1305_state *state = arg; struct lc_xchacha20_params cparams; struct lc_poly1305_params aparams; - uint8_t buf[sizeof(uint64_t) * 2]; - size_t i, olen; - int ret = 0; + size_t olen; + + if (params->cipher->impl != lc_cipher_impl_xchacha20() || + params->auth->impl != lc_auth_impl_poly1305()) + return 0; + + state->auth = params->auth; + state->cipher = params->cipher; + state->aadlen = state->ctlen = 0; + state->aaddone = 0; + + memcpy(cparams.key, params->key, sizeof(params->key)); + memcpy(cparams.nonce, params->nonce, sizeof(params->nonce)); + + cparams.counter = 0; + if (!lc_cipher_encrypt(state->cipher->impl, aparams.key, &olen, + &cparams, zerobuf, LC_POLY1305_KEYLEN)) + return 0; + + if (!lc_auth_init(state->auth, &aparams)) + return 0; + cparams.counter = 1; + + switch (m) { + case AEAD_SEAL: + if (!lc_cipher_encrypt_init(state->cipher, &cparams)) + return 0; + break; + case AEAD_OPEN: + if (!lc_cipher_decrypt_init(state->cipher, &cparams)) + return 0; + break; + default: + return 0; + } + + return 1; +} + +static int +c20_xc20_poly1305_anycrypt_update(void *arg, uint8_t *out, size_t *outlen, + const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen, + enum aead_mode m) +{ + struct chacha20_poly1305_state *state = arg; + size_t ctlen; *outlen = 0; - /* inlen and aadlen are capped by design; enough space of tag. */ - if (inlen > UINT64_MAX || aadlen > UINT64_MAX || - inlen > SIZE_MAX - LC_POLY1305_TAGLEN) + switch (m) { + case AEAD_SEAL: + if (!lc_cipher_encrypt_update(state->cipher, NULL, &ctlen, in, + inlen)) + return 0; + break; + case AEAD_OPEN: + if (!lc_cipher_decrypt_update(state->cipher, NULL, &ctlen, in, + inlen)) + return 0; + break; + default: + if (!lc_cipher_decrypt_update(state->cipher, NULL, &ctlen, in, + inlen)) + return 0; + return 0; + } + + if (aadlen > UINT64_MAX - state->aadlen || + ctlen > UINT64_MAX - state->ctlen) + return 0; + if (aadlen > 0 && state->aaddone) + return 0; + + if (out == NULL) { + *outlen = ctlen; + return 1; + } + + if (aadlen > 0) { + if (!lc_auth_update(state->auth, aad, aadlen)) + return 0; + state->aadlen += aadlen; + } + + if (inlen > 0) { + if (!state->aaddone) { + if (state->aadlen % 16 != 0 && + !lc_auth_update(state->auth, zerobuf, + 16 - (state->aadlen % 16))) + return 0; + state->aaddone = 1; + } + + switch (m) { + case AEAD_SEAL: + if (!lc_cipher_encrypt_update(state->cipher, out, + outlen, in, inlen)) + return 0; + if (!lc_auth_update(state->auth, out, *outlen)) + return 0; + state->ctlen += *outlen; + break; + case AEAD_OPEN: + if (!lc_auth_update(state->auth, in, inlen)) + return 0; + if (!lc_cipher_decrypt_update(state->cipher, out, + outlen, in, inlen)) + return 0; + state->ctlen += inlen; + break; + default: + return 0; + } + } + + return 1; +} + +static int +chacha20_poly1305_seal_init(void *arg, void *initparams) +{ + return chacha20_poly1305_anycrypt_init(arg, initparams, AEAD_SEAL); +} + +static int +xchacha20_poly1305_seal_init(void *arg, void *initparams) +{ + return xchacha20_poly1305_anycrypt_init(arg, initparams, AEAD_SEAL); +} + +static int +c20_xc20_poly1305_seal_update(void *arg, uint8_t *out, size_t *outlen, + const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) +{ + return c20_xc20_poly1305_anycrypt_update(arg, out, outlen, aad, aadlen, + in, inlen, AEAD_SEAL); +} + +static int +c20_xc20_poly1305_seal_final(void *arg, uint8_t *out, size_t *outlen, + uint8_t *tag, size_t *taglen) +{ + struct chacha20_poly1305_state *state = arg; + uint8_t buf[sizeof(uint64_t) * 2]; + size_t ctlen; + + *outlen = *taglen = 0; + if (!lc_cipher_encrypt_final(state->cipher, NULL, &ctlen)) + return 0; + if (ctlen > UINT64_MAX - state->ctlen) + return 0; + + if (out == NULL || tag == NULL) { + *outlen = ctlen; + *taglen = LC_POLY1305_TAGLEN; + return 1; + } + + if (!state->aaddone) { + if (state->aadlen % 16 != 0 && + !lc_auth_update(state->auth, zerobuf, + 16 - (state->aadlen % 16))) + return 0; + state->aaddone = 1; + } + + if (!lc_cipher_encrypt_final(state->cipher, out, outlen)) + return 0; + + if (!lc_auth_update(state->auth, out, *outlen)) + return 0; + state->ctlen += *outlen; + if (state->ctlen % 16 != 0 && + !lc_auth_update(state->auth, zerobuf, 16 - (state->ctlen % 16))) + return 0; + + store64le(&buf[0], state->aadlen); + store64le(&buf[sizeof(uint64_t)], state->ctlen); + if (!lc_auth_update(state->auth, buf, sizeof(buf)) || + !lc_auth_final(state->auth, tag, taglen)) + return 0; + + return 1; +} + +static int +chacha20_poly1305_seal(uint8_t *out, size_t *outlen, uint8_t *tag, + size_t *taglen, void *initparams, const uint8_t *aad, size_t aadlen, + const uint8_t *in, size_t inlen) +{ + struct chacha20_poly1305_state state; + size_t olen; + + *outlen = *taglen = 0; + /* inlen and aadlen are capped by design. */ + if (inlen > UINT64_MAX || aadlen > UINT64_MAX) return 0; /* Counter 0 is used for deriving Poly1305 key. */ if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || @@ -139,238 +287,200 @@ xchacha20_poly1305_seal(uint8_t *out, size_t *outlen, void *initparams, CHACHA20_CTRMAX - 1) return 0; - if (out == NULL) { - *outlen = inlen + LC_POLY1305_TAGLEN; + if (out == NULL || tag == NULL) { + *outlen = inlen; + *taglen = LC_POLY1305_TAGLEN; return 1; } - cctx = lc_cipher_ctx_new(lc_cipher_impl_xchacha20()); - if (cctx == NULL) - goto cleanup; - actx = lc_auth_ctx_new(lc_auth_impl_poly1305()); - if (actx == NULL) - goto cleanup; - - for (i = 0; i < sizeof(params->key); i++) - cparams.key[i] = params->key[i]; - for (i = 0; i < sizeof(params->nonce); i++) - cparams.nonce[i] = params->nonce[i]; - - cparams.counter = 0; - if (!poly1305_keysetup(cctx, aparams.key, &cparams)) - goto cleanup; - - if (!lc_auth_init(actx, &aparams) || - !lc_auth_update(actx, aad, aadlen)) - goto cleanup; - if (aadlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (aadlen % 16))) - goto cleanup; - - cparams.counter = 1; - if (!lc_cipher_encrypt(cctx->impl, out, outlen, &cparams, in, inlen)) - goto cleanup; - - if (!lc_auth_update(actx, out, inlen)) - goto cleanup; - if (inlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (inlen % 16))) - goto cleanup; - - store64le(&buf[0], aadlen); - store64le(&buf[sizeof(uint64_t)], inlen); - if (!lc_auth_update(actx, buf, sizeof(buf)) || - !lc_auth_final(actx, out + inlen, &olen)) - goto cleanup; + if (!chacha20_poly1305_anycrypt_init(&state, initparams, AEAD_SEAL)) + return 0; + if (!c20_xc20_poly1305_anycrypt_update(&state, out, &olen, aad, aadlen, + in, inlen, AEAD_SEAL)) + return 0; + *outlen = olen; + if (!c20_xc20_poly1305_seal_final(&state, out + olen, &olen, tag, + taglen)) + return 0; *outlen += olen; - if (*outlen != inlen + LC_POLY1305_TAGLEN) - goto cleanup; - ret = 1; - cleanup: - lc_scrub(buf, sizeof(buf)); - lc_scrub(&aparams, sizeof(aparams)); - lc_scrub(&cparams, sizeof(cparams)); - lc_auth_ctx_free(actx); - lc_cipher_ctx_free(cctx); + return 1; +} - return ret; +static int +xchacha20_poly1305_seal(uint8_t *out, size_t *outlen, uint8_t *tag, + size_t *taglen, void *initparams, const uint8_t *aad, size_t aadlen, + const uint8_t *in, size_t inlen) +{ + struct chacha20_poly1305_state state; + size_t olen; + + *outlen = *taglen = 0; + /* inlen and aadlen are capped by design. */ + if (inlen > UINT64_MAX || aadlen > UINT64_MAX) + return 0; + /* Counter 0 is used for deriving Poly1305 key. */ + if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || + (inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN > + CHACHA20_CTRMAX - 1) + return 0; + + if (out == NULL || tag == NULL) { + *outlen = inlen; + *taglen = LC_POLY1305_TAGLEN; + return 1; + } + + if (!xchacha20_poly1305_anycrypt_init(&state, initparams, AEAD_SEAL)) + return 0; + if (!c20_xc20_poly1305_anycrypt_update(&state, out, &olen, aad, aadlen, + in, inlen, AEAD_SEAL)) + return 0; + *outlen = olen; + if (!c20_xc20_poly1305_seal_final(&state, out + olen, &olen, tag, + taglen)) + return 0; + *outlen += olen; + + return 1; +} + +static int +chacha20_poly1305_open_init(void *arg, void *initparams) +{ + return chacha20_poly1305_anycrypt_init(arg, initparams, AEAD_OPEN); +} + +static int +xchacha20_poly1305_open_init(void *arg, void *initparams) +{ + return xchacha20_poly1305_anycrypt_init(arg, initparams, AEAD_OPEN); +} + +static int +c20_xc20_poly1305_open_update(void *arg, uint8_t *out, size_t *outlen, + const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) +{ + return c20_xc20_poly1305_anycrypt_update(arg, out, outlen, aad, aadlen, + in, inlen, AEAD_OPEN); +} + +static int +c20_xc20_poly1305_open_final(void *arg, uint8_t *out, size_t *outlen, + const uint8_t *tag, size_t taglen) +{ + struct chacha20_poly1305_state *state = arg; + uint8_t buf[sizeof(uint64_t) * 2], + ctag[LC_POLY1305_TAGLEN]; + size_t ctlen, ctaglen; + + *outlen = 0; + if (!lc_cipher_decrypt_final(state->cipher, NULL, &ctlen)) + return 0; + if (ctlen > UINT64_MAX - state->ctlen || + taglen != LC_POLY1305_TAGLEN) + return 0; + + if (out == NULL) { + *outlen = ctlen; + return 1; + } + + if (!state->aaddone) { + if (state->aadlen % 16 != 0 && + !lc_auth_update(state->auth, zerobuf, + 16 - (state->aadlen % 16))) + return 0; + state->aaddone = 1; + } + + if (state->ctlen % 16 != 0 && + !lc_auth_update(state->auth, zerobuf, 16 - (state->ctlen % 16))) + return 0; + + store64le(&buf[0], state->aadlen); + store64le(&buf[sizeof(uint64_t)], state->ctlen); + if (!lc_auth_update(state->auth, buf, sizeof(buf)) || + !lc_auth_final(state->auth, ctag, &ctaglen)) + return 0; + if (!lc_ct_cmp(ctag, tag, LC_POLY1305_TAGLEN)) + return 0; + + return lc_cipher_decrypt_final(state->cipher, out, outlen); } static int chacha20_poly1305_open(uint8_t *out, size_t *outlen, void *initparams, - const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) + const uint8_t *tag, size_t taglen, const uint8_t *aad, size_t aadlen, + const uint8_t *in, size_t inlen) { - struct lc_chacha20_poly1305_params *params = initparams; - struct lc_cipher_ctx *cctx = NULL; - struct lc_auth_ctx *actx = NULL; - struct lc_chacha20_params cparams; - struct lc_poly1305_params aparams; - uint8_t tag[LC_POLY1305_TAGLEN]; - uint8_t buf[sizeof(uint64_t) * 2]; - size_t i, olen, ctlen; - int ret = 0; + struct chacha20_poly1305_state state; + size_t olen; *outlen = 0; - /* inlen includes the tag; inlen and aadlen are capped by design. */ - if (inlen < LC_POLY1305_TAGLEN || - inlen > UINT64_MAX || aadlen > UINT64_MAX) + /* inlen and aadlen are capped by design. */ + if (inlen > UINT64_MAX || aadlen > UINT64_MAX) return 0; /* Counter 0 is used for deriving Poly1305 key. */ if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || (inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN > - CHACHA20_CTRMAX - 1) { + CHACHA20_CTRMAX - 1) return 0; - } if (out == NULL) { - *outlen = inlen - LC_POLY1305_TAGLEN; + *outlen = inlen; return 1; } - cctx = lc_cipher_ctx_new(lc_cipher_impl_chacha20()); - if (cctx == NULL) - goto cleanup; - actx = lc_auth_ctx_new(lc_auth_impl_poly1305()); - if (actx == NULL) - goto cleanup; + if (!chacha20_poly1305_anycrypt_init(&state, initparams, AEAD_OPEN)) + return 0; + if (!c20_xc20_poly1305_anycrypt_update(&state, out, &olen, aad, aadlen, + in, inlen, AEAD_OPEN)) + return 0; + *outlen = olen; + if (!c20_xc20_poly1305_open_final(&state, out + olen, &olen, tag, + taglen)) + return 0; + *outlen += olen; - for (i = 0; i < sizeof(params->key); i++) - cparams.key[i] = params->key[i]; - for (i = 0; i < sizeof(params->nonce); i++) - cparams.nonce[i] = params->nonce[i]; - - cparams.counter = 0; - if (!poly1305_keysetup(cctx, aparams.key, &cparams)) - goto cleanup; - - if (!lc_auth_init(actx, &aparams) || - !lc_auth_update(actx, aad, aadlen)) - goto cleanup; - if (aadlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (aadlen % 16))) - goto cleanup; - - ctlen = inlen - LC_POLY1305_TAGLEN; - if (!lc_auth_update(actx, in, ctlen)) - goto cleanup; - if (ctlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (ctlen % 16))) - goto cleanup; - - store64le(&buf[0], aadlen); - store64le(&buf[sizeof(uint64_t)], ctlen); - if (!lc_auth_update(actx, buf, sizeof(buf)) || - !lc_auth_final(actx, tag, &olen)) - goto cleanup; - if (olen != LC_POLY1305_TAGLEN) - goto cleanup; - if (!lc_ct_cmp(tag, in + ctlen, LC_POLY1305_TAGLEN)) - goto cleanup; - - cparams.counter = 1; - if (!lc_cipher_decrypt(cctx->impl, out, outlen, &cparams, in, ctlen)) - goto cleanup; - - ret = 1; - - cleanup: - lc_scrub(buf, sizeof(buf)); - lc_scrub(&aparams, sizeof(aparams)); - lc_scrub(&cparams, sizeof(cparams)); - lc_scrub(tag, sizeof(tag)); - lc_auth_ctx_free(actx); - lc_cipher_ctx_free(cctx); - - return ret; + return 1; } static int xchacha20_poly1305_open(uint8_t *out, size_t *outlen, void *initparams, - const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen) + const uint8_t *tag, size_t taglen, const uint8_t *aad, size_t aadlen, + const uint8_t *in, size_t inlen) { - struct lc_xchacha20_poly1305_params *params = initparams; - struct lc_cipher_ctx *cctx = NULL; - struct lc_auth_ctx *actx = NULL; - struct lc_xchacha20_params cparams; - struct lc_poly1305_params aparams; - uint8_t tag[LC_POLY1305_TAGLEN]; - uint8_t buf[sizeof(uint64_t) * 2]; - size_t i, olen, ctlen; - int ret = 0; + struct chacha20_poly1305_state state; + size_t olen; *outlen = 0; - /* inlen includes the tag; inlen and aadlen are capped by design. */ - if (inlen < LC_POLY1305_TAGLEN || - inlen > UINT64_MAX || aadlen > UINT64_MAX) + /* inlen and aadlen are capped by design. */ + if (inlen > UINT64_MAX || aadlen > UINT64_MAX) return 0; /* Counter 0 is used for deriving Poly1305 key. */ if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || (inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN > - CHACHA20_CTRMAX - 1) { + CHACHA20_CTRMAX - 1) return 0; - } if (out == NULL) { - *outlen = inlen - LC_POLY1305_TAGLEN; + *outlen = inlen; return 1; } - cctx = lc_cipher_ctx_new(lc_cipher_impl_xchacha20()); - if (cctx == NULL) - goto cleanup; - actx = lc_auth_ctx_new(lc_auth_impl_poly1305()); - if (actx == NULL) - goto cleanup; + if (!xchacha20_poly1305_anycrypt_init(&state, initparams, AEAD_OPEN)) + return 0; + if (!c20_xc20_poly1305_anycrypt_update(&state, out, &olen, aad, aadlen, + in, inlen, AEAD_OPEN)) + return 0; + *outlen = olen; + if (!c20_xc20_poly1305_open_final(&state, out + olen, &olen, tag, + taglen)) + return 0; + *outlen += olen; - for (i = 0; i < sizeof(params->key); i++) - cparams.key[i] = params->key[i]; - for (i = 0; i < sizeof(params->nonce); i++) - cparams.nonce[i] = params->nonce[i]; - - cparams.counter = 0; - if (!poly1305_keysetup(cctx, aparams.key, &cparams)) - goto cleanup; - - if (!lc_auth_init(actx, &aparams) || - !lc_auth_update(actx, aad, aadlen)) - goto cleanup; - if (aadlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (aadlen % 16))) - goto cleanup; - - ctlen = inlen - LC_POLY1305_TAGLEN; - if (!lc_auth_update(actx, in, ctlen)) - goto cleanup; - if (ctlen % 16 != 0) - if (!lc_auth_update(actx, zerobuf, 16 - (ctlen % 16))) - goto cleanup; - - store64le(&buf[0], aadlen); - store64le(&buf[sizeof(uint64_t)], ctlen); - if (!lc_auth_update(actx, buf, sizeof(buf)) || - !lc_auth_final(actx, tag, &olen)) - goto cleanup; - if (olen != LC_POLY1305_TAGLEN) - goto cleanup; - if (!lc_ct_cmp(tag, in + ctlen, LC_POLY1305_TAGLEN)) - goto cleanup; - - cparams.counter = 1; - if (!lc_cipher_decrypt(cctx->impl, out, outlen, &cparams, in, ctlen)) - goto cleanup; - - ret = 1; - - cleanup: - lc_scrub(buf, sizeof(buf)); - lc_scrub(&aparams, sizeof(aparams)); - lc_scrub(&cparams, sizeof(cparams)); - lc_scrub(tag, sizeof(tag)); - lc_auth_ctx_free(actx); - lc_cipher_ctx_free(cctx); - - return ret; + return 1; } @@ -378,9 +488,17 @@ const struct lc_aead_impl * lc_aead_impl_chacha20_poly1305(void) { static struct lc_aead_impl chacha20_poly1305_impl = { + .seal_init = &chacha20_poly1305_seal_init, + .seal_update = &c20_xc20_poly1305_seal_update, + .seal_final = &c20_xc20_poly1305_seal_final, .seal = &chacha20_poly1305_seal, + + .open_init = &chacha20_poly1305_open_init, + .open_update = &c20_xc20_poly1305_open_update, + .open_final = &c20_xc20_poly1305_open_final, .open = &chacha20_poly1305_open, + .argsz = sizeof(struct chacha20_poly1305_state), .blocklen = LC_CHACHA20_BLOCKLEN, }; @@ -391,10 +509,18 @@ const struct lc_aead_impl * lc_aead_impl_xchacha20_poly1305(void) { static struct lc_aead_impl xchacha20_poly1305_impl = { + .seal_init = &xchacha20_poly1305_seal_init, + .seal_update = &c20_xc20_poly1305_seal_update, + .seal_final = &c20_xc20_poly1305_seal_final, .seal = &xchacha20_poly1305_seal, + + .open_init = &xchacha20_poly1305_open_init, + .open_update = &c20_xc20_poly1305_open_update, + .open_final = &c20_xc20_poly1305_open_final, .open = &xchacha20_poly1305_open, - .blocklen = LC_XCHACHA20_BLOCKLEN, + .argsz = sizeof(struct chacha20_poly1305_state), + .blocklen = LC_CHACHA20_BLOCKLEN, }; return &xchacha20_poly1305_impl; diff --git a/internal.h b/internal.h index f73487d..b7e9b01 100644 --- a/internal.h +++ b/internal.h @@ -59,10 +59,21 @@ */ struct lc_aead_impl { - int (*seal)(uint8_t *, size_t *, void *, const uint8_t *, size_t, - const uint8_t *, size_t); + int (*seal_init)(void *, void *); + int (*seal_update)(void *, uint8_t *, size_t *, const uint8_t *, + size_t, const uint8_t *, size_t); + int (*seal_final)(void *, uint8_t *, size_t *, uint8_t *, + size_t *); + int (*seal)(uint8_t *, size_t *, uint8_t *, size_t *, void *, + const uint8_t *, size_t, const uint8_t *, size_t); + + int (*open_init)(void *, void *); + int (*open_update)(void *, uint8_t *, size_t *, const uint8_t *, + size_t, const uint8_t *, size_t); + int (*open_final)(void *, uint8_t *, size_t *, const uint8_t *, + size_t); int (*open)(uint8_t *, size_t *, void *, const uint8_t *, size_t, - const uint8_t *, size_t); + const uint8_t *, size_t, const uint8_t *, size_t); size_t argsz; size_t blocklen; @@ -121,6 +132,11 @@ struct lc_kdf_impl { * a cryptographic algorithm. */ +struct lc_aead_ctx { + const struct lc_aead_impl *impl; + void *arg; +}; + struct lc_auth_ctx { const struct lc_auth_impl *impl; void *arg; @@ -140,6 +156,16 @@ struct lc_hash_ctx { * *_state holds the internal state of the cryptographic algorithms. */ +/* AEAD. */ + +struct chacha20_poly1305_state { + struct lc_auth_ctx *auth; + struct lc_cipher_ctx *cipher; + uint64_t aadlen; + uint64_t ctlen; + int aaddone; +}; + /* Authentication. */ struct hmac_state { diff --git a/lilcrypto.h b/lilcrypto.h index e6fa55d..bd125c2 100644 --- a/lilcrypto.h +++ b/lilcrypto.h @@ -58,6 +58,7 @@ * Structs. */ +struct lc_aead_ctx; struct lc_aead_impl; struct lc_auth_ctx; @@ -104,13 +105,17 @@ struct lc_xchacha20_params { /* AEAD. */ struct lc_chacha20_poly1305_params { - uint8_t key[LC_CHACHA20_KEYLEN]; - uint8_t nonce[LC_CHACHA20_NONCELEN]; + struct lc_auth_ctx *auth; + struct lc_cipher_ctx *cipher; + uint8_t key[LC_CHACHA20_KEYLEN]; + uint8_t nonce[LC_CHACHA20_NONCELEN]; }; struct lc_xchacha20_poly1305_params { - uint8_t key[LC_XCHACHA20_KEYLEN]; - uint8_t nonce[LC_XCHACHA20_NONCELEN]; + struct lc_auth_ctx *auth; + struct lc_cipher_ctx *cipher; + uint8_t key[LC_XCHACHA20_KEYLEN]; + uint8_t nonce[LC_XCHACHA20_NONCELEN]; }; /* KDF. */ @@ -200,10 +205,26 @@ const struct lc_cipher_impl *lc_cipher_impl_xchacha20(void); * Authenticated encryption with additional data. */ -int lc_aead_seal(const struct lc_aead_impl *, uint8_t *, size_t *, void *, + +int lc_aead_seal_init(struct lc_aead_ctx *, void *); +int lc_aead_seal_update(struct lc_aead_ctx *, uint8_t *, size_t *, const uint8_t *, size_t, const uint8_t *, size_t); +int lc_aead_seal_final(struct lc_aead_ctx *, uint8_t *, size_t *, uint8_t *, + size_t *); +int lc_aead_seal(const struct lc_aead_impl *, uint8_t *, size_t *, + uint8_t *, size_t *, void *, const uint8_t *, size_t, + const uint8_t *, size_t); +int lc_aead_open_init(struct lc_aead_ctx *, void *); +int lc_aead_open_update(struct lc_aead_ctx *, uint8_t *, size_t *, + const uint8_t *, size_t, const uint8_t *, size_t); +int lc_aead_open_final(struct lc_aead_ctx *, uint8_t *, size_t *, + const uint8_t *, size_t); int lc_aead_open(const struct lc_aead_impl *, uint8_t *, size_t *, void *, - const uint8_t *, size_t, const uint8_t *, size_t); + const uint8_t *, size_t, const uint8_t *, size_t, const uint8_t *, + size_t); + +struct lc_aead_ctx *lc_aead_ctx_new(const struct lc_aead_impl *); +void lc_aead_ctx_free(struct lc_aead_ctx *); const struct lc_aead_impl *lc_aead_impl_chacha20_poly1305(void); const struct lc_aead_impl *lc_aead_impl_xchacha20_poly1305(void);