aead: rewrite api

Decouple the authentication tag from the ciphertext and add streaming
interfaces.

Tests will be fixed in the next commit.
This commit is contained in:
Lucas Gabriel Vuotto 2024-06-20 15:52:58 +00:00
parent 91842048d6
commit 5c1325ed11
4 changed files with 538 additions and 290 deletions

89
aead.c
View File

@ -14,21 +14,96 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <stdlib.h>
#include "internal.h" #include "internal.h"
int int
lc_aead_seal(const struct lc_aead_impl *impl, uint8_t *out, size_t *outlen, lc_aead_seal_init(struct lc_aead_ctx *ctx, void *initparams)
void *initparams, const uint8_t *aad, size_t aadlen, const uint8_t *in,
size_t inlen)
{ {
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 int
lc_aead_open(const struct lc_aead_impl *impl, uint8_t *out, size_t *outlen, 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, void *initparams, const uint8_t *tag, size_t taglen, const uint8_t *aad,
size_t inlen) 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);
} }

View File

@ -14,6 +14,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <limits.h>
#include <string.h>
#include "internal.h" #include "internal.h"
#include "util.h" #include "util.h"
@ -23,115 +26,260 @@
* according to draft-irtf-cfrg-xchacha-03. * according to draft-irtf-cfrg-xchacha-03.
*/ */
static int enum aead_mode {
poly1305_keysetup(struct lc_cipher_ctx *cctx, AEAD_SEAL,
uint8_t akey[LC_POLY1305_KEYLEN], void *initparams) AEAD_OPEN,
{ };
size_t akeylen;
return lc_cipher_encrypt(cctx->impl, akey, &akeylen, initparams,
zerobuf, LC_POLY1305_KEYLEN) && akeylen == LC_POLY1305_KEYLEN;
}
static int static int
chacha20_poly1305_seal(uint8_t *out, size_t *outlen, void *initparams, chacha20_poly1305_anycrypt_init(void *arg, void *initparams, enum aead_mode m)
const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen)
{ {
struct lc_chacha20_poly1305_params *params = initparams; struct lc_chacha20_poly1305_params *params = initparams;
struct lc_cipher_ctx *cctx = NULL; struct chacha20_poly1305_state *state = arg;
struct lc_auth_ctx *actx = NULL;
struct lc_chacha20_params cparams; struct lc_chacha20_params cparams;
struct lc_poly1305_params aparams; struct lc_poly1305_params aparams;
uint8_t buf[sizeof(uint64_t) * 2]; size_t olen;
size_t i, olen;
int ret = 0;
*outlen = 0; if (params->cipher->impl != lc_cipher_impl_chacha20() ||
/* inlen and aadlen are capped by design; enough space of tag. */ params->auth->impl != lc_auth_impl_poly1305())
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)
return 0; return 0;
if (out == NULL) { state->auth = params->auth;
*outlen = inlen + LC_POLY1305_TAGLEN; state->cipher = params->cipher;
return 1; state->aadlen = state->ctlen = 0;
} state->aaddone = 0;
cctx = lc_cipher_ctx_new(lc_cipher_impl_chacha20()); memcpy(cparams.key, params->key, sizeof(params->key));
if (cctx == NULL) memcpy(cparams.nonce, params->nonce, sizeof(params->nonce));
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; cparams.counter = 0;
if (!poly1305_keysetup(cctx, aparams.key, &cparams)) if (!lc_cipher_encrypt(state->cipher->impl, aparams.key, &olen,
goto cleanup; &cparams, zerobuf, LC_POLY1305_KEYLEN))
return 0;
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_auth_init(state->auth, &aparams))
return 0;
cparams.counter = 1; cparams.counter = 1;
if (!lc_cipher_encrypt(cctx->impl, out, outlen, &cparams, in, inlen))
goto cleanup;
if (!lc_auth_update(actx, out, inlen)) switch (m) {
goto cleanup; case AEAD_SEAL:
if (inlen % 16 != 0) if (!lc_cipher_encrypt_init(state->cipher, &cparams))
if (!lc_auth_update(actx, zerobuf, 16 - (inlen % 16))) return 0;
goto cleanup; break;
case AEAD_OPEN:
if (!lc_cipher_decrypt_init(state->cipher, &cparams))
return 0;
break;
default:
return 0;
}
store64le(&buf[0], aadlen); return 1;
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;
} }
static int static int
xchacha20_poly1305_seal(uint8_t *out, size_t *outlen, void *initparams, xchacha20_poly1305_anycrypt_init(void *arg, void *initparams, enum aead_mode m)
const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen)
{ {
struct lc_xchacha20_poly1305_params *params = initparams; struct lc_xchacha20_poly1305_params *params = initparams;
struct lc_cipher_ctx *cctx = NULL; struct chacha20_poly1305_state *state = arg;
struct lc_auth_ctx *actx = NULL;
struct lc_xchacha20_params cparams; struct lc_xchacha20_params cparams;
struct lc_poly1305_params aparams; struct lc_poly1305_params aparams;
uint8_t buf[sizeof(uint64_t) * 2]; size_t olen;
size_t i, olen;
int ret = 0; 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; *outlen = 0;
/* inlen and aadlen are capped by design; enough space of tag. */ switch (m) {
if (inlen > UINT64_MAX || aadlen > UINT64_MAX || case AEAD_SEAL:
inlen > SIZE_MAX - LC_POLY1305_TAGLEN) 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; return 0;
/* Counter 0 is used for deriving Poly1305 key. */ /* Counter 0 is used for deriving Poly1305 key. */
if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || 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) CHACHA20_CTRMAX - 1)
return 0; return 0;
if (out == NULL) { if (out == NULL || tag == NULL) {
*outlen = inlen + LC_POLY1305_TAGLEN; *outlen = inlen;
*taglen = LC_POLY1305_TAGLEN;
return 1; return 1;
} }
cctx = lc_cipher_ctx_new(lc_cipher_impl_xchacha20()); if (!chacha20_poly1305_anycrypt_init(&state, initparams, AEAD_SEAL))
if (cctx == NULL) return 0;
goto cleanup; if (!c20_xc20_poly1305_anycrypt_update(&state, out, &olen, aad, aadlen,
actx = lc_auth_ctx_new(lc_auth_impl_poly1305()); in, inlen, AEAD_SEAL))
if (actx == NULL) return 0;
goto cleanup; *outlen = olen;
if (!c20_xc20_poly1305_seal_final(&state, out + olen, &olen, tag,
for (i = 0; i < sizeof(params->key); i++) taglen))
cparams.key[i] = params->key[i]; return 0;
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;
*outlen += olen; *outlen += olen;
if (*outlen != inlen + LC_POLY1305_TAGLEN)
goto cleanup;
ret = 1;
cleanup: return 1;
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; 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 static int
chacha20_poly1305_open(uint8_t *out, size_t *outlen, void *initparams, 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 chacha20_poly1305_state state;
struct lc_cipher_ctx *cctx = NULL; size_t olen;
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;
*outlen = 0; *outlen = 0;
/* inlen includes the tag; inlen and aadlen are capped by design. */ /* inlen and aadlen are capped by design. */
if (inlen < LC_POLY1305_TAGLEN || if (inlen > UINT64_MAX || aadlen > UINT64_MAX)
inlen > UINT64_MAX || aadlen > UINT64_MAX)
return 0; return 0;
/* Counter 0 is used for deriving Poly1305 key. */ /* Counter 0 is used for deriving Poly1305 key. */
if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) ||
(inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN > (inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN >
CHACHA20_CTRMAX - 1) { CHACHA20_CTRMAX - 1)
return 0; return 0;
}
if (out == NULL) { if (out == NULL) {
*outlen = inlen - LC_POLY1305_TAGLEN; *outlen = inlen;
return 1; return 1;
} }
cctx = lc_cipher_ctx_new(lc_cipher_impl_chacha20()); if (!chacha20_poly1305_anycrypt_init(&state, initparams, AEAD_OPEN))
if (cctx == NULL) return 0;
goto cleanup; if (!c20_xc20_poly1305_anycrypt_update(&state, out, &olen, aad, aadlen,
actx = lc_auth_ctx_new(lc_auth_impl_poly1305()); in, inlen, AEAD_OPEN))
if (actx == NULL) return 0;
goto cleanup; *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++) return 1;
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;
} }
static int static int
xchacha20_poly1305_open(uint8_t *out, size_t *outlen, void *initparams, 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 chacha20_poly1305_state state;
struct lc_cipher_ctx *cctx = NULL; size_t olen;
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;
*outlen = 0; *outlen = 0;
/* inlen includes the tag; inlen and aadlen are capped by design. */ /* inlen and aadlen are capped by design. */
if (inlen < LC_POLY1305_TAGLEN || if (inlen > UINT64_MAX || aadlen > UINT64_MAX)
inlen > UINT64_MAX || aadlen > UINT64_MAX)
return 0; return 0;
/* Counter 0 is used for deriving Poly1305 key. */ /* Counter 0 is used for deriving Poly1305 key. */
if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) || if (inlen > SIZE_MAX - (LC_CHACHA20_BLOCKLEN - 1) ||
(inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN > (inlen + LC_CHACHA20_BLOCKLEN - 1) / LC_CHACHA20_BLOCKLEN >
CHACHA20_CTRMAX - 1) { CHACHA20_CTRMAX - 1)
return 0; return 0;
}
if (out == NULL) { if (out == NULL) {
*outlen = inlen - LC_POLY1305_TAGLEN; *outlen = inlen;
return 1; return 1;
} }
cctx = lc_cipher_ctx_new(lc_cipher_impl_xchacha20()); if (!xchacha20_poly1305_anycrypt_init(&state, initparams, AEAD_OPEN))
if (cctx == NULL) return 0;
goto cleanup; if (!c20_xc20_poly1305_anycrypt_update(&state, out, &olen, aad, aadlen,
actx = lc_auth_ctx_new(lc_auth_impl_poly1305()); in, inlen, AEAD_OPEN))
if (actx == NULL) return 0;
goto cleanup; *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++) return 1;
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;
} }
@ -378,9 +488,17 @@ const struct lc_aead_impl *
lc_aead_impl_chacha20_poly1305(void) lc_aead_impl_chacha20_poly1305(void)
{ {
static struct lc_aead_impl chacha20_poly1305_impl = { 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, .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, .open = &chacha20_poly1305_open,
.argsz = sizeof(struct chacha20_poly1305_state),
.blocklen = LC_CHACHA20_BLOCKLEN, .blocklen = LC_CHACHA20_BLOCKLEN,
}; };
@ -391,10 +509,18 @@ const struct lc_aead_impl *
lc_aead_impl_xchacha20_poly1305(void) lc_aead_impl_xchacha20_poly1305(void)
{ {
static struct lc_aead_impl xchacha20_poly1305_impl = { 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, .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, .open = &xchacha20_poly1305_open,
.blocklen = LC_XCHACHA20_BLOCKLEN, .argsz = sizeof(struct chacha20_poly1305_state),
.blocklen = LC_CHACHA20_BLOCKLEN,
}; };
return &xchacha20_poly1305_impl; return &xchacha20_poly1305_impl;

View File

@ -59,10 +59,21 @@
*/ */
struct lc_aead_impl { struct lc_aead_impl {
int (*seal)(uint8_t *, size_t *, void *, const uint8_t *, size_t, int (*seal_init)(void *, void *);
const uint8_t *, size_t); 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, 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 argsz;
size_t blocklen; size_t blocklen;
@ -121,6 +132,11 @@ struct lc_kdf_impl {
* a cryptographic algorithm. * a cryptographic algorithm.
*/ */
struct lc_aead_ctx {
const struct lc_aead_impl *impl;
void *arg;
};
struct lc_auth_ctx { struct lc_auth_ctx {
const struct lc_auth_impl *impl; const struct lc_auth_impl *impl;
void *arg; void *arg;
@ -140,6 +156,16 @@ struct lc_hash_ctx {
* *_state holds the internal state of the cryptographic algorithms. * *_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. */ /* Authentication. */
struct hmac_state { struct hmac_state {

View File

@ -58,6 +58,7 @@
* Structs. * Structs.
*/ */
struct lc_aead_ctx;
struct lc_aead_impl; struct lc_aead_impl;
struct lc_auth_ctx; struct lc_auth_ctx;
@ -104,13 +105,17 @@ struct lc_xchacha20_params {
/* AEAD. */ /* AEAD. */
struct lc_chacha20_poly1305_params { struct lc_chacha20_poly1305_params {
uint8_t key[LC_CHACHA20_KEYLEN]; struct lc_auth_ctx *auth;
uint8_t nonce[LC_CHACHA20_NONCELEN]; struct lc_cipher_ctx *cipher;
uint8_t key[LC_CHACHA20_KEYLEN];
uint8_t nonce[LC_CHACHA20_NONCELEN];
}; };
struct lc_xchacha20_poly1305_params { struct lc_xchacha20_poly1305_params {
uint8_t key[LC_XCHACHA20_KEYLEN]; struct lc_auth_ctx *auth;
uint8_t nonce[LC_XCHACHA20_NONCELEN]; struct lc_cipher_ctx *cipher;
uint8_t key[LC_XCHACHA20_KEYLEN];
uint8_t nonce[LC_XCHACHA20_NONCELEN];
}; };
/* KDF. */ /* KDF. */
@ -200,10 +205,26 @@ const struct lc_cipher_impl *lc_cipher_impl_xchacha20(void);
* Authenticated encryption with additional data. * 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); 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 *, 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_chacha20_poly1305(void);
const struct lc_aead_impl *lc_aead_impl_xchacha20_poly1305(void); const struct lc_aead_impl *lc_aead_impl_xchacha20_poly1305(void);