ADDITIONAL SYSTEM INFORMATION :
Tested against jdk 9,14,15,17 with same result
A DESCRIPTION OF THE PROBLEM :
The final CCC and finished message cannot be retransmitted.
It is an implementation bug. Every handshake message should be able to get retransmitted.
The bug has been addressed, but not fixed by:
"JDK-8167680 : DTLS implementation bugs" (https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8167680) was implemented to address the issue of retransmitting Final CCS and Finished DTLS messages addressing "JDK-8163419 : Final CCS and Finished DTLS messages can't be re-transmitted" (https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8163419)
The implementation is now sending an extra set of CCS and Finished packets regardless if there is packet loss or not, which makes the test-case work (https://github.com/AdoptOpenJDK/openjdk-jdk/blob/master/test/jdk/javax/net/ssl/DTLS/PacketLossRetransmission.java) due to the test only dropping one packets, but it cannot recover if the extra set of CCS and Finished are also lost. In addition always sending an extra set of CCS and Finished is adding unnecessary overhead.
Example
Client Server
....
-- ClientKeyExchange -->
-- ChangeCipherSpec -->
-- Finished -->
X <-- ChangeCipherSpec --
X <-- Finished --
X <-- ChangeCipherSpec --
X <-- Finished --
Client repeatedly sends last flight
----- ... --->
-- ClientKeyExchange -->
-- ChangeCipherSpec -->
-- Finished -->
Server ignores because handshake it complete
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
A modified version of PacketLossRetransmission has been added, which takes the number of packets to drop as parameter. "Actual Result" shows the result of PacketLossRetransmission being run with scenario "drop finished packets 2 times"
Params : -Djdk.tls.client.enableSessionTicketExtension=false server 20 2
DTLSOverDatagram was modified (not included) to print content type and handshake types.
Server socket timeout was increased from 10s to 20s to allow for client retransmission
Line 565: serverSocket.setSoTimeout(SOCKET_TIMEOUT*2);
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
After receiving the client retransmission the server sends:
Server: ----produce handshake packet(99, OK, NEED_WRAP)----CHANGE_CIPHER_SPEC
Server: ----produce handshake packet(98, OK, NEED_WRAP)----HANDSHAKE FINISHED
ACTUAL -
Server: =======handshake(199, NEED_UNWRAP)=======
Server: Receive DTLS records, handshake status is NEED_UNWRAP
Client: =======handshake(199, NEED_WRAP)=======
Client: ----produce handshake packet(99, OK, NEED_UNWRAP)----HANDSHAKE CLIENT_HELLO
Client: Produced 1 packets
Client: =======handshake(198, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Server: =======handshake(198, NEED_TASK)=======
Server: =======handshake(197, NEED_WRAP)=======
Server: ----produce handshake packet(99, OK, NEED_UNWRAP)----HANDSHAKE HELLO_VERIFY_REQUEST
Server: Produced 1 packets
Server: =======handshake(196, NEED_UNWRAP)=======
Server: Receive DTLS records, handshake status is NEED_UNWRAP
Client: =======handshake(197, NEED_TASK)=======
Client: =======handshake(196, NEED_WRAP)=======
Client: ----produce handshake packet(99, OK, NEED_UNWRAP)----HANDSHAKE CLIENT_HELLO
Client: Produced 1 packets
Client: =======handshake(195, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Server: =======handshake(195, NEED_TASK)=======
Server: =======handshake(194, NEED_WRAP)=======
Server: ----produce handshake packet(99, OK, NEED_WRAP)----HANDSHAKE SERVER_HELLO
Server: ----produce handshake packet(98, OK, NEED_WRAP)----HANDSHAKE CERTIFICATE
Server: ----produce handshake packet(97, OK, NEED_WRAP)----HANDSHAKE SERVER_KEY_EXCHANGE
Server: ----produce handshake packet(96, OK, NEED_UNWRAP)----HANDSHAKE SERVER_HELLO_DONE
Server: Produced 4 packets
Server: =======handshake(193, NEED_UNWRAP)=======
Server: Receive DTLS records, handshake status is NEED_UNWRAP
Client: =======handshake(194, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Client: =======handshake(193, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Client: =======handshake(192, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Client: =======handshake(191, NEED_TASK)=======
Client: =======handshake(190, NEED_UNWRAP_AGAIN)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP_AGAIN
Client: =======handshake(189, NEED_TASK)=======
Client: =======handshake(188, NEED_UNWRAP_AGAIN)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP_AGAIN
Client: =======handshake(187, NEED_TASK)=======
Client: =======handshake(186, NEED_UNWRAP_AGAIN)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP_AGAIN
Client: =======handshake(185, NEED_TASK)=======
Client: =======handshake(184, NEED_WRAP)=======
Client: ----produce handshake packet(99, OK, NEED_WRAP)----HANDSHAKE CLIENT_KEY_EXCHANGE
Client: ----produce handshake packet(98, OK, NEED_WRAP)----CHANGE_CIPHER_SPEC
Client: ----produce handshake packet(97, OK, NEED_UNWRAP)----HANDSHAKE FINISHED
Client: Produced 3 packets
Client: =======handshake(183, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Server: =======handshake(192, NEED_UNWRAP)=======
Server: Receive DTLS records, handshake status is NEED_UNWRAP
Server: =======handshake(191, NEED_UNWRAP)=======
Server: Receive DTLS records, handshake status is NEED_UNWRAP
Server: =======handshake(190, NEED_TASK)=======
Server: =======handshake(189, NEED_UNWRAP_AGAIN)=======
Server: Receive DTLS records, handshake status is NEED_UNWRAP_AGAIN
Server: =======handshake(188, NEED_UNWRAP_AGAIN)=======
Server: Receive DTLS records, handshake status is NEED_UNWRAP_AGAIN
Server: =======handshake(187, NEED_WRAP)=======
Server: ----produce handshake packet(99, OK, NEED_WRAP)----CHANGE_CIPHER_SPEC
Server: ----produce handshake packet(98, OK, NEED_WRAP)----HANDSHAKE FINISHED
Server: ----produce handshake packet(97, OK, NEED_WRAP)----CHANGE_CIPHER_SPEC
Server: ----produce handshake packet(96, OK, FINISHED)----HANDSHAKE FINISHED
Server: Produce handshake packets: Handshake status is FINISHED, finish the loop
Loss a packet of handshake message
Loss a packet of handshake message
Server: Produced 2 packets
Server: Handshake status is FINISHED after producing handshake packets, finish the loop
Server: Handshake finished, status is NOT_HANDSHAKING
Client: =======handshake(182, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Server: Negotiated protocol is DTLSv1.2
Client: =======handshake(181, NEED_UNWRAP)=======
Server: Negotiated cipher suite is TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Client: Warning: java.net.SocketTimeoutException: Receive timed out
Client: ----produce handshake packet(99, OK, NEED_WRAP)----HANDSHAKE CLIENT_KEY_EXCHANGE
Client: ----produce handshake packet(98, OK, NEED_WRAP)----CHANGE_CIPHER_SPEC
Client: ----produce handshake packet(97, OK, NEED_UNWRAP)----HANDSHAKE FINISHED
Client: Reproduced 3 packets
Reproduced packet
Reproduced packet
Reproduced packet
Client: New handshake status is NEED_UNWRAP
Client: =======handshake(180, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 58 bytesProduced = 0 packet type: HANDSHAKE
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 14 bytesProduced = 0 packet type: CHANGE_CIPHER_SPEC
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 61 bytesProduced = 0 packet type: HANDSHAKE
Client: Warning: java.net.SocketTimeoutException: Receive timed out
Client: ----produce handshake packet(99, OK, NEED_WRAP)----HANDSHAKE CLIENT_KEY_EXCHANGE
Client: ----produce handshake packet(98, OK, NEED_WRAP)----CHANGE_CIPHER_SPEC
Client: ----produce handshake packet(97, OK, NEED_UNWRAP)----HANDSHAKE FINISHED
Client: Reproduced 3 packets
Reproduced packet
Reproduced packet
Reproduced packet
Client: New handshake status is NEED_UNWRAP
Client: =======handshake(179, NEED_UNWRAP)=======
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 58 bytesProduced = 0 packet type: HANDSHAKE
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 14 bytesProduced = 0 packet type: CHANGE_CIPHER_SPEC
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 61 bytesProduced = 0 packet type: HANDSHAKE
Client: Warning: java.net.SocketTimeoutException: Receive timed out
Client: ----produce handshake packet(99, OK, NEED_WRAP)----HANDSHAKE CLIENT_KEY_EXCHANGE
Client: ----produce handshake packet(98, OK, NEED_WRAP)----CHANGE_CIPHER_SPEC
Client: ----produce handshake packet(97, OK, NEED_UNWRAP)----HANDSHAKE FINISHED
Client: Reproduced 3 packets
Reproduced packet
Reproduced packet
Reproduced packet
Client: New handshake status is NEED_UNWRAP
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 58 bytesProduced = 0 packet type: HANDSHAKE
Client: =======handshake(178, NEED_UNWRAP)=======
Client: Receive DTLS records, handshake status is NEED_UNWRAP
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 14 bytesProduced = 0 packet type: CHANGE_CIPHER_SPEC
Server: Received packet no data - Engine status is Status = OK HandshakeStatus = NOT_HANDSHAKING
bytesConsumed = 61 bytesProduced = 0 packet type: HANDSHAKE
---------- BEGIN SOURCE ----------
public class PacketLossRetransmission extends DTLSOverDatagram {
private static boolean isClient;
private static byte handshakeType;
private static int packetsToDrop;
public static void main(String[] args) throws Exception {
isClient = args[0].equals("client");
handshakeType = Byte.valueOf(args[1]);
packetsToDrop = Integer.parseInt(args[2]);
PacketLossRetransmission testCase = new PacketLossRetransmission();
testCase.runTest(testCase);
}
@Override
boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
String side, List<DatagramPacket> packets) throws Exception {
boolean finished = super.produceHandshakePackets(
engine, socketAddr, side, packets);
Iterator<DatagramPacket> packetIterator = packets.iterator();
while (packetIterator.hasNext()) {
DatagramPacket packet = packetIterator.next();
if ((packetsToDrop > 0) && (!(isClient ^ engine.getUseClientMode()))) {
packet = getPacket(Collections.singletonList(packet), handshakeType);
if (packet != null) {
packetsToDrop--;
System.out.println("Loss a packet of handshake message ");
packetIterator.remove();
}
}
}
return finished;
}
}
---------- END SOURCE ----------
FREQUENCY : always