Implement XChaCha20 and XChaCha20-Poly1305

This commit is contained in:
Lucas Gabriel Vuotto 2024-06-07 02:18:50 +00:00
parent 06f835e27c
commit 0605d10bb3
9 changed files with 278 additions and 3 deletions

4
README
View File

@ -33,13 +33,13 @@ Ciphers
-------
- [x] ChaCha20
- [ ] XChaCha20
- [x] XChaCha20
AEAD
----
- [x] ChaCha20-Poly1305
- [ ] XChaCha20-Poly1305
- [x] XChaCha20-Poly1305
ECC
---

View File

@ -180,14 +180,174 @@ chacha20_poly1305_open(const uint8_t *key, size_t keylen, const uint8_t *iv,
return 1;
}
static int
xchacha20_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 (!xchacha20_common_init(&cctx, key, keylen, iv, ivlen) ||
!chacha20_common_update(&cctx, poly1305_key, &olen, poly1305_key,
LC_POLY1305_KEYLEN))
return 0;
if (!chacha20_common_final(&cctx, poly1305_key + olen, &olen))
return 0;
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 (!xchacha20_common_init_from(&cctx, key, keylen, iv, ivlen, 1))
return 0;
if (!chacha20_common_update(&cctx, out, &olen, in, inlen))
return 0;
*outlen = olen;
if (!chacha20_common_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 int
xchacha20_poly1305_open(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)
{
const uint8_t *tagp;
struct chacha20_ctx cctx;
struct poly1305_ctx pctx;
uint8_t poly1305_key[LC_POLY1305_KEYLEN];
uint8_t tag[LC_POLY1305_TAGLEN];
uint8_t buf[sizeof(uint64_t) * 2];
size_t i, olen, ctlen;
if (inlen < LC_POLY1305_TAGLEN ||
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;
ctlen = inlen - LC_POLY1305_TAGLEN;
tagp = in + ctlen;
for (i = 0; i < LC_POLY1305_KEYLEN; i++)
poly1305_key[i] = 0;
if (!xchacha20_common_init(&cctx, key, keylen, iv, ivlen) ||
!chacha20_common_update(&cctx, poly1305_key, &olen, poly1305_key,
LC_POLY1305_KEYLEN))
return 0;
if (!chacha20_common_final(&cctx, poly1305_key + olen, &olen))
return 0;
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 (!poly1305_update(&pctx, in, ctlen))
return 0;
if (ctlen % 16 != 0)
if (!poly1305_update(&pctx, zeropad, 16 - (ctlen % 16)))
return 0;
store64le(&buf[0], aadlen);
store64le(&buf[sizeof(uint64_t)], ctlen);
if (!poly1305_update(&pctx, buf, sizeof(buf)) ||
!poly1305_final(&pctx, tag, &olen))
return 0;
if (!lc_ct_cmp(tag, tagp, LC_POLY1305_TAGLEN))
return 0;
lc_scrub(buf, sizeof(buf));
lc_scrub(poly1305_key, sizeof(poly1305_key));
if (!xchacha20_common_init_from(&cctx, key, keylen, iv, ivlen, 1))
return 0;
if (!chacha20_common_update(&cctx, out, &olen, in, ctlen))
return 0;
*outlen = olen;
if (!chacha20_common_final(&cctx, out + olen, &olen))
return 0;
*outlen += olen;
return 1;
}
static struct lc_aead_impl chacha20_poly1305_impl = {
.seal = &chacha20_poly1305_seal,
.open = &chacha20_poly1305_open,
};
static struct lc_aead_impl xchacha20_poly1305_impl = {
.seal = &xchacha20_poly1305_seal,
.open = &xchacha20_poly1305_open,
};
const struct lc_aead_impl *
lc_aead_impl_chacha20_poly1305(void)
{
return &chacha20_poly1305_impl;
}
const struct lc_aead_impl *
lc_aead_impl_xchacha20_poly1305(void)
{
return &xchacha20_poly1305_impl;
}

View File

@ -54,6 +54,49 @@ chacha20_common_init(void *arg, const uint8_t *key, size_t keylen,
return chacha20_common_init_from(arg, key, keylen, iv, ivlen, 0);
}
int
xchacha20_common_init_from(void *arg, const uint8_t *key, size_t keylen,
const uint8_t *iv, size_t ivlen, uint64_t counter)
{
struct chacha20_ctx *ctx = arg;
size_t i;
if (keylen != LC_XCHACHA20_KEYLEN || ivlen != LC_XCHACHA20_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]);
for (i = 0; i < CHACHA20_NONCE_WORDS; i++)
ctx->n[i] = load32le(&iv[i * 4]);
ctx->mlen = 0;
hchacha20_block(ctx);
ctx->k[0] = ctx->s[0];
ctx->k[1] = ctx->s[1];
ctx->k[2] = ctx->s[2];
ctx->k[3] = ctx->s[3];
ctx->k[4] = ctx->s[12];
ctx->k[5] = ctx->s[13];
ctx->k[6] = ctx->s[14];
ctx->k[7] = ctx->s[15];
ctx->n[0] = counter;
ctx->n[1] = counter >> 32;
ctx->n[2] = load32le(&iv[16]);
ctx->n[3] = load32le(&iv[20]);
return 1;
}
int
xchacha20_common_init(void *arg, const uint8_t *key, size_t keylen,
const uint8_t *iv, size_t ivlen)
{
return xchacha20_common_init_from(arg, key, keylen, iv, ivlen, 0);
}
int
chacha20_common_update(void *arg, uint8_t *out, size_t *outlen,
const uint8_t *in, size_t inlen)
@ -201,8 +244,29 @@ static struct lc_cipher_impl chacha20_impl = {
.ctx_free = NULL,
};
static struct lc_cipher_impl xchacha20_impl = {
.encrypt_init = &xchacha20_common_init,
.encrypt_update = &chacha20_common_update,
.encrypt_final = &chacha20_common_final,
.encrypt = &chacha20_common,
.decrypt_init = &xchacha20_common_init,
.decrypt_update = &chacha20_common_update,
.decrypt_final = &chacha20_common_final,
.decrypt = &chacha20_common,
.ctx_new = &chacha20_ctx_new,
.ctx_free = NULL,
};
const struct lc_cipher_impl *
lc_cipher_impl_chacha20(void)
{
return &chacha20_impl;
}
const struct lc_cipher_impl *
lc_cipher_impl_xchacha20(void)
{
return &xchacha20_impl;
}

View File

@ -22,6 +22,10 @@ int chacha20_common_init_from(void *, const uint8_t *, size_t,
const uint8_t *, size_t, uint32_t);
int chacha20_common_init(void *, const uint8_t *, size_t, const uint8_t *,
size_t);
int xchacha20_common_init_from(void *, const uint8_t *, size_t,
const uint8_t *, size_t, uint64_t);
int xchacha20_common_init(void *, const uint8_t *, size_t, const uint8_t *,
size_t);
int chacha20_common_update(void *, uint8_t *, size_t *, const uint8_t *,
size_t);
int chacha20_common_final(void *, uint8_t *, size_t *);

View File

@ -83,3 +83,42 @@ chacha20_block(struct chacha20_ctx *ctx)
for (i = 0; i < CHACHA20_CHUNK_WORDS; i++)
ctx->s[i] += x[i];
}
void
hchacha20_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->n[0];
x[13] = ctx->n[1];
x[14] = ctx->n[2];
x[15] = ctx->n[3];
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];
}

