crypto: Add support for the XChaCha20-Poly1305 AEAD cipher.

This cipher is a wrapper around the ChaCha20-Poly1305 AEAD cipher
which accepts a larger nonce.  Part of the nonce is used along with
the key as an input to HChaCha20 to generate a derived key used for
ChaCha20-Poly1305.

This cipher is used by WireGuard.

Reviewed by:	markj
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D33523
This commit is contained in:
John Baldwin 2022-01-11 14:16:05 -08:00
parent 7df4c50643
commit 8f35841f1f
6 changed files with 86 additions and 2 deletions

View File

@ -31,7 +31,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd October 6, 2021 .Dd January 11, 2022
.Dt CRYPTO 7 .Dt CRYPTO 7
.Os .Os
.Sh NAME .Sh NAME
@ -170,6 +170,9 @@ AES Counter with CBC-MAC
.It Dv CRYPTO_CHACHA20_POLY1305 Ta 12, 8 Ta 32 Ta 16 Ta .It Dv CRYPTO_CHACHA20_POLY1305 Ta 12, 8 Ta 32 Ta 16 Ta
ChaCha20-Poly1305 ChaCha20-Poly1305
.El .El
.It Dv CRYPTO_XCHACHA20_POLY1305 Ta 24 Ta 32 Ta 16 Ta
XChaCha20-Poly1305
.El
.Sh SEE ALSO .Sh SEE ALSO
.Xr crypto 4 , .Xr crypto 4 ,
.Xr crypto 9 .Xr crypto 9

View File

@ -579,6 +579,8 @@ crypto_cipher(const struct crypto_session_params *csp)
return (&enc_xform_ccm); return (&enc_xform_ccm);
case CRYPTO_CHACHA20_POLY1305: case CRYPTO_CHACHA20_POLY1305:
return (&enc_xform_chacha20_poly1305); return (&enc_xform_chacha20_poly1305);
case CRYPTO_XCHACHA20_POLY1305:
return (&enc_xform_xchacha20_poly1305);
default: default:
return (NULL); return (NULL);
} }
@ -671,6 +673,7 @@ static enum alg_type {
[CRYPTO_AES_CCM_CBC_MAC] = ALG_KEYED_DIGEST, [CRYPTO_AES_CCM_CBC_MAC] = ALG_KEYED_DIGEST,
[CRYPTO_AES_CCM_16] = ALG_AEAD, [CRYPTO_AES_CCM_16] = ALG_AEAD,
[CRYPTO_CHACHA20_POLY1305] = ALG_AEAD, [CRYPTO_CHACHA20_POLY1305] = ALG_AEAD,
[CRYPTO_XCHACHA20_POLY1305] = ALG_AEAD,
}; };
static enum alg_type static enum alg_type
@ -865,6 +868,12 @@ check_csp(const struct crypto_session_params *csp)
if (csp->csp_auth_mlen > POLY1305_HASH_LEN) if (csp->csp_auth_mlen > POLY1305_HASH_LEN)
return (false); return (false);
break; break;
case CRYPTO_XCHACHA20_POLY1305:
if (csp->csp_ivlen != XCHACHA20_POLY1305_IV_LEN)
return (false);
if (csp->csp_auth_mlen > POLY1305_HASH_LEN)
return (false);
break;
} }
break; break;
case CSP_MODE_ETA: case CSP_MODE_ETA:

View File

