Ensure various SSL options are passed down to the record layer
[openssl.git] / ssl / record / methods / tlsrecord.c
index 691dd12ca57d0c38c10dd30c62f7f1a76cc952f9..d12b38ce3947a16a672fd45a265238c167b24501 100644 (file)
@@ -101,6 +101,14 @@ struct ossl_record_layer_st
     /* uncompress */
     COMP_CTX *expand;
 
+    /* Only used by SSLv3 */
+    unsigned char mac_secret[EVP_MAX_MD_SIZE];
+
+    /* TLSv1.3 static IV */
+    unsigned char iv[EVP_MAX_IV_LENGTH];
+
+    size_t taglen;
+
     /* Function pointers for version specific functions */
     /* Function pointers for version specific functions */
     struct record_functions_st *funcs;
@@ -166,23 +174,6 @@ static int tls_provider_set_tls_parameters(OSSL_RECORD_LAYER *rl,
     return 1;
 }
 
-static int tls_fail_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
-                                     unsigned char *key, size_t keylen,
-                                     unsigned char *iv, size_t ivlen,
-                                     unsigned char *mackey, size_t mackeylen,
-                                     const EVP_CIPHER *ciph,
-                                     size_t taglen,
-                                     /* TODO(RECLAYER): This probably should not be an int */
-                                     int mactype,
-                                     const EVP_MD *md,
-                                     const SSL_COMP *comp,
-                                     /* TODO(RECLAYER): Remove me */
-                                     SSL_CONNECTION *s)
-{
-    RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-    return 0;
-}
-
 static int tls_any_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
                                     unsigned char *key, size_t keylen,
                                     unsigned char *iv, size_t ivlen,
@@ -206,6 +197,69 @@ static int tls_any_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
     return 1;
 }
 
+/* TODO(RECLAYER): Handle OPENSSL_NO_COMP */
+static int ssl3_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
+                                 unsigned char *key, size_t keylen,
+                                 unsigned char *iv, size_t ivlen,
+                                 unsigned char *mackey, size_t mackeylen,
+                                 const EVP_CIPHER *ciph,
+                                 size_t taglen,
+                                 /* TODO(RECLAYER): This probably should not be an int */
+                                 int mactype,
+                                 const EVP_MD *md,
+                                 const SSL_COMP *comp,
+                                 /* TODO(RECLAYER): Remove me */
+                                 SSL_CONNECTION *s)
+{
+    EVP_CIPHER_CTX *ciph_ctx;
+
+    if (md == NULL) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    if ((rl->enc_read_ctx = EVP_CIPHER_CTX_new()) == NULL) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ciph_ctx = rl->enc_read_ctx;
+
+    rl->read_hash = EVP_MD_CTX_new();
+    if (rl->read_hash == NULL) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+#ifndef OPENSSL_NO_COMP
+    if (comp != NULL) {
+        rl->expand = COMP_CTX_new(comp->method);
+        if (rl->expand == NULL) {
+            RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR,
+                        SSL_R_COMPRESSION_LIBRARY_ERROR);
+            return 0;
+        }
+    }
+#endif
+
+    if (!EVP_DecryptInit_ex(ciph_ctx, ciph, NULL, key, iv)) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    if (EVP_CIPHER_get0_provider(ciph) != NULL
+            && !tls_provider_set_tls_parameters(rl, ciph_ctx, ciph, md, s)) {
+        /* RLAYERfatal already called */
+        return 0;
+    }
+
+    if (mackeylen > sizeof(rl->mac_secret)) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    memcpy(rl->mac_secret, mackey, mackeylen);
+
+    return 1;
+}
+
 /* TODO(RECLAYER): Handle OPENSSL_NO_COMP */
 static int tls1_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
                                  unsigned char *key, size_t keylen,