View File

@ -38,3 +38,4 @@ struct chacha20_ctx {
void chacha20_block(struct chacha20_ctx *);
void hchacha20_block(struct chacha20_ctx *);

View File

@ -34,6 +34,8 @@
/* Ciphers. */
#define LC_CHACHA20_KEYLEN 32
#define LC_CHACHA20_IVLEN 12
#define LC_XCHACHA20_KEYLEN 32
#define LC_XCHACHA20_IVLEN 24
/*
@ -115,6 +117,7 @@ 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);
const struct lc_cipher_impl *lc_cipher_impl_xchacha20(void);
/*
@ -132,6 +135,7 @@ int lc_aead_open(const struct lc_aead_impl *, const uint8_t *, size_t,
const uint8_t *, size_t);
const struct lc_aead_impl *lc_aead_impl_chacha20_poly1305(void);
const struct lc_aead_impl *lc_aead_impl_xchacha20_poly1305(void);
/*

View File

@ -22,7 +22,9 @@ tests-aead:
.for p in ${AEAD}
perl ${.CURDIR}/aead.pl ${TESTOPTS} -x ./${p} \
${WYCHEPROOF_DIR}/testvectors/chacha20_poly1305_test.json \
${WYCHEPROOF_DIR}/testvectors_v1/chacha20_poly1305_test.json
${WYCHEPROOF_DIR}/testvectors_v1/chacha20_poly1305_test.json \
${WYCHEPROOF_DIR}/testvectors/xchacha20_poly1305_test.json \
${WYCHEPROOF_DIR}/testvectors_v1/xchacha20_poly1305_test.json
.endfor
tests-mac:

View File

@ -79,6 +79,7 @@ kw2impl(const char *s)
{
static const struct kwimpl tbl[] = {
{ "CHACHA20-POLY1305", &lc_aead_impl_chacha20_poly1305 },
{ "XCHACHA20-POLY1305", &lc_aead_impl_xchacha20_poly1305 },
};
struct kwimpl *match;