test/javax/net/ssl/DTLS/DTLSOverDatagram.java

Print this page
8167680 DTLS implementation bugs

@@ -46,14 +46,10 @@
 /**
  * An example to show the way to use SSLEngine in datagram connections.
  */
 public class DTLSOverDatagram {
 
-    static {
-        System.setProperty("javax.net.debug", "ssl");
-    }
-
     private static int MAX_HANDSHAKE_LOOPS = 200;
     private static int MAX_APP_READ_LOOPS = 60;
     private static int SOCKET_TIMEOUT = 10 * 1000; // in millis
     private static int BUFFER_SIZE = 1024;
     private static int MAXIMUM_PACKET_SIZE = 1024;

@@ -158,10 +154,11 @@
                 throw new RuntimeException(
                         "Too much loops to produce handshake packets");
             }
 
             SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+            log(side, "=======handshake(" + loops + ", " + hs + ")=======");
             if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
                 hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
 
                 log(side, "Receive DTLS records, handshake status is " + hs);
 

@@ -237,10 +234,11 @@
             } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
                 List<DatagramPacket> packets = new ArrayList<>();
                 boolean finished = produceHandshakePackets(
                     engine, peerAddr, side, packets);
 
+                log(side, "Produced " + packets.size() + " packets");
                 for (DatagramPacket p : packets) {
                     socket.send(p);
                 }
 
                 if (finished) {

@@ -250,18 +248,20 @@
                     endLoops = true;
                 }
             } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                 runDelegatedTasks(engine);
             } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
-                log(side, "Handshake status is NOT_HANDSHAKING, finish the loop");
+                log(side,
+                    "Handshake status is NOT_HANDSHAKING, finish the loop");
                 endLoops = true;
             } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
                 throw new Exception(
                         "Unexpected status, SSLEngine.getHandshakeStatus() "
                                 + "shouldn't return FINISHED");
             } else {
-                throw new Exception("Can't reach here, handshake status is " + hs);
+                throw new Exception(
+                        "Can't reach here, handshake status is " + hs);
             }
         }
 
         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
         log(side, "Handshake finished, status is " + hs);

@@ -277,11 +277,13 @@
         }
         log(side, "Negotiated protocol is " + session.getProtocol());
         log(side, "Negotiated cipher suite is " + session.getCipherSuite());
 
         // handshake status should be NOT_HANDSHAKING
-        // according to the spec, SSLEngine.getHandshakeStatus() can't return FINISHED
+        //
+        // According to the spec, SSLEngine.getHandshakeStatus() can't
+        // return FINISHED.
         if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
             throw new Exception("Unexpected handshake status " + hs);
         }
     }
 

@@ -346,17 +348,20 @@
             SSLEngineResult r = engine.wrap(oApp, oNet);
             oNet.flip();
 
             SSLEngineResult.Status rs = r.getStatus();
             SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
+            log(side, "====packet(" + loops + ", " + rs + ", " + hs + ")====");
             if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
                 // the client maximum fragment size config does not work?
                 throw new Exception("Buffer overflow: " +
                             "incorrect server maximum fragment size");
             } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
-                log(side, "Produce handshake packets: BUFFER_UNDERFLOW occured");
-                log(side, "Produce handshake packets: Handshake status: " + hs);
+                log(side,
+                        "Produce handshake packets: BUFFER_UNDERFLOW occured");
+                log(side,
+                        "Produce handshake packets: Handshake status: " + hs);
                 // bad packet, or the client maximum fragment size
                 // config does not work?
                 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                     throw new Exception("Buffer underflow: " +
                             "incorrect server maximum fragment size");

@@ -451,10 +456,57 @@
         }
 
         return packets;
     }
 
+    // Get a datagram packet for the specified handshake type.
+    static DatagramPacket getPacket(
+            List<DatagramPacket> packets, byte handshakeType) {
+        boolean matched = false;
+        for (DatagramPacket packet : packets) {
+            byte[] data = packet.getData();
+            int offset = packet.getOffset();
+            int length = packet.getLength();
+
+            // Normally, this pakcet should be a handshake message
+            // record.  However, even if the underlying platform
+            // splits the record more, we don't really worry about
+            // the improper packet loss because DTLS implementation
+            // should be able to handle packet loss properly.
+            //
+            // See RFC 6347 for the detailed format of DTLS records.
+            if (handshakeType == -1) {      // ChangeCipherSpec
+                // Is it a ChangeCipherSpec message?
+                matched = (length == 14) && (data[offset] == 0x14);
+            } else if ((length >= 25) &&    // 25: handshake mini size
+                (data[offset] == 0x16)) {   // a handshake message
+
+                // check epoch number for initial handshake only
+                if (data[offset + 3] == 0x00) {     // 3,4: epoch
+                    if (data[offset + 4] == 0x00) { // plaintext
+                        matched =
+                            (data[offset + 13] == handshakeType);
+                    } else {                        // cipherext
+                        // The 1st ciphertext is a Finished message.
+                        //
+                        // If it is not proposed to loss the Finished
+                        // message, it is not necessary to check the
+                        // following packets any mroe as a Finished
+                        // message is the last handshake message.
+                        matched = (handshakeType == 20);
+                    }
+                }
+            }
+
+            if (matched) {
+                return packet;
+            }
+        }
+
+        return null;
+    }
+
     // run delegated tasks
     void runDelegatedTasks(SSLEngine engine) throws Exception {
         Runnable runnable;
         while ((runnable = engine.getDelegatedTask()) != null) {
             runnable.run();