Add an implementation of CHACHA20_POLY1305 to cryptosoft.

This uses the chacha20 IETF and poly1305 implementations from
libsodium.  A seperate auth_hash is created for the auth side whose
Setkey method derives the poly1305 key from the AEAD key and nonce as
described in RFC 8439.

Sponsored by:	Netflix
Differential Revision:	https://reviews.freebsd.org/D27837
This commit is contained in:
John Baldwin 2021-02-18 09:22:18 -08:00
parent fc8fc743d8
commit dd2e1352b6
5 changed files with 335 additions and 5 deletions

View File

@ -4925,7 +4925,8 @@ opencrypto/gfmult.c optional crypto | ipsec | ipsec_support
opencrypto/rmd160.c optional crypto | ipsec | ipsec_support
opencrypto/xform.c optional crypto | ipsec | ipsec_support
opencrypto/xform_cbc_mac.c optional crypto
opencrypto/xform_chacha20_poly1305.c optional crypto
opencrypto/xform_chacha20_poly1305.c optional crypto \
compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium"
opencrypto/xform_poly1305.c optional crypto \
compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium"
contrib/libsodium/src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \

View File

@ -15,6 +15,8 @@ LIBSODIUM=${SRCTOP}/sys/contrib/libsodium/src/libsodium
.PATH: ${SRCTOP}/sys/contrib/libb2
.PATH: ${LIBSODIUM}/crypto_onetimeauth/poly1305
.PATH: ${LIBSODIUM}/crypto_onetimeauth/poly1305/donna
.PATH: ${LIBSODIUM}/crypto_stream/chacha20
.PATH: ${LIBSODIUM}/crypto_stream/chacha20/ref
.PATH: ${LIBSODIUM}/crypto_verify/sodium
.PATH: ${SRCTOP}/sys/crypto/libsodium
@ -55,8 +57,14 @@ SRCS += chacha-sw.c
LIBSODIUM_INC=${LIBSODIUM}/include
LIBSODIUM_COMPAT=${SRCTOP}/sys/crypto/libsodium
SRCS += xform_chacha20_poly1305.c
CFLAGS.xform_chacha20_poly1305.c+= -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
SRCS += xform_poly1305.c
CFLAGS.xform_poly1305.c += -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
SRCS += stream_chacha20.c
CFLAGS.stream_chacha20.c += -I${LIBSODIUM_INC}/sodium -I${LIBSODIUM_COMPAT}
SRCS += chacha20_ref.c
CFLAGS.chacha20_ref.c += -I${LIBSODIUM_INC}/sodium -I${LIBSODIUM_COMPAT}
SRCS += onetimeauth_poly1305.c
CFLAGS.onetimeauth_poly1305.c += -I${LIBSODIUM_INC}/sodium -I${LIBSODIUM_COMPAT}
SRCS += poly1305_donna.c
@ -67,7 +75,6 @@ SRCS += randombytes.c
CFLAGS.randombytes.c += -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
SRCS += utils.c
CFLAGS.utils.c += -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
SRCS += xform_chacha20_poly1305.c
SRCS += opt_param.h cryptodev_if.h bus_if.h device_if.h
SRCS += opt_compat.h

View File