@@ -337,7 +391,52 @@ static int tls1_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
     }
     if (EVP_CIPHER_get0_provider(ciph) != NULL
             && !tls_provider_set_tls_parameters(rl, ciph_ctx, ciph, md, s)) {
-        /* SSLfatal already called */
+        /* RLAYERfatal already called */
+        return 0;
+    }
+
+    return 1;
+}
+
+static int tls13_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
+                                  unsigned char *key, size_t keylen,
+                                  unsigned char *iv, size_t ivlen,
+                                  unsigned char *mackey, size_t mackeylen,
+                                  const EVP_CIPHER *ciph,
+                                  size_t taglen,
+                                  /* TODO(RECLAYER): This probably should not be an int */
+                                  int mactype,
+                                  const EVP_MD *md,
+                                  const SSL_COMP *comp,
+                                  /* TODO(RECLAYER): Remove me */
+                                  SSL_CONNECTION *s)
+{
+    EVP_CIPHER_CTX *ciph_ctx;
+    int mode;
+
+    if (ivlen > sizeof(rl->iv)) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    memcpy(rl->iv, iv, ivlen);
+
+    ciph_ctx = rl->enc_read_ctx = EVP_CIPHER_CTX_new();
+    if (ciph_ctx == NULL) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    RECORD_LAYER_reset_read_sequence(&s->rlayer);
+    rl->taglen = taglen;
+
+    mode = EVP_CIPHER_get_mode(ciph);
+
+    if (EVP_DecryptInit_ex(ciph_ctx, ciph, NULL, NULL, NULL) <= 0
+        || !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_IVLEN, ivlen, NULL)
+        || (mode == EVP_CIPH_CCM_MODE
+            && !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_TAG,  taglen, NULL))
+        || EVP_DecryptInit_ex(ciph_ctx, NULL, NULL, key, NULL) <= 0) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
         return 0;
     }
 
@@ -358,6 +457,125 @@ static int tls_any_cipher(OSSL_RECORD_LAYER *rl, SSL3_RECORD *recs, size_t n_rec
     return 1;
 }
 