@ -129,6 +129,7 @@
#define AES_XTS_IV_LEN 8 #define AES_XTS_IV_LEN 8
#define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ #define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */
#define CHACHA20_POLY1305_IV_LEN 12 #define CHACHA20_POLY1305_IV_LEN 12
#define XCHACHA20_POLY1305_IV_LEN 24
/* Min and Max Encryption Key Sizes */ /* Min and Max Encryption Key Sizes */
#define NULL_MIN_KEY 0 #define NULL_MIN_KEY 0
@ -142,6 +143,7 @@
#define CAMELLIA_MIN_KEY 16 #define CAMELLIA_MIN_KEY 16
#define CAMELLIA_MAX_KEY 32 #define CAMELLIA_MAX_KEY 32
#define CHACHA20_POLY1305_KEY 32 #define CHACHA20_POLY1305_KEY 32
#define XCHACHA20_POLY1305_KEY 32
/* Maximum hash algorithm result length */ /* Maximum hash algorithm result length */
#define AALG_MAX_RESULT_LEN 64 /* Keep this updated */ #define AALG_MAX_RESULT_LEN 64 /* Keep this updated */
@ -191,7 +193,8 @@
#define CRYPTO_AES_CCM_CBC_MAC 39 /* auth side */ #define CRYPTO_AES_CCM_CBC_MAC 39 /* auth side */
#define CRYPTO_AES_CCM_16 40 /* cipher side */ #define CRYPTO_AES_CCM_16 40 /* cipher side */
#define CRYPTO_CHACHA20_POLY1305 41 /* combined AEAD cipher per RFC 8439 */ #define CRYPTO_CHACHA20_POLY1305 41 /* combined AEAD cipher per RFC 8439 */
#define CRYPTO_ALGORITHM_MAX 41 /* Keep updated - see below */ #define CRYPTO_XCHACHA20_POLY1305 42
#define CRYPTO_ALGORITHM_MAX 42 /* Keep updated - see below */
#define CRYPTO_ALGO_VALID(x) ((x) >= CRYPTO_ALGORITHM_MIN && \ #define CRYPTO_ALGO_VALID(x) ((x) >= CRYPTO_ALGORITHM_MIN && \
(x) <= CRYPTO_ALGORITHM_MAX) (x) <= CRYPTO_ALGORITHM_MAX)

View File

