JDK-8221253 : TLSv1.3 may generate TLSInnerPlainText longer than 2^14+1 bytes
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 11,12,13
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86_64
  • Submitted: 2019-03-19
  • Updated: 2020-11-23
  • Resolved: 2019-05-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13 JDK 8 Other
11.0.5-oracleFixed 13 b21Fixed 8u261Fixed openjdk8u272Fixed
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
Linux 64-bit. Originally discovered with OpenJDK 11.0.2, but I can reproduce it with the tip of http://hg.openjdk.java.net/jdk-updates/jdk12u/

A DESCRIPTION OF THE PROBLEM :
The TLSv1.3 spec has this to say about the allowed length of TLSInnerPlaintext:

   The presence of padding does not change the overall record size
   limitations - the full encoded TLSInnerPlaintext MUST NOT exceed 2^14
   + 1 octets.  If the maximum fragment length is reduced, as for
   example by the max_fragment_length extension from [RFC6066], then the
   reduced limit applies to the full plaintext, including the content
   type and padding.

However, t13Encrypt in src/java.base/share/classes/sun/security/ssl/OutputRecord.java will always append 16 padding bytes to the fragment, without taking into account its original size:

    private static final class T13PaddingHolder {
        private static final byte[] zeros = new byte[16];
    }

    private long t13Encrypt(
            SSLWriteCipher encCipher, byte contentType, int headerSize) {
        if (!encCipher.isNullCipher()) {
            // inner plaintext
            write(contentType);
            write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length);
        }
        ...

This causes trouble with TLSv1.3 servers because we set Record.maxDataSize to 2^14, and so the added 1 + 16 bytes may cause the resulting TLSInnerPlaintext to go over the length limit.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run Test.java

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test TLSv1.3 server page at https://tls13.crypto.mozilla.org/ should be printed to console.
ACTUAL -
https://tls13.crypto.mozilla.org/ returns a "record_overflow" error.

---------- BEGIN SOURCE ----------
import javax.net.ssl.HttpsURLConnection;
import java.net.URL;
import java.io.*;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;

public class Test {
    public static void main(String[] args) throws Exception {
        StringBuilder data = new StringBuilder();
        data.append("----abc\r\n")
            .append("Content-Disposition: form-data; name=\"json_file\"; filename=\"test.json\"\r\n")
            .append("Content-Type: application/octet-stream\r\n")
            .append("\r\n");
        for (int i = 0; i < 325717; i++)
            data.append("a");
        data.append("----abc--");
        URL url = new URL("https://tls13.crypto.mozilla.org/");
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=--abc");
        OutputStreamWriter osw = new OutputStreamWriter(connection.getOutputStream());
        osw.write(data.toString());
        osw.flush();
        osw.close();
        BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String line;
        while((line = br.readLine()) != null) {
            System.out.println(line);
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Disabling TLSv1.3 support by setting jdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2"


Comments
Fix Request (11u): the fix applies almost cleanly (few whitespace issues) to jdk11u not applicable to jdk8 as it misses tls1.3
10-06-2019

Please also concern the length limits on the received records. RFC 8446 section 6.2 record_overflow: A TLSCiphertext record was received that had a length more than 2^14 + 256 bytes, or a record decrypted to a TLSPlaintext record with more than 2^14 bytes (or some other negotiated limit). If the received data doesn't obey these limits, JSSE should alert record_overflow immediately. It looks the current implementation doesn't care these limits.
06-05-2019

The portion of TLS1.3 spec referred to in bug report is at : https://tools.ietf.org/id/draft-ietf-tls-tls13-26.html#rfc.section.5.4 To reproduce the issue, run the attached test case: JDK 11.0.2 - Fail JDK 12 - Fail JDK 13-ea+11 - Fail Output: Exception in thread "main" javax.net.ssl.SSLException: Received fatal alert: record_overflow at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:133) at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:307) at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:285) at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:180) at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164) at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1183) at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1153) at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:828) at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252) at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292) at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351) at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:746) at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:689) at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:717) at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1604) at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509) at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:227) at JI9059846.main(JI9059846.java:28)
21-03-2019