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:
@@ -149,7 +149,7 @@ SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, enable, CTLFLAG_RWTUN,
|
|||||||
static bool ktls_cbc_enable = true;
|
static bool ktls_cbc_enable = true;
|
||||||
SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, cbc_enable, CTLFLAG_RWTUN,
|
SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, cbc_enable, CTLFLAG_RWTUN,
|
||||||
&ktls_cbc_enable, 1,
|
&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;
|
static bool ktls_sw_buffer_cache = true;
|
||||||
SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, sw_buffer_cache, CTLFLAG_RDTUN,
|
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_ccc -= tls_len;
|
||||||
sb->sb_tlsdcc = 0;
|
sb->sb_tlsdcc = 0;
|
||||||
|
|
||||||
|
if (error != EMSGSIZE)
|
||||||
|
error = EBADMSG;
|
||||||
CURVNET_SET(so->so_vnet);
|
CURVNET_SET(so->so_vnet);
|
||||||
so->so_error = EBADMSG;
|
so->so_error = error;
|
||||||
sorwakeup_locked(so);
|
sorwakeup_locked(so);
|
||||||
CURVNET_RESTORE();
|
CURVNET_RESTORE();
|
||||||
|
|
||||||
|
|||||||
+130
-3
@@ -101,6 +101,11 @@ SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls10_cbc_encrypts,
|
|||||||
CTLFLAG_RD, &ocf_tls10_cbc_encrypts,
|
CTLFLAG_RD, &ocf_tls10_cbc_encrypts,
|
||||||
"Total number of OCF TLS 1.0 CBC encryption operations");
|
"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);
|
static COUNTER_U64_DEFINE_EARLY(ocf_tls11_cbc_encrypts);
|
||||||
SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls11_cbc_encrypts,
|
SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls11_cbc_encrypts,
|
||||||
CTLFLAG_RD, &ocf_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);
|
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 = {
|
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
|
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)
|
tls->params.tls_vminor > TLS_MINOR_VER_TWO)
|
||||||
return (EPROTONOSUPPORT);
|
return (EPROTONOSUPPORT);
|
||||||
|
|
||||||
/* AES-CBC is not supported for receive. */
|
/* AES-CBC is not supported for receive for TLS 1.0. */
|
||||||
if (direction == KTLS_RX)
|
if (direction == KTLS_RX &&
|
||||||
|
tls->params.tls_vminor == TLS_MINOR_VER_ZERO)
|
||||||
return (EPROTONOSUPPORT);
|
return (EPROTONOSUPPORT);
|
||||||
|
|
||||||
csp.csp_flags |= CSP_F_SEPARATE_OUTPUT;
|
csp.csp_flags |= CSP_F_SEPARATE_OUTPUT;
|
||||||
|
|||||||
Reference in New Issue
Block a user