diff --git include/internal/ktls.h include/internal/ktls.h
index 95492fd065..3c82cae26b 100644
--- include/internal/ktls.h
+++ include/internal/ktls.h
@@ -40,6 +40,11 @@
 #   define OPENSSL_KTLS_AES_GCM_128
 #   define OPENSSL_KTLS_AES_GCM_256
 #   define OPENSSL_KTLS_TLS13
+#   ifdef TLS_CHACHA20_IV_LEN
+#    ifndef OPENSSL_NO_CHACHA
+#     define OPENSSL_KTLS_CHACHA20_POLY1305
+#    endif
+#   endif
 
 typedef struct tls_enable ktls_crypto_info_t;
 
diff --git ssl/ktls.c ssl/ktls.c
index 79d980959e..e343d382cc 100644
--- ssl/ktls.c
+++ ssl/ktls.c
@@ -10,6 +10,67 @@
 #include "ssl_local.h"
 #include "internal/ktls.h"
 
+#ifndef OPENSSL_NO_KTLS_RX
+ /*
+  * Count the number of records that were not processed yet from record boundary.
+  *
+  * This function assumes that there are only fully formed records read in the
+  * record layer. If read_ahead is enabled, then this might be false and this
+  * function will fail.
+  */
+static int count_unprocessed_records(SSL *s)
+{
+    SSL3_BUFFER *rbuf = RECORD_LAYER_get_rbuf(&s->rlayer);
+    PACKET pkt, subpkt;
+    int count = 0;
+
+    if (!PACKET_buf_init(&pkt, rbuf->buf + rbuf->offset, rbuf->left))
+        return -1;
+
+    while (PACKET_remaining(&pkt) > 0) {
+        /* Skip record type and version */
+        if (!PACKET_forward(&pkt, 3))
+            return -1;
+
+        /* Read until next record */
+        if (!PACKET_get_length_prefixed_2(&pkt, &subpkt))
+            return -1;
+
+        count += 1;
+    }
+
+    return count;
+}
+
+/*
+ * The kernel cannot offload receive if a partial TLS record has been read.
+ * Check the read buffer for unprocessed records.  If the buffer contains a
+ * partial record, fail and return 0.  Otherwise, update the sequence
+ * number at *rec_seq for the count of unprocessed records and return 1.
+ */
+static int check_rx_read_ahead(SSL *s, unsigned char *rec_seq)
+{
+    int bit, count_unprocessed;
+
+    count_unprocessed = count_unprocessed_records(s);
+    if (count_unprocessed < 0)
+        return 0;
+
+    /* increment the crypto_info record sequence */
+    while (count_unprocessed) {
+        for (bit = 7; bit >= 0; bit--) { /* increment */
+            ++rec_seq[bit];
+            if (rec_seq[bit] != 0)
+                break;
+        }
+        count_unprocessed--;
+
+    }
+
+    return 1;
+}
+#endif
+
 #if defined(__FreeBSD__)
 # include "crypto/cryptodev.h"
 
