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:
parent
fc8fc743d8
commit
dd2e1352b6
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user