git: 9a673b715897 - main - ktls: Add software support for AES-CBC decryption for TLS 1.1+.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 15 Nov 2022 20:04:17 UTC
The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=9a673b7158973d86558a5d381e4784a561576b98 commit 9a673b7158973d86558a5d381e4784a561576b98 Author: John Baldwin <jhb@FreeBSD.org> AuthorDate: 2022-11-15 20:02:03 +0000 Commit: John Baldwin <jhb@FreeBSD.org> CommitDate: 2022-11-15 20:02:03 +0000 ktls: Add software support for AES-CBC decryption for TLS 1.1+. This is mainly intended to provide a fallback for TOE TLS which may need to use software decryption for an initial record at the start of a connection. Reviewed by: markj Sponsored by: Chelsio Communications Differential Revision: https://reviews.freebsd.org/D37370 --- sys/kern/uipc_ktls.c | 6 ++- sys/opencrypto/ktls_ocf.c | 133 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 134 insertions(+), 5 deletions(-) diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c index 3df1f2843c32..ae9201976988 100644 --- a/sys/kern/uipc_ktls.c +++ b/sys/kern/uipc_ktls.c @@ -149,7 +149,7 @@ SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, enable, CTLFLAG_RWTUN, static bool ktls_cbc_enable = true; SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, cbc_enable, CTLFLAG_RWTUN, &ktls_cbc_enable, 1, - "Enable Support of AES-CBC crypto for kernel TLS"); + "Enable support of AES-CBC crypto for kernel TLS"); static bool ktls_sw_buffer_cache = true; SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, sw_buffer_cache, CTLFLAG_RDTUN, @@ -2444,8 +2444,10 @@ ktls_decrypt(struct socket *so) sb->sb_ccc -= tls_len; sb->sb_tlsdcc = 0; + if (error != EMSGSIZE) + error = EBADMSG; CURVNET_SET(so->so_vnet); - so->so_error = EBADMSG; + so->so_error = error; sorwakeup_locked(so); CURVNET_RESTORE(); diff --git a/sys/opencrypto/ktls_ocf.c b/sys/opencrypto/ktls_ocf.c index 6347ca459646..938133fa451a 100644 --- a/sys/opencrypto/ktls_ocf.c +++ b/sys/opencrypto/ktls_ocf.c @@ -101,6 +101,11 @@ SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls10_cbc_encrypts, CTLFLAG_RD, &ocf_tls10_cbc_encrypts, "Total number of OCF TLS 1.0 CBC encryption operations"); +static COUNTER_U64_DEFINE_EARLY(ocf_tls11_cbc_decrypts); +SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls11_cbc_decrypts, + CTLFLAG_RD, &ocf_tls11_cbc_decrypts, + "Total number of OCF TLS 1.1/1.2 CBC decryption operations"); + static COUNTER_U64_DEFINE_EARLY(ocf_tls11_cbc_encrypts); SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls11_cbc_encrypts, CTLFLAG_RD, &ocf_tls11_cbc_encrypts, @@ -416,8 +421,129 @@ ktls_ocf_tls_cbc_encrypt(struct ktls_ocf_encrypt_state *state, return (error); } +static int +check_padding(void *arg, void *data, u_int len) +{ + uint8_t pad = *(uint8_t *)arg; + const char *cp = data; + + while (len > 0) { + if (*cp != pad) + return (EBADMSG); + cp++; + len--; + } + return (0); +} + +static int +ktls_ocf_tls_cbc_decrypt(struct ktls_session *tls, + const struct tls_record_layer *hdr, struct mbuf *m, uint64_t seqno, + int *trailer_len) +{ + struct tls_mac_data ad; + struct cryptop crp; + struct uio uio; + struct ktls_ocf_session *os; + struct iovec *iov; + struct mbuf *n; + u_int iovcnt; + int i, error, skip; + uint16_t tls_len, tls_comp_len; + uint8_t pad; + + os = tls->ocf_session; + + /* + * Ensure record is a multiple of the cipher block size and + * contains at least an explicit IV, MAC, and at least one + * padding byte. + */ + tls_len = ntohs(hdr->tls_length); + if (tls_len % AES_BLOCK_LEN != 0 || + tls_len < AES_BLOCK_LEN + roundup2(os->mac_len + 1, AES_BLOCK_LEN)) + return (EMSGSIZE); + + /* First, decrypt the record. */ + crypto_initreq(&crp, os->sid); + crp.crp_iv_start = sizeof(*hdr); + crp.crp_payload_start = tls->params.tls_hlen; + crp.crp_payload_length = tls_len - AES_BLOCK_LEN; + crypto_use_mbuf(&crp, m); + crp.crp_op = CRYPTO_OP_DECRYPT; + crp.crp_flags = CRYPTO_F_CBIMM; + + counter_u64_add(ocf_tls11_cbc_decrypts, 1); + + error = ktls_ocf_dispatch(os, &crp); + crypto_destroyreq(&crp); + if (error) + return (error); + + /* Verify the padding. */ + m_copydata(m, sizeof(*hdr) + tls_len - 1, 1, &pad); + *trailer_len = os->mac_len + pad + 1; + if (AES_BLOCK_LEN + *trailer_len > tls_len) + return (EBADMSG); + error = m_apply(m, sizeof(*hdr) + tls_len - (pad + 1), pad + 1, + check_padding, &pad); + if (error) + return (error); + + /* Verify the MAC. */ + tls_comp_len = tls_len - (AES_BLOCK_LEN + *trailer_len); + memset(&uio, 0, sizeof(uio)); + + /* + * Allocate and populate the iov. Have to skip over the TLS + * header in 'm' as it is not part of the MAC input. + */ + iovcnt = 1; + for (n = m; n != NULL; n = n->m_next) + iovcnt++; + iov = malloc(iovcnt * sizeof(*iov), M_KTLS_OCF, M_WAITOK); + iov[0].iov_base = &ad; + iov[0].iov_len = sizeof(ad); + skip = sizeof(*hdr) + AES_BLOCK_LEN; + for (i = 1, n = m; n != NULL; i++, n = n->m_next) { + if (n->m_len < skip) { + skip -= n->m_len; + continue; + } + iov[i].iov_base = mtod(n, char *) + skip; + iov[i].iov_len = n->m_len - skip; + skip = 0; + } + uio.uio_iov = iov; + uio.uio_iovcnt = i; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_td = curthread; + uio.uio_resid = sizeof(ad) + tls_len - AES_BLOCK_LEN; + + /* Initialize the AAD. */ + ad.seq = htobe64(seqno); + ad.type = hdr->tls_type; + ad.tls_vmajor = hdr->tls_vmajor; + ad.tls_vminor = hdr->tls_vminor; + ad.tls_length = htons(tls_comp_len); + + crypto_initreq(&crp, os->mac_sid); + crp.crp_payload_start = 0; + crp.crp_payload_length = sizeof(ad) + tls_comp_len; + crp.crp_digest_start = crp.crp_payload_length; + crp.crp_op = CRYPTO_OP_VERIFY_DIGEST; + crp.crp_flags = CRYPTO_F_CBIMM; + crypto_use_uio(&crp, &uio); + error = ktls_ocf_dispatch(os, &crp); + + crypto_destroyreq(&crp); + free(iov, M_KTLS_OCF); + return (error); +} + static const struct ktls_ocf_sw ktls_ocf_tls_cbc_sw = { - .encrypt = ktls_ocf_tls_cbc_encrypt + .encrypt = ktls_ocf_tls_cbc_encrypt, + .decrypt = ktls_ocf_tls_cbc_decrypt }; static int @@ -912,8 +1038,9 @@ ktls_ocf_try(struct socket *so, struct ktls_session *tls, int direction) tls->params.tls_vminor > TLS_MINOR_VER_TWO) return (EPROTONOSUPPORT); - /* AES-CBC is not supported for receive. */ - if (direction == KTLS_RX) + /* AES-CBC is not supported for receive for TLS 1.0. */ + if (direction == KTLS_RX && + tls->params.tls_vminor == TLS_MINOR_VER_ZERO) return (EPROTONOSUPPORT); csp.csp_flags |= CSP_F_SEPARATE_OUTPUT;