From 42dcd39528c6188a259951e28bbad309234324e4 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Wed, 6 Oct 2021 14:08:49 -0700 Subject: [PATCH] crypto: Support Chacha20-Poly1305 with a nonce size of 8 bytes. This is useful for WireGuard which uses a nonce of 8 bytes rather than the 12 bytes used for IPsec and TLS. Note that this also fixes a (should be) harmless bug in ossl(4) where the counter was incorrectly treated as a 64-bit counter instead of a 32-bit counter in terms of wrapping when using a 12 byte nonce. However, this required a single message (TLS record) longer than 64 * (2^32 - 1) bytes (about 256 GB) to trigger. Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D32122 --- share/man/man7/crypto.7 | 2 +- sys/crypto/openssl/ossl_chacha20.c | 14 ++++++----- sys/opencrypto/crypto.c | 7 +++++- sys/opencrypto/cryptosoft.c | 3 --- sys/opencrypto/xform_chacha20_poly1305.c | 31 +++++++++++++++++++----- tools/tools/crypto/cryptocheck.c | 4 +-- 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/share/man/man7/crypto.7 b/share/man/man7/crypto.7 index d75daa62adcb..c7af22af4deb 100644 --- a/share/man/man7/crypto.7 +++ b/share/man/man7/crypto.7 @@ -167,7 +167,7 @@ The following AEAD algorithms are supported: AES Galois/Counter Mode .It Dv CRYPTO_AES_CCM_16 Ta 12, 7-13 Ta 16, 24, 32 Ta 16 Ta AES Counter with CBC-MAC -.It Dv CRYPTO_CHACHA20_POLY1305 Ta 12 Ta 32 Ta 16 Ta +.It Dv CRYPTO_CHACHA20_POLY1305 Ta 12, 8 Ta 32 Ta 16 Ta ChaCha20-Poly1305 .El .Sh SEE ALSO diff --git a/sys/crypto/openssl/ossl_chacha20.c b/sys/crypto/openssl/ossl_chacha20.c index a6f56aca1f8f..aa125121e8b4 100644 --- a/sys/crypto/openssl/ossl_chacha20.c +++ b/sys/crypto/openssl/ossl_chacha20.c @@ -161,7 +161,8 @@ ossl_chacha20_poly1305_encrypt(struct cryptop *crp, for (i = 0; i < nitems(key); i++) key[i] = CHACHA_U8TOU32(cipher_key + i * 4); - crypto_read_iv(crp, counter + 1); + memset(counter, 0, sizeof(counter)); + crypto_read_iv(crp, counter + (CHACHA_CTR_SIZE - csp->csp_ivlen) / 4); for (i = 1; i < nitems(counter); i++) counter[i] = le32toh(counter[i]); @@ -223,7 +224,7 @@ ossl_chacha20_poly1305_encrypt(struct cryptop *crp, /* Truncate if the 32-bit counter would roll over. */ next_counter = counter[0] + todo / CHACHA_BLK_SIZE; - if (next_counter < counter[0]) { + if (csp->csp_ivlen == 8 && next_counter < counter[0]) { todo -= next_counter * CHACHA_BLK_SIZE; next_counter = 0; } @@ -232,7 +233,7 @@ ossl_chacha20_poly1305_encrypt(struct cryptop *crp, Poly1305_Update(&auth_ctx, out, todo); counter[0] = next_counter; - if (counter[0] == 0) + if (csp->csp_ivlen == 8 && counter[0] == 0) counter[1]++; if (out == block) { @@ -307,7 +308,8 @@ ossl_chacha20_poly1305_decrypt(struct cryptop *crp, for (i = 0; i < nitems(key); i++) key[i] = CHACHA_U8TOU32(cipher_key + i * 4); - crypto_read_iv(crp, counter + 1); + memset(counter, 0, sizeof(counter)); + crypto_read_iv(crp, counter + (CHACHA_CTR_SIZE - csp->csp_ivlen) / 4); for (i = 1; i < nitems(counter); i++) counter[i] = le32toh(counter[i]); @@ -391,7 +393,7 @@ ossl_chacha20_poly1305_decrypt(struct cryptop *crp, /* Truncate if the 32-bit counter would roll over. */ next_counter = counter[0] + todo / CHACHA_BLK_SIZE; - if (next_counter < counter[0]) { + if (csp->csp_ivlen == 8 && next_counter < counter[0]) { todo -= next_counter * CHACHA_BLK_SIZE; next_counter = 0; } @@ -399,7 +401,7 @@ ossl_chacha20_poly1305_decrypt(struct cryptop *crp, ChaCha20_ctr32(out, in, todo, key, counter); counter[0] = next_counter; - if (counter[0] == 0) + if (csp->csp_ivlen == 8 && counter[0] == 0) counter[1]++; if (out == block) { diff --git a/sys/opencrypto/crypto.c b/sys/opencrypto/crypto.c index 3a71e53129be..85c22fd51ee2 100644 --- a/sys/opencrypto/crypto.c +++ b/sys/opencrypto/crypto.c @@ -852,10 +852,15 @@ check_csp(const struct crypto_session_params *csp) return (false); break; case CRYPTO_AES_NIST_GCM_16: - case CRYPTO_CHACHA20_POLY1305: if (csp->csp_auth_mlen > 16) return (false); break; + case CRYPTO_CHACHA20_POLY1305: + if (csp->csp_ivlen != 8 && csp->csp_ivlen != 12) + return (false); + if (csp->csp_auth_mlen > POLY1305_HASH_LEN) + return (false); + break; } break; case CSP_MODE_ETA: diff --git a/sys/opencrypto/cryptosoft.c b/sys/opencrypto/cryptosoft.c index e51bade8a3f8..94ba1a5af8f5 100644 --- a/sys/opencrypto/cryptosoft.c +++ b/sys/opencrypto/cryptosoft.c @@ -1393,9 +1393,6 @@ swcr_setup_chacha20_poly1305(struct swcr_session *ses, struct swcr_auth *swa; const 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; diff --git a/sys/opencrypto/xform_chacha20_poly1305.c b/sys/opencrypto/xform_chacha20_poly1305.c index 51ae11c923e7..47d26dbd1d21 100644 --- a/sys/opencrypto/xform_chacha20_poly1305.c +++ b/sys/opencrypto/xform_chacha20_poly1305.c @@ -34,6 +34,7 @@ struct chacha20_poly1305_cipher_ctx { const void *key; uint32_t ic; + bool ietf; char nonce[CHACHA20_POLY1305_IV_LEN]; }; @@ -58,7 +59,8 @@ chacha20_poly1305_reinit(void *vctx, const uint8_t *iv, size_t ivlen) ("%s: invalid nonce length", __func__)); /* Block 0 is used for the poly1305 key. */ - memcpy(ctx->nonce, iv, sizeof(ctx->nonce)); + memcpy(ctx->nonce, iv, ivlen); + ctx->ietf = (ivlen == CHACHA20_POLY1305_IV_LEN); ctx->ic = 1; } @@ -68,8 +70,12 @@ 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); + if (ctx->ietf) + error = crypto_stream_chacha20_ietf_xor_ic(out, in, + CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key); + else + error = crypto_stream_chacha20_xor_ic(out, in, + CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key); KASSERT(error == 0, ("%s failed: %d", __func__, error)); ctx->ic++; } @@ -82,8 +88,12 @@ chacha20_poly1305_crypt_last(void *vctx, const uint8_t *in, uint8_t *out, int error; - error = crypto_stream_chacha20_ietf_xor_ic(out, in, len, ctx->nonce, - ctx->ic, ctx->key); + if (ctx->ietf) + error = crypto_stream_chacha20_ietf_xor_ic(out, in, len, + ctx->nonce, ctx->ic, ctx->key); + else + error = crypto_stream_chacha20_xor_ic(out, in, len, ctx->nonce, + ctx->ic, ctx->key); KASSERT(error == 0, ("%s failed: %d", __func__, error)); } @@ -129,7 +139,16 @@ 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); + switch (noncelen) { + case 8: + crypto_stream_chacha20(block, sizeof(block), nonce, ctx->key); + break; + case CHACHA20_POLY1305_IV_LEN: + crypto_stream_chacha20_ietf(block, sizeof(block), nonce, ctx->key); + break; + default: + __assert_unreachable(); + } crypto_onetimeauth_poly1305_init(&ctx->state, block); explicit_bzero(block, sizeof(block)); } diff --git a/tools/tools/crypto/cryptocheck.c b/tools/tools/crypto/cryptocheck.c index 63c6ce1f9d28..05b761a0c87a 100644 --- a/tools/tools/crypto/cryptocheck.c +++ b/tools/tools/crypto/cryptocheck.c @@ -126,7 +126,7 @@ * aes-ccm 128-bit AES-CCM * aes-ccm192 192-bit AES-CCM * aes-ccm256 256-bit AES-CCM - * chacha20-poly1305 Chacha20 (96 bit nonce) with Poly1305 per RFC 8439 + * chacha20-poly1305 Chacha20 with Poly1305 per RFC 8439 */ #include @@ -233,7 +233,7 @@ static const struct alg { .evp_cipher = EVP_aes_256_ccm }, { .name = "chacha20-poly1305", .cipher = CRYPTO_CHACHA20_POLY1305, .type = T_AEAD, .tag_len = POLY1305_HASH_LEN, - .iv_sizes = { CHACHA20_POLY1305_IV_LEN }, + .iv_sizes = { CHACHA20_POLY1305_IV_LEN, 8 }, .evp_cipher = EVP_chacha20_poly1305 }, };