@@ -37,6 +98,10 @@ int ktls_check_supported_cipher(const SSL *s, const EVP_CIPHER *c,
     case SSL_AES128GCM:
     case SSL_AES256GCM:
         return 1;
+# ifdef OPENSSL_KTLS_CHACHA20_POLY1305
+    case SSL_CHACHA20POLY1305:
+        return 1;
+# endif
     case SSL_AES128:
     case SSL_AES256:
         if (s->ext.use_etm)
@@ -55,9 +120,9 @@ int ktls_check_supported_cipher(const SSL *s, const EVP_CIPHER *c,
 }
 
 /* Function to configure kernel TLS structure */
-int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
+int ktls_configure_crypto(SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
                           void *rl_sequence, ktls_crypto_info_t *crypto_info,
-                          unsigned char **rec_seq, unsigned char *iv,
+                          int is_tx, unsigned char *iv,
                           unsigned char *key, unsigned char *mac_key,
                           size_t mac_secret_size)
 {
@@ -71,6 +136,12 @@ int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
         else
             crypto_info->iv_len = EVP_GCM_TLS_FIXED_IV_LEN;
         break;
+# ifdef OPENSSL_KTLS_CHACHA20_POLY1305
+    case SSL_CHACHA20POLY1305:
+        crypto_info->cipher_algorithm = CRYPTO_CHACHA20_POLY1305;
+        crypto_info->iv_len = EVP_CIPHER_CTX_get_iv_length(dd);
+        break;
+# endif
     case SSL_AES128:
     case SSL_AES256:
         switch (s->s3.tmp.new_cipher->algorithm_mac) {
@@ -101,11 +172,11 @@ int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
     crypto_info->tls_vminor = (s->version & 0x000000ff);
 # ifdef TCP_RXTLS_ENABLE
     memcpy(crypto_info->rec_seq, rl_sequence, sizeof(crypto_info->rec_seq));
-    if (rec_seq != NULL)
-        *rec_seq = crypto_info->rec_seq;
+    if (!is_tx && !check_rx_read_ahead(s, crypto_info->rec_seq))
+        return 0;
 # else
-    if (rec_seq != NULL)
-        *rec_seq = NULL;
+    if (!is_tx)
+        return 0;
 # endif
     return 1;
 };
@@ -154,15 +225,20 @@ int ktls_check_supported_cipher(const SSL *s, const EVP_CIPHER *c,
 }
 
 /* Function to configure kernel TLS structure */
-int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
+int ktls_configure_crypto(SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
                           void *rl_sequence, ktls_crypto_info_t *crypto_info,
-                          unsigned char **rec_seq, unsigned char *iv,
+                          int is_tx, unsigned char *iv,
                           unsigned char *key, unsigned char *mac_key,
                           size_t mac_secret_size)
 {
     unsigned char geniv[12];
     unsigned char *iiv = iv;
 
+# ifdef OPENSSL_NO_KTLS_RX
+    if (!is_tx)
+        return 0;
+# endif
+
     if (s->version == TLS1_2_VERSION &&
         EVP_CIPHER_get_mode(c) == EVP_CIPH_GCM_MODE) {
         if (!EVP_CIPHER_CTX_get_updated_iv(dd, geniv,
@@ -186,8 +262,8 @@ int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
         memcpy(crypto_info->gcm128.key, key, EVP_CIPHER_get_key_length(c));
         memcpy(crypto_info->gcm128.rec_seq, rl_sequence,
                TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
-        if (rec_seq != NULL)
-            *rec_seq = crypto_info->gcm128.rec_seq;
+        if (!is_tx && !check_rx_read_ahead(s, crypto_info->gcm128.rec_seq))
+            return 0;
         return 1;
 # endif
 # ifdef OPENSSL_KTLS_AES_GCM_256
@@ -201,8 +277,8 @@ int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
         memcpy(crypto_info->gcm256.key, key, EVP_CIPHER_get_key_length(c));
         memcpy(crypto_info->gcm256.rec_seq, rl_sequence,
                TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE);
-        if (rec_seq != NULL)
-            *rec_seq = crypto_info->gcm256.rec_seq;
+        if (!is_tx && !check_rx_read_ahead(s, crypto_info->gcm256.rec_seq))
+            return 0;
         return 1;
 # endif
 # ifdef OPENSSL_KTLS_AES_CCM_128
@@ -216,8 +292,8 @@ int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
         memcpy(crypto_info->ccm128.key, key, EVP_CIPHER_get_key_length(c));
         memcpy(crypto_info->ccm128.rec_seq, rl_sequence,
                TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE);
-        if (rec_seq != NULL)
-            *rec_seq = crypto_info->ccm128.rec_seq;
+        if (!is_tx && !check_rx_read_ahead(s, crypto_info->ccm128.rec_seq))
+            return 0;
         return 1;
 # endif
 # ifdef OPENSSL_KTLS_CHACHA20_POLY1305
@@ -231,8 +307,10 @@ int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
                EVP_CIPHER_get_key_length(c));
         memcpy(crypto_info->chacha20poly1305.rec_seq, rl_sequence,
                TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE);
-        if (rec_seq != NULL)
-            *rec_seq = crypto_info->chacha20poly1305.rec_seq;
+        if (!is_tx
+                && !check_rx_read_ahead(s,
+                                        crypto_info->chacha20poly1305.rec_seq))
+            return 0;
         return 1;
 # endif
     default:
diff --git ssl/record/ssl3_record.c ssl/record/ssl3_record.c
index d8ef018741..63caac080f 100644
--- ssl/record/ssl3_record.c
+++ ssl/record/ssl3_record.c
@@ -185,18 +185,23 @@ int ssl3_get_record(SSL *s)
     int imac_size;
     size_t num_recs = 0, max_recs, j;
     PACKET pkt, sslv2pkt;
-    int is_ktls_left;
+    int using_ktls;
     SSL_MAC_BUF *macbufs = NULL;
     int ret = -1;
 
     rr = RECORD_LAYER_get_rrec(&s->rlayer);
     rbuf = RECORD_LAYER_get_rbuf(&s->rlayer);
-    is_ktls_left = (SSL3_BUFFER_get_left(rbuf) > 0);
     max_recs = s->max_pipelines;
     if (max_recs == 0)
         max_recs = 1;
     sess = s->session;
 
+    /*
+     * KTLS reads full records. If there is any data left,
+     * then it is from before enabling ktls.
+     */
+    using_ktls = BIO_get_ktls_recv(s->rbio) && SSL3_BUFFER_get_left(rbuf) == 0;
+
     do {
         thisrr = &rr[num_recs];
 
@@ -361,7 +366,9 @@ int ssl3_get_record(SSL *s)
                     }
                 }
 
-                if (SSL_IS_TLS13(s) && s->enc_read_ctx != NULL) {
+                if (SSL_IS_TLS13(s)
+                        && s->enc_read_ctx != NULL
+                        && !using_ktls) {
                     if (thisrr->type != SSL3_RT_APPLICATION_DATA
                             && (thisrr->type != SSL3_RT_CHANGE_CIPHER_SPEC
                                 || !SSL_IS_FIRST_HANDSHAKE(s))
@@ -391,7 +398,13 @@ int ssl3_get_record(SSL *s)
         }
 
         if (SSL_IS_TLS13(s)) {
-            if (thisrr->length > SSL3_RT_MAX_TLS13_ENCRYPTED_LENGTH) {
+            size_t len = SSL3_RT_MAX_TLS13_ENCRYPTED_LENGTH;
+
+            /* KTLS strips the inner record type. */
+            if (using_ktls)
+                len = SSL3_RT_MAX_ENCRYPTED_LENGTH;
+
+            if (thisrr->length > len) {
                 SSLfatal(s, SSL_AD_RECORD_OVERFLOW,
                          SSL_R_ENCRYPTED_LENGTH_TOO_LONG);
                 return -1;
@@ -409,7 +422,7 @@ int ssl3_get_record(SSL *s)
 #endif
 
             /* KTLS may use all of the buffer */
-            if (BIO_get_ktls_recv(s->rbio) && !is_ktls_left)
+            if (using_ktls)
                 len = SSL3_BUFFER_get_left(rbuf);
 
             if (thisrr->length > len) {
@@ -518,11 +531,7 @@ int ssl3_get_record(SSL *s)
         return 1;
     }
 
-    /*
-     * KTLS reads full records. If there is any data left,
-     * then it is from before enabling ktls
-     */
-    if (BIO_get_ktls_recv(s->rbio) && !is_ktls_left)
+    if (using_ktls)
         goto skip_decryption;
 
     if (s->read_hash != NULL) {
@@ -677,21 +686,29 @@ int ssl3_get_record(SSL *s)
         if (SSL_IS_TLS13(s)
                 && s->enc_read_ctx != NULL
                 && thisrr->type != SSL3_RT_ALERT) {
-            size_t end;
+            /*
+             * The following logic are irrelevant in KTLS: the kernel provides
+             * unprotected record and thus record type represent the actual
+             * content type, and padding is already removed and thisrr->type and
+             * thisrr->length should have the correct values.
+             */
+            if (!using_ktls) {
+                size_t end;
 
-            if (thisrr->length == 0
-                    || thisrr->type != SSL3_RT_APPLICATION_DATA) {
-                SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_RECORD_TYPE);
-                goto end;
+                if (thisrr->length == 0
+                        || thisrr->type != SSL3_RT_APPLICATION_DATA) {
+                    SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_RECORD_TYPE);
+                    goto end;
+                }
+
+                /* Strip trailing padding */
+                for (end = thisrr->length - 1; end > 0 && thisrr->data[end] == 0;
+                     end--)
+                    continue;
+
+                thisrr->length = end;
+                thisrr->type = thisrr->data[end];
             }
-
-            /* Strip trailing padding */
-            for (end = thisrr->length - 1; end > 0 && thisrr->data[end] == 0;
-                 end--)
-                continue;
-
-            thisrr->length = end;
-            thisrr->type = thisrr->data[end];
             if (thisrr->type != SSL3_RT_APPLICATION_DATA
                     && thisrr->type != SSL3_RT_ALERT
                     && thisrr->type != SSL3_RT_HANDSHAKE) {
@@ -700,7 +717,7 @@ int ssl3_get_record(SSL *s)
             }
             if (s->msg_callback)
                 s->msg_callback(0, s->version, SSL3_RT_INNER_CONTENT_TYPE,
-                                &thisrr->data[end], 1, s, s->msg_callback_arg);
+                                &thisrr->type, 1, s, s->msg_callback_arg);
         }
 
         /*
@@ -723,8 +740,7 @@ int ssl3_get_record(SSL *s)
          * Therefore we have to rely on KTLS to check the plaintext length
          * limit in the kernel.
          */
-        if (thisrr->length > SSL3_RT_MAX_PLAIN_LENGTH
-                && (!BIO_get_ktls_recv(s->rbio) || is_ktls_left)) {
+        if (thisrr->length > SSL3_RT_MAX_PLAIN_LENGTH && !using_ktls) {
             SSLfatal(s, SSL_AD_RECORD_OVERFLOW, SSL_R_DATA_LENGTH_TOO_LONG);
             goto end;
         }
diff --git ssl/ssl_local.h ssl/ssl_local.h
index 5471e900b8..79ced2f468 100644
--- ssl/ssl_local.h
+++ ssl/ssl_local.h
@@ -2760,9 +2760,9 @@ __owur int ssl_log_secret(SSL *ssl, const char *label,
 /* ktls.c */
 int ktls_check_supported_cipher(const SSL *s, const EVP_CIPHER *c,
                                 const EVP_CIPHER_CTX *dd);
-int ktls_configure_crypto(const SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
+int ktls_configure_crypto(SSL *s, const EVP_CIPHER *c, EVP_CIPHER_CTX *dd,
                           void *rl_sequence, ktls_crypto_info_t *crypto_info,
-                          unsigned char **rec_seq, unsigned char *iv,
+                          int is_tx, unsigned char *iv,
                           unsigned char *key, unsigned char *mac_key,
                           size_t mac_secret_size);
 #  endif
diff --git ssl/t1_enc.c ssl/t1_enc.c
index 237a19cd93..900ba14fbd 100644
--- ssl/t1_enc.c
+++ ssl/t1_enc.c
@@ -98,42 +98,6 @@ static int tls1_generate_key_block(SSL *s, unsigned char *km, size_t num)
     return ret;
 }
 
-#ifndef OPENSSL_NO_KTLS
- /*
-  * Count the number of records that were not processed yet from record boundary.
-  *
-  * This function assumes that there are only fully formed records read in the
-  * record layer. If read_ahead is enabled, then this might be false and this
-  * function will fail.
-  */
-# ifndef OPENSSL_NO_KTLS_RX
-static int count_unprocessed_records(SSL *s)
-{
-    SSL3_BUFFER *rbuf = RECORD_LAYER_get_rbuf(&s->rlayer);
-    PACKET pkt, subpkt;
-    int count = 0;
-
-    if (!PACKET_buf_init(&pkt, rbuf->buf + rbuf->offset, rbuf->left))
-        return -1;
-
-    while (PACKET_remaining(&pkt) > 0) {
-        /* Skip record type and version */
-        if (!PACKET_forward(&pkt, 3))
-            return -1;
-
-        /* Read until next record */
-        if (!PACKET_get_length_prefixed_2(&pkt, &subpkt))
-            return -1;
-
-        count += 1;
-    }
-
-    return count;
-}
-# endif
-#endif
-
-
 int tls_provider_set_tls_params(SSL *s, EVP_CIPHER_CTX *ctx,
                                 const EVP_CIPHER *ciph,
                                 const EVP_MD *md)
@@ -201,12 +165,7 @@ int tls1_change_cipher_state(SSL *s, int which)
     int reuse_dd = 0;
 #ifndef OPENSSL_NO_KTLS
     ktls_crypto_info_t crypto_info;
-    unsigned char *rec_seq;
     void *rl_sequence;
-# ifndef OPENSSL_NO_KTLS_RX
-    int count_unprocessed;
-    int bit;
-# endif
     BIO *bio;
 #endif
 
@@ -473,30 +432,11 @@ int tls1_change_cipher_state(SSL *s, int which)
     else
         rl_sequence = RECORD_LAYER_get_read_sequence(&s->rlayer);
 
-    if (!ktls_configure_crypto(s, c, dd, rl_sequence, &crypto_info, &rec_seq,
-                               iv, key, ms, *mac_secret_size))
+    if (!ktls_configure_crypto(s, c, dd, rl_sequence, &crypto_info,
+                               which & SSL3_CC_WRITE, iv, key, ms,
+                               *mac_secret_size))
         goto skip_ktls;
 
-    if (which & SSL3_CC_READ) {
-# ifndef OPENSSL_NO_KTLS_RX
-        count_unprocessed = count_unprocessed_records(s);
-        if (count_unprocessed < 0)
-            goto skip_ktls;
-
-        /* increment the crypto_info record sequence */
-        while (count_unprocessed) {
-            for (bit = 7; bit >= 0; bit--) { /* increment */
-                ++rec_seq[bit];
-                if (rec_seq[bit] != 0)
-                    break;
-            }
-            count_unprocessed--;
-        }
-# else
-        goto skip_ktls;
-# endif
-    }
-
     /* ktls works with user provided buffers directly */
     if (BIO_set_ktls(bio, &crypto_info, which & SSL3_CC_WRITE)) {
         if (which & SSL3_CC_WRITE)
diff --git ssl/tls13_enc.c ssl/tls13_enc.c
index 12388922e3..eaab0e2a74 100644
--- ssl/tls13_enc.c
+++ ssl/tls13_enc.c
@@ -434,6 +434,7 @@ int tls13_change_cipher_state(SSL *s, int which)
     const EVP_CIPHER *cipher = NULL;
 #if !defined(OPENSSL_NO_KTLS) && defined(OPENSSL_KTLS_TLS13)
     ktls_crypto_info_t crypto_info;
+    void *rl_sequence;
     BIO *bio;
 #endif
 
@@ -688,8 +689,7 @@ int tls13_change_cipher_state(SSL *s, int which)
         s->statem.enc_write_state = ENC_WRITE_STATE_VALID;
 #ifndef OPENSSL_NO_KTLS
 # if defined(OPENSSL_KTLS_TLS13)
-    if (!(which & SSL3_CC_WRITE)
-            || !(which & SSL3_CC_APPLICATION)
+    if (!(which & SSL3_CC_APPLICATION)
             || (s->options & SSL_OP_ENABLE_KTLS) == 0)
         goto skip_ktls;
 
@@ -705,7 +705,10 @@ int tls13_change_cipher_state(SSL *s, int which)
     if (!ktls_check_supported_cipher(s, cipher, ciph_ctx))
         goto skip_ktls;
 
-    bio = s->wbio;
+    if (which & SSL3_CC_WRITE)
+        bio = s->wbio;
+    else
+        bio = s->rbio;
 
     if (!ossl_assert(bio != NULL)) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
@@ -713,18 +716,26 @@ int tls13_change_cipher_state(SSL *s, int which)
     }
 
     /* All future data will get encrypted by ktls. Flush the BIO or skip ktls */
-    if (BIO_flush(bio) <= 0)
-        goto skip_ktls;
+    if (which & SSL3_CC_WRITE) {
+        if (BIO_flush(bio) <= 0)
+            goto skip_ktls;
+    }
 
     /* configure kernel crypto structure */
-    if (!ktls_configure_crypto(s, cipher, ciph_ctx,
-                               RECORD_LAYER_get_write_sequence(&s->rlayer),
-                               &crypto_info, NULL, iv, key, NULL, 0))
+    if (which & SSL3_CC_WRITE)
+        rl_sequence = RECORD_LAYER_get_write_sequence(&s->rlayer);
+    else
+        rl_sequence = RECORD_LAYER_get_read_sequence(&s->rlayer);
+
+    if (!ktls_configure_crypto(s, cipher, ciph_ctx, rl_sequence, &crypto_info,
+                               which & SSL3_CC_WRITE, iv, key, NULL, 0))
         goto skip_ktls;
 
     /* ktls works with user provided buffers directly */
-    if (BIO_set_ktls(bio, &crypto_info, which & SSL3_CC_WRITE))
-        ssl3_release_write_buffer(s);
+    if (BIO_set_ktls(bio, &crypto_info, which & SSL3_CC_WRITE)) {
+        if (which & SSL3_CC_WRITE)
+            ssl3_release_write_buffer(s);
+    }
 skip_ktls:
 # endif
 #endif
diff --git test/sslapitest.c test/sslapitest.c
index 2911d6e94b..faf2eec2bc 100644
--- test/sslapitest.c
+++ test/sslapitest.c
@@ -1243,7 +1243,7 @@ static int execute_test_ktls(int cis_ktls, int sis_ktls,
 #if defined(OPENSSL_NO_KTLS_RX)
     rx_supported = 0;
 #else
-    rx_supported = (tls_version != TLS1_3_VERSION);
+    rx_supported = 1;
 #endif
     if (!cis_ktls || !rx_supported) {
         if (!TEST_false(BIO_get_ktls_recv(clientssl->rbio)))