@ -1330,6 +1330,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16: case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305: case CRYPTO_CHACHA20_POLY1305:
case CRYPTO_XCHACHA20_POLY1305:
return (EINVAL); return (EINVAL);
default: default:
if (!swcr_cipher_supported(csp)) if (!swcr_cipher_supported(csp))
@ -1355,6 +1356,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
} }
break; break;
case CRYPTO_CHACHA20_POLY1305: case CRYPTO_CHACHA20_POLY1305:
case CRYPTO_XCHACHA20_POLY1305:
break; break;
default: default:
return (EINVAL); return (EINVAL);
@ -1366,6 +1368,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16: case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305: case CRYPTO_CHACHA20_POLY1305:
case CRYPTO_XCHACHA20_POLY1305:
return (EINVAL); return (EINVAL);
} }
switch (csp->csp_auth_alg) { switch (csp->csp_auth_alg) {
@ -1422,6 +1425,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16: case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305: case CRYPTO_CHACHA20_POLY1305:
case CRYPTO_XCHACHA20_POLY1305:
panic("bad cipher algo"); panic("bad cipher algo");
#endif #endif
default: default:
@ -1446,6 +1450,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
ses->swcr_process = swcr_ccm; ses->swcr_process = swcr_ccm;
break; break;
case CRYPTO_CHACHA20_POLY1305: case CRYPTO_CHACHA20_POLY1305:
case CRYPTO_XCHACHA20_POLY1305:
error = swcr_setup_aead(ses, csp); error = swcr_setup_aead(ses, csp);
if (error == 0) if (error == 0)
ses->swcr_process = swcr_chacha20_poly1305; ses->swcr_process = swcr_chacha20_poly1305;
@ -1462,6 +1467,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16: case CRYPTO_AES_CCM_16:
case CRYPTO_CHACHA20_POLY1305: case CRYPTO_CHACHA20_POLY1305:
case CRYPTO_XCHACHA20_POLY1305:
panic("bad eta cipher algo"); panic("bad eta cipher algo");
} }
switch (csp->csp_auth_alg) { switch (csp->csp_auth_alg) {

View File

@ -28,6 +28,7 @@
#include <opencrypto/xform_auth.h> #include <opencrypto/xform_auth.h>
#include <opencrypto/xform_enc.h> #include <opencrypto/xform_enc.h>
#include <sodium/crypto_core_hchacha20.h>
#include <sodium/crypto_onetimeauth_poly1305.h> #include <sodium/crypto_onetimeauth_poly1305.h>
#include <sodium/crypto_stream_chacha20.h> #include <sodium/crypto_stream_chacha20.h>
@ -39,6 +40,12 @@ struct chacha20_poly1305_ctx {
char nonce[CHACHA20_POLY1305_IV_LEN]; char nonce[CHACHA20_POLY1305_IV_LEN];
}; };
struct xchacha20_poly1305_ctx {
struct chacha20_poly1305_ctx base_ctx; /* must be first */
const void *key;
char derived_key[CHACHA20_POLY1305_KEY];
};
static int static int
chacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len) chacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
{ {
@ -144,3 +151,58 @@ const struct enc_xform enc_xform_chacha20_poly1305 = {
.update = chacha20_poly1305_update, .update = chacha20_poly1305_update,
.final = chacha20_poly1305_final, .final = chacha20_poly1305_final,
}; };
static int
xchacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
{
struct xchacha20_poly1305_ctx *ctx = vctx;
if (len != XCHACHA20_POLY1305_KEY)
return (EINVAL);
ctx->key = key;
ctx->base_ctx.key = ctx->derived_key;
return (0);
}
static void
xchacha20_poly1305_reinit(void *vctx, const uint8_t *iv, size_t ivlen)
{
struct xchacha20_poly1305_ctx *ctx = vctx;
char nonce[CHACHA20_POLY1305_IV_LEN];
KASSERT(ivlen == XCHACHA20_POLY1305_IV_LEN,
("%s: invalid nonce length", __func__));
/*
* Use HChaCha20 to derive the internal key used for
* ChaCha20-Poly1305.
*/
crypto_core_hchacha20(ctx->derived_key, iv, ctx->key, NULL);
memset(nonce, 0, 4);
memcpy(nonce + 4, iv + crypto_core_hchacha20_INPUTBYTES,
sizeof(nonce) - 4);
chacha20_poly1305_reinit(&ctx->base_ctx, nonce, sizeof(nonce));
explicit_bzero(nonce, sizeof(nonce));
}
const struct enc_xform enc_xform_xchacha20_poly1305 = {
.type = CRYPTO_XCHACHA20_POLY1305,
.name = "XChaCha20-Poly1305",
.ctxsize = sizeof(struct xchacha20_poly1305_ctx),
.blocksize = 1,
.native_blocksize = CHACHA20_NATIVE_BLOCK_LEN,
.ivsize = XCHACHA20_POLY1305_IV_LEN,
.minkey = XCHACHA20_POLY1305_KEY,
.maxkey = XCHACHA20_POLY1305_KEY,
.macsize = POLY1305_HASH_LEN,
.encrypt = chacha20_poly1305_crypt,
.decrypt = chacha20_poly1305_crypt,
.setkey = xchacha20_poly1305_setkey,
.reinit = xchacha20_poly1305_reinit,
.encrypt_last = chacha20_poly1305_crypt_last,
.decrypt_last = chacha20_poly1305_crypt_last,
.update = chacha20_poly1305_update,
.final = chacha20_poly1305_final,
};

View File

@ -89,6 +89,7 @@ extern const struct enc_xform enc_xform_aes_xts;
extern const struct enc_xform enc_xform_camellia; extern const struct enc_xform enc_xform_camellia;
extern const struct enc_xform enc_xform_chacha20; extern const struct enc_xform enc_xform_chacha20;
extern const struct enc_xform enc_xform_chacha20_poly1305; extern const struct enc_xform enc_xform_chacha20_poly1305;
extern const struct enc_xform enc_xform_xchacha20_poly1305;
extern const struct enc_xform enc_xform_ccm; extern const struct enc_xform enc_xform_ccm;
struct aes_icm_ctx { struct aes_icm_ctx {