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
This commit is contained in:
John Baldwin 2022-11-15 12:02:03 -08:00
parent b97ee269ea
commit 9a673b7158
2 changed files with 134 additions and 5 deletions

View File

@ -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();

View File

@ -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;