+/*-
+ * ssl3_enc encrypts/decrypts |n_recs| records in |inrecs|. Calls SSLfatal on
+ * internal error, but not otherwise. It is the responsibility of the caller to
+ * report a bad_record_mac
+ *
+ * Returns:
+ *    0: if the record is publicly invalid, or an internal error
+ *    1: Success or Mac-then-encrypt decryption failed (MAC will be randomised)
+ */
+static int ssl3_cipher(OSSL_RECORD_LAYER *rl, SSL3_RECORD *inrecs, size_t n_recs,
+                       int sending, SSL_MAC_BUF *mac, size_t macsize,
+                       /* TODO(RECLAYER): Remove me */ SSL_CONNECTION *s)
+{
+    SSL3_RECORD *rec;
+    EVP_CIPHER_CTX *ds;
+    size_t l, i;
+    size_t bs;
+    const EVP_CIPHER *enc;
+    int provided;
+
+    rec = inrecs;
+    /*
+     * We shouldn't ever be called with more than one record in the SSLv3 case
+     */
+    if (n_recs != 1)
+        return 0;
+    if (sending) {
+        ds = s->enc_write_ctx;
+        if (s->enc_write_ctx == NULL)
+            enc = NULL;
+        else
+            enc = EVP_CIPHER_CTX_get0_cipher(s->enc_write_ctx);
+    } else {
+        ds = rl->enc_read_ctx;
+        if (rl->enc_read_ctx == NULL)
+            enc = NULL;
+        else
+            enc = EVP_CIPHER_CTX_get0_cipher(rl->enc_read_ctx);
+    }
+
+    provided = (EVP_CIPHER_get0_provider(enc) != NULL);
+
+    l = rec->length;
+    bs = EVP_CIPHER_CTX_get_block_size(ds);
+
+    /* COMPRESS */
+
+    if ((bs != 1) && sending && !provided) {
+        /*
+            * We only do this for legacy ciphers. Provided ciphers add the
+            * padding on the provider side.
+            */
+        i = bs - (l % bs);
+
+        /* we need to add 'i-1' padding bytes */
+        l += i;
+        /*
+            * the last of these zero bytes will be overwritten with the
+            * padding length.
+            */
+        memset(&rec->input[rec->length], 0, i);
+        rec->length += i;
+        rec->input[l - 1] = (unsigned char)(i - 1);
+    }
+
+    if (!sending) {
+        if (l == 0 || l % bs != 0) {
+            /* Publicly invalid */
+            return 0;
+        }
+        /* otherwise, rec->length >= bs */
+    }
+
+    if (provided) {
+        int outlen;
+
+        if (!EVP_CipherUpdate(ds, rec->data, &outlen, rec->input,
+                                (unsigned int)l))
+            return 0;
+        rec->length = outlen;
+
+        if (!sending && mac != NULL) {
+            /* Now get a pointer to the MAC */
+            OSSL_PARAM params[2], *p = params;
+
+            /* Get the MAC */
+            mac->alloced = 0;
+
+            *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_TLS_MAC,
+                                                    (void **)&mac->mac,
+                                                    macsize);
+            *p = OSSL_PARAM_construct_end();
+
+            if (!EVP_CIPHER_CTX_get_params(ds, params)) {
+                /* Shouldn't normally happen */
+                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
+        }
+    } else {
+        if (EVP_Cipher(ds, rec->data, rec->input, (unsigned int)l) < 1) {
+            /* Shouldn't happen */
+            RLAYERfatal(rl, SSL_AD_BAD_RECORD_MAC, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+
+        if (!sending)
+            return ssl3_cbc_remove_padding_and_mac(&rec->length,
+                                        rec->orig_len,
+                                        rec->data,
+                                        (mac != NULL) ? &mac->mac : NULL,
+                                        (mac != NULL) ? &mac->alloced : NULL,
+                                        bs,
+                                        macsize,
+                                        rl->libctx);
+    }
+
+    return 1;
+}
 
 #define MAX_PADDING 256
 /*-
@@ -690,6 +908,258 @@ static int tls1_cipher(OSSL_RECORD_LAYER *rl, SSL3_RECORD *recs, size_t n_recs,
     return 1;
 }
 
+static int tls13_cipher(OSSL_RECORD_LAYER *rl, SSL3_RECORD *recs, size_t n_recs,
+                        int sending, SSL_MAC_BUF *mac, size_t macsize,
+                        /* TODO(RECLAYER): Remove me */ SSL_CONNECTION *s)
+{
+    EVP_CIPHER_CTX *ctx;
+    unsigned char iv[EVP_MAX_IV_LENGTH], recheader[SSL3_RT_HEADER_LENGTH];
+    size_t ivlen, offset, loop, hdrlen;
+    unsigned char *staticiv;
+    unsigned char *seq;
+    int lenu, lenf;
+    SSL3_RECORD *rec = &recs[0];
+    WPACKET wpkt;
+    const EVP_CIPHER *cipher;
+    int mode;
+
+    if (n_recs != 1) {
+        /* Should not happen */
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    if (sending) {
+        ctx = s->enc_write_ctx;
+        staticiv = s->write_iv;
+        seq = RECORD_LAYER_get_write_sequence(&s->rlayer);
+    } else {
+        ctx = rl->enc_read_ctx;
+        staticiv = rl->iv;
+        seq = RECORD_LAYER_get_read_sequence(&s->rlayer);
+    }
+
+    cipher = EVP_CIPHER_CTX_get0_cipher(ctx);
+    if (cipher == NULL) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    mode = EVP_CIPHER_get_mode(cipher);
+
+    /*
+     * If we're sending an alert and ctx != NULL then we must be forcing
+     * plaintext alerts. If we're reading and ctx != NULL then we allow
+     * plaintext alerts at certain points in the handshake. If we've got this
+     * far then we have already validated that a plaintext alert is ok here.
+     */
+    if (ctx == NULL || rec->type == SSL3_RT_ALERT) {
+        memmove(rec->data, rec->input, rec->length);
+        rec->input = rec->data;
+        return 1;
+    }
+
+    ivlen = EVP_CIPHER_CTX_get_iv_length(ctx);
+
+    if (!sending) {
+        /*
+         * Take off tag. There must be at least one byte of content type as
+         * well as the tag
+         */
+        if (rec->length < rl->taglen + 1)
+            return 0;
+        rec->length -= rl->taglen;
+    }
+
+    /* Set up IV */
+    if (ivlen < SEQ_NUM_SIZE) {
+        /* Should not happen */
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    offset = ivlen - SEQ_NUM_SIZE;
+    memcpy(iv, staticiv, offset);
+    for (loop = 0; loop < SEQ_NUM_SIZE; loop++)
+        iv[offset + loop] = staticiv[offset + loop] ^ seq[loop];
+
+    /* Increment the sequence counter */
+    for (loop = SEQ_NUM_SIZE; loop > 0; loop--) {
+        ++seq[loop - 1];
+        if (seq[loop - 1] != 0)
+            break;
+    }
+    if (loop == 0) {
+        /* Sequence has wrapped */
+        return 0;
+    }
+
+    if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv, sending) <= 0
+            || (!sending && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
+                                                rl->taglen,
+                                                rec->data + rec->length) <= 0)) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    /* Set up the AAD */
+    if (!WPACKET_init_static_len(&wpkt, recheader, sizeof(recheader), 0)
+            || !WPACKET_put_bytes_u8(&wpkt, rec->type)
+            || !WPACKET_put_bytes_u16(&wpkt, rec->rec_version)
+            || !WPACKET_put_bytes_u16(&wpkt, rec->length + rl->taglen)
+            || !WPACKET_get_total_written(&wpkt, &hdrlen)
+            || hdrlen != SSL3_RT_HEADER_LENGTH
+            || !WPACKET_finish(&wpkt)) {
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        WPACKET_cleanup(&wpkt);
+        return 0;
+    }
+
+    /*
+     * For CCM we must explicitly set the total plaintext length before we add
+     * any AAD.
+     */
+    if ((mode == EVP_CIPH_CCM_MODE
+                 && EVP_CipherUpdate(ctx, NULL, &lenu, NULL,
+                                     (unsigned int)rec->length) <= 0)
+            || EVP_CipherUpdate(ctx, NULL, &lenu, recheader,
+                                sizeof(recheader)) <= 0
+            || EVP_CipherUpdate(ctx, rec->data, &lenu, rec->input,
+                                (unsigned int)rec->length) <= 0
+            || EVP_CipherFinal_ex(ctx, rec->data + lenu, &lenf) <= 0
+            || (size_t)(lenu + lenf) != rec->length) {
+        return 0;
+    }
+    if (sending) {
+        /* Add the tag */
+        if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, rl->taglen,
+                                rec->data + rec->length) <= 0) {
+            RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+        rec->length += rl->taglen;
+    }
+
+    return 1;
+}
+
+static const unsigned char ssl3_pad_1[48] = {
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
+};
+
+static const unsigned char ssl3_pad_2[48] = {
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c
+};
+
+static int ssl3_mac(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec, unsigned char *md,
+                    int sending, SSL_CONNECTION *ssl)
+{
+    unsigned char *mac_sec, *seq;
+    const EVP_MD_CTX *hash;
+    unsigned char *p, rec_char;
+    size_t md_size;
+    size_t npad;
+    int t;
+
+    if (sending) {
+        mac_sec = &(ssl->s3.write_mac_secret[0]);
+        seq = RECORD_LAYER_get_write_sequence(&ssl->rlayer);
+        hash = ssl->write_hash;
+    } else {
+        mac_sec = &(rl->mac_secret[0]);
+        seq = RECORD_LAYER_get_read_sequence(&ssl->rlayer);
+        hash = rl->read_hash;
+    }
+
+    t = EVP_MD_CTX_get_size(hash);
+    if (t < 0)
+        return 0;
+    md_size = t;
+    npad = (48 / md_size) * md_size;
+
+    if (!sending
+        && EVP_CIPHER_CTX_get_mode(rl->enc_read_ctx) == EVP_CIPH_CBC_MODE
+        && ssl3_cbc_record_digest_supported(hash)) {
+#ifdef OPENSSL_NO_DEPRECATED_3_0
+        return 0;
+#else
+        /*
+         * This is a CBC-encrypted record. We must avoid leaking any
+         * timing-side channel information about how many blocks of data we
+         * are hashing because that gives an attacker a timing-oracle.
+         */
+
+        /*-
+         * npad is, at most, 48 bytes and that's with MD5:
+         *   16 + 48 + 8 (sequence bytes) + 1 + 2 = 75.
+         *
+         * With SHA-1 (the largest hash speced for SSLv3) the hash size
+         * goes up 4, but npad goes down by 8, resulting in a smaller
+         * total size.
+         */
+        unsigned char header[75];
+        size_t j = 0;
+        memcpy(header + j, mac_sec, md_size);
+        j += md_size;
+        memcpy(header + j, ssl3_pad_1, npad);
+        j += npad;
+        memcpy(header + j, seq, 8);
+        j += 8;
+        header[j++] = rec->type;
+        header[j++] = (unsigned char)(rec->length >> 8);
+        header[j++] = (unsigned char)(rec->length & 0xff);
+
+        /* Final param == is SSLv3 */
+        if (ssl3_cbc_digest_record(EVP_MD_CTX_get0_md(hash),
+                                   md, &md_size,
+                                   header, rec->input,
+                                   rec->length, rec->orig_len,
+                                   mac_sec, md_size, 1) <= 0)
+            return 0;
+#endif
+    } else {
+        unsigned int md_size_u;
+        /* Chop the digest off the end :-) */
+        EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
+
+        if (md_ctx == NULL)
+            return 0;
+
+        rec_char = rec->type;
+        p = md;
+        s2n(rec->length, p);
+        if (EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0
+            || EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0
+            || EVP_DigestUpdate(md_ctx, ssl3_pad_1, npad) <= 0
+            || EVP_DigestUpdate(md_ctx, seq, 8) <= 0
+            || EVP_DigestUpdate(md_ctx, &rec_char, 1) <= 0
+            || EVP_DigestUpdate(md_ctx, md, 2) <= 0
+            || EVP_DigestUpdate(md_ctx, rec->input, rec->length) <= 0
+            || EVP_DigestFinal_ex(md_ctx, md, NULL) <= 0
+            || EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0
+            || EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0
+            || EVP_DigestUpdate(md_ctx, ssl3_pad_2, npad) <= 0
+            || EVP_DigestUpdate(md_ctx, md, md_size) <= 0
+            || EVP_DigestFinal_ex(md_ctx, md, &md_size_u) <= 0) {
+            EVP_MD_CTX_free(md_ctx);
+            return 0;
+        }
+
+        EVP_MD_CTX_free(md_ctx);
+    }
+
+    ssl3_record_sequence_update(seq);
+    return 1;
+}
+
 static int tls1_mac(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec, unsigned char *md,
                     int sending, SSL_CONNECTION *ssl)
 {
@@ -798,7 +1268,6 @@ static int tls1_mac(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec, unsigned char *md,
     return ret;
 }
 
-
 struct record_functions_st tls_any_funcs = {
     tls_any_set_crypto_state,
     tls_any_cipher,
@@ -806,8 +1275,8 @@ struct record_functions_st tls_any_funcs = {
 };
 
 struct record_functions_st tls_1_3_funcs = {
-    tls_fail_set_crypto_state,
-    NULL,
+    tls13_set_crypto_state,
+    tls13_cipher,
     NULL
 };
 
@@ -830,9 +1299,9 @@ struct record_functions_st tls_1_0_funcs = {
 };
 
 struct record_functions_st ssl_3_0_funcs = {
-    tls_fail_set_crypto_state,
-    NULL,
-    NULL
+    ssl3_set_crypto_state,
+    ssl3_cipher,
+    ssl3_mac
 };
 
 static int tls_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio);
@@ -1017,7 +1486,7 @@ static int tls_read_n(OSSL_RECORD_LAYER *rl, size_t n, size_t max, int extend,
      * Ktls always reads full records.
      * Also, we always act like read_ahead is set for DTLS.
      */
-    if (!BIO_get_ktls_recv(s->rbio) && !rl->read_ahead
+    if (!BIO_get_ktls_recv(rl->bio) && !rl->read_ahead
             && !rl->isdtls) {
         /* ignore max parameter */
         max = n;
@@ -1344,7 +1813,7 @@ static int tls_get_more_records(OSSL_RECORD_LAYER *rl,
                 }
 
                 if (SSL_CONNECTION_IS_TLS13(s)
-                        && s->enc_read_ctx != NULL
+                        && rl->enc_read_ctx != NULL
                         && !using_ktls) {
                     if (thisrr->type != SSL3_RT_APPLICATION_DATA
                             && (thisrr->type != SSL3_RT_CHANGE_CIPHER_SPEC
@@ -1562,10 +2031,7 @@ static int tls_get_more_records(OSSL_RECORD_LAYER *rl,
      * TODO(RECLAYER): Only call rl functions once TLSv1.3/SSLv3 is moved to new
      * record layer code
      */
-    if (!SSL_CONNECTION_IS_TLS13(s))
-        enc_err = rl->funcs->cipher(rl, rr, num_recs, 0, macbufs, mac_size, s);
-    else
-        enc_err = ssl->method->ssl3_enc->enc(s, rr, num_recs, 0, macbufs, mac_size);
+    enc_err = rl->funcs->cipher(rl, rr, num_recs, 0, macbufs, mac_size, s);
 
     /*-
      * enc_err is:
@@ -1666,7 +2132,7 @@ static int tls_get_more_records(OSSL_RECORD_LAYER *rl,
         }
 
         if (SSL_CONNECTION_IS_TLS13(s)
-                && s->enc_read_ctx != NULL
+                && rl->enc_read_ctx != NULL
                 && thisrr->type != SSL3_RT_ALERT) {
             /*
              * The following logic are irrelevant in KTLS: the kernel provides
@@ -1875,6 +2341,10 @@ static OSSL_RECORD_LAYER *tls_new_record_layer(OSSL_LIB_CTX *libctx,
         goto err;
     }
 
+    /*
+     * TODO(RECLAYER): Need to handle the case where the params are updated
+     * after the record layer has been created.
+     */
     p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_OPTIONS);
     if (p != NULL && !OSSL_PARAM_get_uint64(p, &rl->options)) {
         RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
@@ -1887,11 +2357,22 @@ static OSSL_RECORD_LAYER *tls_new_record_layer(OSSL_LIB_CTX *libctx,
         goto err;
     }
 
-
-    p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_READ_AHEAD);
-    if (p != NULL && !OSSL_PARAM_get_int(p, &rl->read_ahead)) {
-        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-        goto err;
+    if (level == OSSL_RECORD_PROTECTION_LEVEL_APPLICATION) {
+        /*
+         * We ignore any read_ahead setting prior to the application protection
+         * level. Otherwise we may read ahead data in a lower protection level
+         * that is destined for a higher protection level. To simplify the logic
+         * we don't support that at this stage.
+         */
+        /*
+         * TODO(RECLAYER): Handle the case of read_ahead at the application
+         * level and a key update/reneg occurs.
+         */
+        p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_READ_AHEAD);
+        if (p != NULL && !OSSL_PARAM_get_int(p, &rl->read_ahead)) {
+            RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
+            goto err;
+        }
     }
 
     rl->libctx = libctx;
@@ -1984,6 +2465,7 @@ static OSSL_RECORD_LAYER *dtls_new_record_layer(OSSL_LIB_CTX *libctx,
 
 static void tls_free(OSSL_RECORD_LAYER *rl)
 {
+    /* TODO(RECLAYER): Cleanse sensitive fields */
     BIO_free(rl->bio);
     OPENSSL_free(rl);
 }