From 0f60abb7c63d9bc2330a6ba2b13e5a78c10d1215 Mon Sep 17 00:00:00 2001 From: jsing Date: Sat, 20 Aug 2022 19:22:28 +0000 Subject: [PATCH] Provide EVP_chacha20_poly1305() EVP_chacha20_poly1305() is an EVP_CIPHER implementation of the ChaCha20-Poly1305 AEAD. This is potentially used to provide encryption for the QUIC transport layer. Where possible, this should be avoided in favour of the significantly saner EVP_AEAD interface. ok tb@ --- lib/libcrypto/evp/e_chacha20poly1305.c | 257 ++++++++++++++++++++++++- lib/libcrypto/evp/evp.h | 10 +- 2 files changed, 260 insertions(+), 7 deletions(-) diff --git a/lib/libcrypto/evp/e_chacha20poly1305.c b/lib/libcrypto/evp/e_chacha20poly1305.c index 3b29364586d..129adb9ef9b 100644 --- a/lib/libcrypto/evp/e_chacha20poly1305.c +++ b/lib/libcrypto/evp/e_chacha20poly1305.c @@ -1,6 +1,7 @@ -/* $OpenBSD: e_chacha20poly1305.c,v 1.22 2022/08/20 18:51:09 jsing Exp $ */ +/* $OpenBSD: e_chacha20poly1305.c,v 1.23 2022/08/20 19:22:28 jsing Exp $ */ /* + * Copyright (c) 2022 Joel Sing * Copyright (c) 2015 Reyk Floter * Copyright (c) 2014, Google Inc. * @@ -29,6 +30,7 @@ #include #include +#include "bytestring.h" #include "evp_locl.h" #define POLY1305_TAG_LEN 16 @@ -99,14 +101,11 @@ poly1305_update_with_length(poly1305_state *poly1305, } static void -poly1305_update_with_pad16(poly1305_state *poly1305, - const unsigned char *data, size_t data_len) +poly1305_pad16(poly1305_state *poly1305, size_t data_len) { static const unsigned char zero_pad16[16]; size_t pad_len; - CRYPTO_poly1305_update(poly1305, data, data_len); - /* pad16() is defined in RFC 7539 2.8.1. */ if ((pad_len = data_len % 16) == 0) return; @@ -114,6 +113,14 @@ poly1305_update_with_pad16(poly1305_state *poly1305, CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len); } +static void +poly1305_update_with_pad16(poly1305_state *poly1305, + const unsigned char *data, size_t data_len) +{ + CRYPTO_poly1305_update(poly1305, data, data_len); + poly1305_pad16(poly1305, data_len); +} + static int aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, unsigned char *out, size_t *out_len, size_t max_out_len, const unsigned char *nonce, @@ -360,4 +367,244 @@ EVP_aead_xchacha20_poly1305() return &aead_xchacha20_poly1305; } +struct chacha20_poly1305_ctx { + ChaCha_ctx chacha; + poly1305_state poly1305; + + unsigned char key[32]; + unsigned char nonce[CHACHA20_NONCE_LEN]; + size_t nonce_len; + unsigned char tag[POLY1305_TAG_LEN]; + size_t tag_len; + + size_t ad_len; + size_t in_len; + + int in_ad; + int started; +}; + +static int +chacha20_poly1305_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, + const unsigned char *iv, int encrypt) +{ + struct chacha20_poly1305_ctx *cpx = ctx->cipher_data; + uint8_t *data; + CBB cbb; + int ret = 0; + + memset(&cbb, 0, sizeof(cbb)); + + if (key == NULL && iv == NULL) + goto done; + + cpx->started = 0; + + if (key != NULL) + memcpy(cpx->key, key, sizeof(cpx->key)); + + if (iv != NULL) { + /* + * Left zero pad if configured nonce length is less than ChaCha + * nonce length. + */ + if (!CBB_init_fixed(&cbb, cpx->nonce, sizeof(cpx->nonce))) + goto err; + if (!CBB_add_space(&cbb, &data, sizeof(cpx->nonce) - cpx->nonce_len)) + goto err; + if (!CBB_add_bytes(&cbb, iv, cpx->nonce_len)) + goto err; + if (!CBB_finish(&cbb, NULL, NULL)) + goto err; + } + + done: + ret = 1; + + err: + CBB_cleanup(&cbb); + + return ret; +} + +static int +chacha20_poly1305_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + struct chacha20_poly1305_ctx *cpx = ctx->cipher_data; + + /* + * Since we're making AEAD work within the constraints of EVP_CIPHER... + * If in is non-NULL then this is an update, while if in is NULL then + * this is a final. If in is non-NULL but out is NULL, then the input + * being provided is associated data. Plus we have to handle encryption + * (sealing) and decryption (opening) in the same function. + */ + + if (!cpx->started) { + unsigned char poly1305_key[32]; + const unsigned char *iv; + uint64_t ctr; + + ctr = (uint64_t)((uint32_t)(cpx->nonce[0]) | + (uint32_t)(cpx->nonce[1]) << 8 | + (uint32_t)(cpx->nonce[2]) << 16 | + (uint32_t)(cpx->nonce[3]) << 24) << 32; + iv = cpx->nonce + CHACHA20_CONSTANT_LEN; + + ChaCha_set_key(&cpx->chacha, cpx->key, 8 * sizeof(cpx->key)); + ChaCha_set_iv(&cpx->chacha, iv, NULL); + + /* See chacha.c for details re handling of counter. */ + cpx->chacha.input[12] = (uint32_t)ctr; + cpx->chacha.input[13] = (uint32_t)(ctr >> 32); + + memset(poly1305_key, 0, sizeof(poly1305_key)); + ChaCha(&cpx->chacha, poly1305_key, poly1305_key, + sizeof(poly1305_key)); + CRYPTO_poly1305_init(&cpx->poly1305, poly1305_key); + + /* Mark remaining key block as used. */ + cpx->chacha.unused = 0; + + cpx->ad_len = 0; + cpx->in_len = 0; + cpx->in_ad = 0; + + cpx->started = 1; + } + + if (len > SIZE_MAX - cpx->in_len) { + EVPerror(EVP_R_TOO_LARGE); + return 0; + } + + /* Disallow authenticated data after plaintext/ciphertext. */ + if (cpx->in_len > 0 && in != NULL && out == NULL) + return -1; + + if (cpx->in_ad && (in == NULL || out != NULL)) { + poly1305_pad16(&cpx->poly1305, cpx->ad_len); + cpx->in_ad = 0; + } + + /* Update with AD or plaintext/ciphertext. */ + if (in != NULL) { + if (out == NULL) { + cpx->ad_len += len; + cpx->in_ad = 1; + } else { + ChaCha(&cpx->chacha, out, in, len); + cpx->in_len += len; + } + if (ctx->encrypt && out != NULL) + CRYPTO_poly1305_update(&cpx->poly1305, out, len); + else + CRYPTO_poly1305_update(&cpx->poly1305, in, len); + + return len; + } + + /* Final. */ + poly1305_pad16(&cpx->poly1305, cpx->in_len); + poly1305_update_with_length(&cpx->poly1305, NULL, cpx->ad_len); + poly1305_update_with_length(&cpx->poly1305, NULL, cpx->in_len); + + if (ctx->encrypt) { + CRYPTO_poly1305_finish(&cpx->poly1305, cpx->tag); + cpx->tag_len = sizeof(cpx->tag); + } else { + unsigned char tag[POLY1305_TAG_LEN]; + + /* Ensure that a tag has been provided. */ + if (cpx->tag_len <= 0) + return -1; + + CRYPTO_poly1305_finish(&cpx->poly1305, tag); + if (timingsafe_memcmp(tag, cpx->tag, cpx->tag_len) != 0) + return -1; + } + + cpx->started = 0; + + return len; +} + +static int +chacha20_poly1305_cleanup(EVP_CIPHER_CTX *ctx) +{ + struct chacha20_poly1305_ctx *cpx = ctx->cipher_data; + + explicit_bzero(cpx, sizeof(*cpx)); + + return 0; +} + +static int +chacha20_poly1305_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) +{ + struct chacha20_poly1305_ctx *cpx = ctx->cipher_data; + + switch (type) { + case EVP_CTRL_INIT: + memset(cpx, 0, sizeof(*cpx)); + cpx->nonce_len = sizeof(cpx->nonce); + return 1; + + case EVP_CTRL_AEAD_SET_IVLEN: + if (arg <= 0 || arg > sizeof(cpx->nonce)) + return 0; + cpx->nonce_len = arg; + return 1; + + case EVP_CTRL_AEAD_SET_TAG: + if (ctx->encrypt) + return 0; + if (arg <= 0 || arg > sizeof(cpx->tag)) + return 0; + if (ptr != NULL) { + memcpy(cpx->tag, ptr, arg); + cpx->tag_len = arg; + } + return 1; + + case EVP_CTRL_AEAD_GET_TAG: + if (!ctx->encrypt) + return 0; + if (arg <= 0 || arg > cpx->tag_len) + return 0; + memcpy(ptr, cpx->tag, arg); + return 1; + + case EVP_CTRL_AEAD_SET_IV_FIXED: + if (arg != sizeof(cpx->nonce)) + return 0; + memcpy(cpx->nonce, ptr, arg); + return 1; + } + + return 0; +} + +static const EVP_CIPHER cipher_chacha20_poly1305 = { + .nid = NID_chacha20_poly1305, + .block_size = 1, + .key_len = 32, + .iv_len = 12, + .flags = EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT | + EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_AEAD_CIPHER | + EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_FLAG_DEFAULT_ASN1, + .init = chacha20_poly1305_init, + .do_cipher = chacha20_poly1305_cipher, + .cleanup = chacha20_poly1305_cleanup, + .ctx_size = sizeof(struct chacha20_poly1305_ctx), + .ctrl = chacha20_poly1305_ctrl, +}; + +const EVP_CIPHER * +EVP_chacha20_poly1305(void) +{ + return &cipher_chacha20_poly1305; +} + #endif /* !OPENSSL_NO_CHACHA && !OPENSSL_NO_POLY1305 */ diff --git a/lib/libcrypto/evp/evp.h b/lib/libcrypto/evp/evp.h index f016b6377c9..0b7fc08fc8f 100644 --- a/lib/libcrypto/evp/evp.h +++ b/lib/libcrypto/evp/evp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: evp.h,v 1.103 2022/07/12 14:42:49 kn Exp $ */ +/* $OpenBSD: evp.h,v 1.104 2022/08/20 19:22:28 jsing Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -251,10 +251,11 @@ extern "C" { #define EVP_CTRL_AEAD_SET_IVLEN 0x9 #define EVP_CTRL_AEAD_GET_TAG 0x10 #define EVP_CTRL_AEAD_SET_TAG 0x11 +#define EVP_CTRL_AEAD_SET_IV_FIXED 0x12 #define EVP_CTRL_GCM_SET_IVLEN EVP_CTRL_AEAD_SET_IVLEN #define EVP_CTRL_GCM_GET_TAG EVP_CTRL_AEAD_GET_TAG #define EVP_CTRL_GCM_SET_TAG EVP_CTRL_AEAD_SET_TAG -#define EVP_CTRL_GCM_SET_IV_FIXED 0x12 +#define EVP_CTRL_GCM_SET_IV_FIXED EVP_CTRL_AEAD_SET_IV_FIXED #define EVP_CTRL_GCM_IV_GEN 0x13 #define EVP_CTRL_CCM_SET_IVLEN EVP_CTRL_AEAD_SET_IVLEN #define EVP_CTRL_CCM_GET_TAG EVP_CTRL_AEAD_GET_TAG @@ -680,6 +681,11 @@ const EVP_CIPHER *EVP_aes_256_ccm(void); const EVP_CIPHER *EVP_aes_256_gcm(void); const EVP_CIPHER *EVP_aes_256_wrap(void); const EVP_CIPHER *EVP_aes_256_xts(void); +#if defined(LIBRESSL_NEXT_API) || defined(LIBRESSL_INTERNAL) +#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) +const EVP_CIPHER *EVP_chacha20_poly1305(void); +#endif +#endif #if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA1) const EVP_CIPHER *EVP_aes_128_cbc_hmac_sha1(void); const EVP_CIPHER *EVP_aes_256_cbc_hmac_sha1(void); -- 2.20.1