JDK-8163419 : Final CCS and Finished DTLS messages can't be re-transmitted
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 9
  • Priority: P2
  • Status: Closed
  • Resolution: Delivered
  • OS: generic
  • CPU: generic
  • Submitted: 2016-08-08
  • Updated: 2021-03-15
  • Resolved: 2016-11-29
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 9
9Resolved
Related Reports
Blocks :  
Duplicate :  
Relates :  
Description
While DTLS handshaking, if some messages got lost, they should be re-created with new sequence numbers, and re-sent.

It doesn't work with final CCS and Finished messages. If final CCS/Finished messages got lost, then further calls of SSLEngine.wrap() don't produce new CCS/Finished messages with new sequence numbers. It happens because SSLEngine switches to NOT_HANDSHAKING state, and expects application data. As a result, new CCS/Finished messages can't be generated.

There is a workaround which may work sometimes (not sure about all DTLS implementations). Final CCS/Finished messages can be stored, and re-sent if necessary. But in this case, messages with old sequence numbers are re-sent which violates DTLS spec.
Comments
This issue will be addressed together with JDK-8161086.
14-09-2016

This is another fix which doesn't check if there is no application data. Instead, it follows DTLS spec: https://tools.ietf.org/html/rfc6347#section-4.2.4 ... In addition, for at least twice the default MSL defined for [TCP], when in the FINISHED state, the node that transmits the last flight (the server in an ordinary handshake or the client in a resumed handshake) MUST respond to a retransmit of the peer's last flight with a retransmit of the last flight. ... http://cr.openjdk.java.net/~asmotrak/8163419/webrev.01/
11-08-2016

The following patch seems to fix this problem: http://cr.openjdk.java.net/~asmotrak/8163419/webrev.00/ It updates SSLEngineImpl to re-transmit CCS/Finished messages if: - the protocol is DTLS - SSLEngine finished handshaking (the status is NOT_HANDSHAKING) - no application data passed to wrap
08-08-2016

I see two possible scenarios when final CCS/Finished messages were lost. A local SSLEngine switched to NOT_HANDSHAKING state, and expects first application data. 1. The remote peer doesn't re-send anything, and as a result, timeout happens. 2. The remote peer re-sends previous handshaking messages. But a local SSLEngine expects application data. In this case, SSLEngine.unwrap() doesn't return any application data. For example, see DTLSOverDatagram.java. recBuffer.remaining() returns 0 if handshake messages were received: http://cr.openjdk.java.net/~asmotrak/8159416/webrev.08/test/javax/net/ssl/DTLS/DTLSOverDatagram.java.html 398 byte[] buf = new byte[BUFFER_SIZE]; 399 DatagramPacket packet = new DatagramPacket(buf, buf.length); 400 log(side, "receiveAppData(): wait for a packet"); 401 try { 402 socket.receive(packet); 403 log(side, "receiveAppData(): received a packet"); 404 } catch (SocketTimeoutException e) { 405 log(side, "Warning: " + e); 406 continue; 407 } 408 ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength()); 409 ByteBuffer recBuffer = ByteBuffer.allocate(BUFFER_SIZE); 410 SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer); 411 log(side, "receiveAppData(): engine status is " + rs); 412 recBuffer.flip(); 413 if (recBuffer.remaining() != 0) { 414 return recBuffer; 415 } In both cases, SSLEngine.wrap() should be called to create new CCS/Finished messages. CCC 8043758 says the following about re-transmission: http://ccc.us.oracle.com/8043758 ... Q-2. How to handle retransmission in an application? ---------------------------------------------------- SSLEngine.unwrap() and SSLEngine.wrap() can be used together to handle retransmission in an application. A typical scenario for handling DTLS handshaking retransmission looks like: 1. Create and initialize an instance of DTLS SSLEngine, begin handshaking. 2. If the handshake status is HandshakeStatus.NEED_UNWRAP, wait for data from network. 3. If wait timeout, as indicates that the previous delivered handshake messages may have been lost, call SSLEngine.wrap(). Note that for DTLS handshaking retransmission, the determined handshake status is not necessarily to be HandshakeStatus.NEED_WRAP for the call to SSLEngine.wrap(). 4. Deliver the wrapped packets if available. 5. Determine the handshake status for further processes. ... Handshake packets can be create with the following code: http://cr.openjdk.java.net/~asmotrak/8159416/webrev.08/test/javax/net/ssl/DTLS/DTLSOverDatagram.java.html 425 // produce handshake packets 426 boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr, 427 String side, List<DatagramPacket> packets) throws Exception { 428 429 boolean endLoops = false; 430 int loops = MAX_HANDSHAKE_LOOPS; 431 while (!endLoops && 432 (serverException == null) && (clientException == null)) { 433 434 if (--loops < 0) { 435 throw new RuntimeException( 436 "Too much loops to produce handshake packets"); 437 } 438 439 ByteBuffer oNet = ByteBuffer.allocate(32768); 440 ByteBuffer oApp = ByteBuffer.allocate(0); 441 SSLEngineResult r = engine.wrap(oApp, oNet); Currently it doesn't work if we want to re-create final CCS/Finished messages. SSLEngine.wrap() returns no handshake data. An empty "oApp" buffer is passed to SSLEngine.wrap() to re-create handshake messages. SSLEngine.wrap() might recognize that no application data is passed to it, and re-generate last handshake messages even if it is in NOT_HANDSHAKING state.
08-08-2016

The problem can be reproduced by PacketLoss.java test which was added for JDK-8161086 http://cr.openjdk.java.net/~asmotrak/8159416/webrev.08/ The test drops different packets while DTLS handshaking, and one of testcases drops final CCS or Finished messages.
08-08-2016