@ -867,6 +867,168 @@ swcr_ccm(struct swcr_session *ses, struct cryptop *crp)
return (error);
}
static int
swcr_chacha20_poly1305(struct swcr_session *ses, struct cryptop *crp)
{
const struct crypto_session_params *csp;
uint64_t blkbuf[howmany(CHACHA20_NATIVE_BLOCK_LEN, sizeof(uint64_t))];
u_char *blk = (u_char *)blkbuf;
u_char tag[POLY1305_HASH_LEN];
struct crypto_buffer_cursor cc_in, cc_out;
const u_char *inblk;
u_char *outblk;
uint64_t *blkp;
union authctx ctx;
struct swcr_auth *swa;
struct swcr_encdec *swe;
struct auth_hash *axf;
struct enc_xform *exf;
int blksz, error, r, resid;
swa = &ses->swcr_auth;
axf = swa->sw_axf;
swe = &ses->swcr_encdec;
exf = swe->sw_exf;
blksz = exf->native_blocksize;
KASSERT(blksz <= sizeof(blkbuf), ("%s: blocksize mismatch", __func__));
if ((crp->crp_flags & CRYPTO_F_IV_SEPARATE) == 0)
return (EINVAL);
csp = crypto_get_params(crp->crp_session);
/* Generate Poly1305 key. */
if (crp->crp_cipher_key != NULL)
axf->Setkey(&ctx, crp->crp_cipher_key, csp->csp_cipher_klen);
else
axf->Setkey(&ctx, csp->csp_cipher_key, csp->csp_cipher_klen);
axf->Reinit(&ctx, crp->crp_iv, csp->csp_ivlen);
/* Supply MAC with AAD */
if (crp->crp_aad != NULL)
axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length);
else
crypto_apply(crp, crp->crp_aad_start,
crp->crp_aad_length, axf->Update, &ctx);
if (crp->crp_aad_length % 16 != 0) {
/* padding1 */
memset(blk, 0, 16);
axf->Update(&ctx, blk, 16 - crp->crp_aad_length % 16);
}
if (crp->crp_cipher_key != NULL)
exf->setkey(swe->sw_kschedule, crp->crp_cipher_key,
csp->csp_cipher_klen);
exf->reinit(swe->sw_kschedule, crp->crp_iv);
/* Do encryption with MAC */
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_payload_start);
if (CRYPTO_HAS_OUTPUT_BUFFER(crp)) {
crypto_cursor_init(&cc_out, &crp->crp_obuf);
crypto_cursor_advance(&cc_out, crp->crp_payload_output_start);
} else
cc_out = cc_in;
for (resid = crp->crp_payload_length; resid >= blksz; resid -= blksz) {
if (crypto_cursor_seglen(&cc_in) < blksz) {
crypto_cursor_copydata(&cc_in, blksz, blk);
inblk = blk;
} else {
inblk = crypto_cursor_segbase(&cc_in);
crypto_cursor_advance(&cc_in, blksz);
}
if (CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
if (crypto_cursor_seglen(&cc_out) < blksz)
outblk = blk;
else
outblk = crypto_cursor_segbase(&cc_out);
exf->encrypt(swe->sw_kschedule, inblk, outblk);
axf->Update(&ctx, outblk, blksz);
if (outblk == blk)
crypto_cursor_copyback(&cc_out, blksz, blk);
else
crypto_cursor_advance(&cc_out, blksz);
} else {
axf->Update(&ctx, inblk, blksz);
}
}
if (resid > 0) {
crypto_cursor_copydata(&cc_in, resid, blk);
if (CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
exf->encrypt_last(swe->sw_kschedule, blk, blk, resid);
crypto_cursor_copyback(&cc_out, resid, blk);
}
axf->Update(&ctx, blk, resid);
if (resid % 16 != 0) {
/* padding2 */
memset(blk, 0, 16);
axf->Update(&ctx, blk, 16 - resid % 16);
}
}
/* lengths */
blkp = (uint64_t *)blk;
blkp[0] = htole64(crp->crp_aad_length);
blkp[1] = htole64(crp->crp_payload_length);
axf->Update(&ctx, blk, sizeof(uint64_t) * 2);
/* Finalize MAC */
axf->Final(tag, &ctx);
/* Validate tag */
error = 0;
if (!CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
u_char tag2[POLY1305_HASH_LEN];
crypto_copydata(crp, crp->crp_digest_start, swa->sw_mlen, tag2);
r = timingsafe_bcmp(tag, tag2, swa->sw_mlen);
explicit_bzero(tag2, sizeof(tag2));
if (r != 0) {
error = EBADMSG;
goto out;
}
/* tag matches, decrypt data */
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_payload_start);
for (resid = crp->crp_payload_length; resid > blksz;
resid -= blksz) {
if (crypto_cursor_seglen(&cc_in) < blksz) {
crypto_cursor_copydata(&cc_in, blksz, blk);
inblk = blk;
} else {
inblk = crypto_cursor_segbase(&cc_in);
crypto_cursor_advance(&cc_in, blksz);
}
if (crypto_cursor_seglen(&cc_out) < blksz)
outblk = blk;
else
outblk = crypto_cursor_segbase(&cc_out);
exf->decrypt(swe->sw_kschedule, inblk, outblk);
if (outblk == blk)
crypto_cursor_copyback(&cc_out, blksz, blk);
else
crypto_cursor_advance(&cc_out, blksz);
}
if (resid > 0) {
crypto_cursor_copydata(&cc_in, resid, blk);
exf->decrypt_last(swe->sw_kschedule, blk, blk, resid);
crypto_cursor_copyback(&cc_out, resid, blk);
}
} else {
/* Inject the authentication data */
crypto_copyback(crp, crp->crp_digest_start, swa->sw_mlen, tag);
}
out:
explicit_bzero(blkbuf, sizeof(blkbuf));
explicit_bzero(tag, sizeof(tag));
explicit_bzero(&ctx, sizeof(ctx));
return (error);
}
/*
* Apply a cipher and a digest to perform EtA.
*/
@ -1171,6 +1333,33 @@ swcr_setup_ccm(struct swcr_session *ses,
return (swcr_setup_cipher(ses, csp));
}
static int
swcr_setup_chacha20_poly1305(struct swcr_session *ses,
const struct crypto_session_params *csp)
{
struct swcr_auth *swa;
struct auth_hash *axf;
if (csp->csp_ivlen != CHACHA20_POLY1305_IV_LEN)
return (EINVAL);
/* First, setup the auth side. */
swa = &ses->swcr_auth;
axf = &auth_hash_chacha20_poly1305;
swa->sw_axf = axf;
if (csp->csp_auth_mlen < 0 || csp->csp_auth_mlen > axf->hashsize)
return (EINVAL);
if (csp->csp_auth_mlen == 0)
swa->sw_mlen = axf->hashsize;
else
swa->sw_mlen = csp->csp_auth_mlen;
/* The auth state is regenerated for each nonce. */
/* Second, setup the cipher side. */
return (swcr_setup_cipher(ses, csp));
}
static bool
swcr_auth_supported(const struct crypto_session_params *csp)
{
@ -1258,6 +1447,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305:
return (EINVAL);
default:
if (!swcr_cipher_supported(csp))
@ -1273,6 +1463,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305:
break;
default:
return (EINVAL);
@ -1283,6 +1474,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305:
return (EINVAL);
}
switch (csp->csp_auth_alg) {
@ -1343,6 +1535,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
#ifdef INVARIANTS
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305:
panic("bad cipher algo");
#endif
default:
@ -1366,6 +1559,11 @@ swcr_newsession(device_t dev, crypto_session_t cses,
if (error == 0)
ses->swcr_process = swcr_ccm;
break;
case CRYPTO_CHACHA20_POLY1305:
error = swcr_setup_chacha20_poly1305(ses, csp);
if (error == 0)
ses->swcr_process = swcr_chacha20_poly1305;
break;
#ifdef INVARIANTS
default:
panic("bad aead algo");
@ -1377,6 +1575,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305:
panic("bad eta cipher algo");
}
switch (csp->csp_auth_alg) {

View File

@ -84,6 +84,7 @@ extern struct auth_hash auth_hash_poly1305;
extern struct auth_hash auth_hash_ccm_cbc_mac_128;
extern struct auth_hash auth_hash_ccm_cbc_mac_192;
extern struct auth_hash auth_hash_ccm_cbc_mac_256;
extern struct auth_hash auth_hash_chacha20_poly1305;
union authctx {
SHA1_CTX sha1ctx;

View File

@ -25,17 +25,139 @@
* SUCH DAMAGE.
*/
#include <crypto/chacha20/chacha.h>
#include <opencrypto/xform_auth.h>
#include <opencrypto/xform_enc.h>
#include <sodium/crypto_onetimeauth_poly1305.h>
#include <sodium/crypto_stream_chacha20.h>
struct chacha20_poly1305_cipher_ctx {
const void *key;
uint32_t ic;
char nonce[CHACHA20_POLY1305_IV_LEN];
};
static int
chacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
{
struct chacha20_poly1305_cipher_ctx *ctx = vctx;
if (len != CHACHA20_POLY1305_KEY)
return (EINVAL);
ctx->key = key;
return (0);
}
static void
chacha20_poly1305_reinit(void *vctx, const uint8_t *iv)
{
struct chacha20_poly1305_cipher_ctx *ctx = vctx;
/* Block 0 is used for the poly1305 key. */
memcpy(ctx->nonce, iv, sizeof(ctx->nonce));
ctx->ic = 1;
}
static void
chacha20_poly1305_crypt(void *vctx, const uint8_t *in, uint8_t *out)
{
struct chacha20_poly1305_cipher_ctx *ctx = vctx;
int error;
error = crypto_stream_chacha20_ietf_xor_ic(out, in,
CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key);
KASSERT(error == 0, ("%s failed: %d", __func__, error));
ctx->ic++;
}
static void
chacha20_poly1305_crypt_last(void *vctx, const uint8_t *in, uint8_t *out,
size_t len)
{
struct chacha20_poly1305_cipher_ctx *ctx = vctx;
int error;
error = crypto_stream_chacha20_ietf_xor_ic(out, in, len, ctx->nonce,
ctx->ic, ctx->key);
KASSERT(error == 0, ("%s failed: %d", __func__, error));
}
struct enc_xform enc_xform_chacha20_poly1305 = {
.type = CRYPTO_CHACHA20_POLY1305,
.name = "ChaCha20-Poly1305",
.ctxsize = sizeof(struct chacha_ctx),
.ctxsize = sizeof(struct chacha20_poly1305_cipher_ctx),
.blocksize = 1,
.native_blocksize = CHACHA_BLOCKLEN,
.native_blocksize = CHACHA20_NATIVE_BLOCK_LEN,
.ivsize = CHACHA20_POLY1305_IV_LEN,
.minkey = CHACHA20_POLY1305_KEY,
.maxkey = CHACHA20_POLY1305_KEY,
.encrypt = chacha20_poly1305_crypt,
.decrypt = chacha20_poly1305_crypt,
.setkey = chacha20_poly1305_setkey,
.reinit = chacha20_poly1305_reinit,
.encrypt_last = chacha20_poly1305_crypt_last,
.decrypt_last = chacha20_poly1305_crypt_last,
};
struct chacha20_poly1305_auth_ctx {
struct crypto_onetimeauth_poly1305_state state;
const void *key;
};
CTASSERT(sizeof(union authctx) >= sizeof(struct chacha20_poly1305_auth_ctx));
static void
chacha20_poly1305_Init(void *vctx)
{
}
static void
chacha20_poly1305_Setkey(void *vctx, const uint8_t *key, u_int klen)
{
struct chacha20_poly1305_auth_ctx *ctx = vctx;
ctx->key = key;
}
static void
chacha20_poly1305_Reinit(void *vctx, const uint8_t *nonce, u_int noncelen)
{
struct chacha20_poly1305_auth_ctx *ctx = vctx;
char block[CHACHA20_NATIVE_BLOCK_LEN];
crypto_stream_chacha20_ietf(block, sizeof(block), nonce, ctx->key);
crypto_onetimeauth_poly1305_init(&ctx->state, block);
explicit_bzero(block, sizeof(block));
}
static int
chacha20_poly1305_Update(void *vctx, const void *data, u_int len)
{
struct chacha20_poly1305_auth_ctx *ctx = vctx;
crypto_onetimeauth_poly1305_update(&ctx->state, data, len);
return (0);
}
static void
chacha20_poly1305_Final(uint8_t *digest, void *vctx)
{
struct chacha20_poly1305_auth_ctx *ctx = vctx;
crypto_onetimeauth_poly1305_final(&ctx->state, digest);
}
struct auth_hash auth_hash_chacha20_poly1305 = {
.type = CRYPTO_POLY1305,
.name = "ChaCha20-Poly1305",
.keysize = POLY1305_KEY_LEN,
.hashsize = POLY1305_HASH_LEN,
.ctxsize = sizeof(struct chacha20_poly1305_auth_ctx),
.blocksize = crypto_onetimeauth_poly1305_BYTES,
.Init = chacha20_poly1305_Init,
.Setkey = chacha20_poly1305_Setkey,
.Reinit = chacha20_poly1305_Reinit,
.Update = chacha20_poly1305_Update,
.Final = chacha20_poly1305_Final,
};