Implement XChaCha20 and XChaCha20-Poly1305
This commit is contained in:
parent
06f835e27c
commit
0605d10bb3
4
README
4
README
@ -33,13 +33,13 @@ Ciphers
|
||||
-------
|
||||
|
||||
- [x] ChaCha20
|
||||
- [ ] XChaCha20
|
||||
- [x] XChaCha20
|
||||
|
||||
AEAD
|
||||
----
|
||||
|
||||
- [x] ChaCha20-Poly1305
|
||||
- [ ] XChaCha20-Poly1305
|
||||
- [x] XChaCha20-Poly1305
|
||||
|
||||
ECC
|
||||
---
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -38,3 +38,4 @@ struct chacha20_ctx {
|
||||
|
||||
|
||||
void chacha20_block(struct chacha20_ctx *);
|
||||
void hchacha20_block(struct chacha20_ctx *);
|
||||
|
@ -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);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user