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

Print this page
8167680 DTLS implementation bugs


  31  * @modules java.base/sun.security.util
  32  * @run main/othervm DTLSOverDatagram
  33  */
  34 
  35 import java.io.*;
  36 import java.nio.*;
  37 import java.net.*;
  38 import java.util.*;
  39 import java.security.*;
  40 import java.security.cert.*;
  41 import javax.net.ssl.*;
  42 import java.util.concurrent.*;
  43 
  44 import sun.security.util.HexDumpEncoder;
  45 
  46 /**
  47  * An example to show the way to use SSLEngine in datagram connections.
  48  */
  49 public class DTLSOverDatagram {
  50 
  51     static {
  52         System.setProperty("javax.net.debug", "ssl");
  53     }
  54 
  55     private static int MAX_HANDSHAKE_LOOPS = 200;
  56     private static int MAX_APP_READ_LOOPS = 60;
  57     private static int SOCKET_TIMEOUT = 10 * 1000; // in millis
  58     private static int BUFFER_SIZE = 1024;
  59     private static int MAXIMUM_PACKET_SIZE = 1024;
  60 
  61     /*
  62      * The following is to set up the keystores.
  63      */
  64     private static String pathToStores = "../etc";
  65     private static String keyStoreFile = "keystore";
  66     private static String trustStoreFile = "truststore";
  67     private static String passwd = "passphrase";
  68 
  69     private static String keyFilename =
  70             System.getProperty("test.src", ".") + "/" + pathToStores +
  71                 "/" + keyStoreFile;
  72     private static String trustFilename =
  73             System.getProperty("test.src", ".") + "/" + pathToStores +
  74                 "/" + trustStoreFile;


 143 
 144         return engine;
 145     }
 146 
 147     // handshake
 148     void handshake(SSLEngine engine, DatagramSocket socket,
 149             SocketAddress peerAddr, String side) throws Exception {
 150 
 151         boolean endLoops = false;
 152         int loops = MAX_HANDSHAKE_LOOPS;
 153         engine.beginHandshake();
 154         while (!endLoops &&
 155                 (serverException == null) && (clientException == null)) {
 156 
 157             if (--loops < 0) {
 158                 throw new RuntimeException(
 159                         "Too much loops to produce handshake packets");
 160             }
 161 
 162             SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();

 163             if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
 164                 hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
 165 
 166                 log(side, "Receive DTLS records, handshake status is " + hs);
 167 
 168                 ByteBuffer iNet;
 169                 ByteBuffer iApp;
 170                 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
 171                     byte[] buf = new byte[BUFFER_SIZE];
 172                     DatagramPacket packet = new DatagramPacket(buf, buf.length);
 173                     try {
 174                         socket.receive(packet);
 175                     } catch (SocketTimeoutException ste) {
 176                         log(side, "Warning: " + ste);
 177 
 178                         List<DatagramPacket> packets = new ArrayList<>();
 179                         boolean finished = onReceiveTimeout(
 180                                 engine, peerAddr, side, packets);
 181 
 182                         for (DatagramPacket p : packets) {


 222                     if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 223                         throw new Exception("Buffer underflow: " +
 224                             "incorrect client maximum fragment size");
 225                     } // otherwise, ignore this packet
 226                 } else if (rs == SSLEngineResult.Status.CLOSED) {
 227                     throw new Exception(
 228                             "SSL engine closed, handshake status is " + hs);
 229                 } else {
 230                     throw new Exception("Can't reach here, result is " + rs);
 231                 }
 232 
 233                 if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
 234                     log(side, "Handshake status is FINISHED, finish the loop");
 235                     endLoops = true;
 236                 }
 237             } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
 238                 List<DatagramPacket> packets = new ArrayList<>();
 239                 boolean finished = produceHandshakePackets(
 240                     engine, peerAddr, side, packets);
 241 

 242                 for (DatagramPacket p : packets) {
 243                     socket.send(p);
 244                 }
 245 
 246                 if (finished) {
 247                     log(side, "Handshake status is FINISHED "
 248                             + "after producing handshake packets, "
 249                             + "finish the loop");
 250                     endLoops = true;
 251                 }
 252             } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
 253                 runDelegatedTasks(engine);
 254             } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 255                 log(side, "Handshake status is NOT_HANDSHAKING, finish the loop");

 256                 endLoops = true;
 257             } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
 258                 throw new Exception(
 259                         "Unexpected status, SSLEngine.getHandshakeStatus() "
 260                                 + "shouldn't return FINISHED");
 261             } else {
 262                 throw new Exception("Can't reach here, handshake status is " + hs);

 263             }
 264         }
 265 
 266         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
 267         log(side, "Handshake finished, status is " + hs);
 268 
 269         if (engine.getHandshakeSession() != null) {
 270             throw new Exception(
 271                     "Handshake finished, but handshake session is not null");
 272         }
 273 
 274         SSLSession session = engine.getSession();
 275         if (session == null) {
 276             throw new Exception("Handshake finished, but session is null");
 277         }
 278         log(side, "Negotiated protocol is " + session.getProtocol());
 279         log(side, "Negotiated cipher suite is " + session.getCipherSuite());
 280 
 281         // handshake status should be NOT_HANDSHAKING
 282         // according to the spec, SSLEngine.getHandshakeStatus() can't return FINISHED


 283         if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 284             throw new Exception("Unexpected handshake status " + hs);
 285         }
 286     }
 287 
 288     // deliver application data
 289     void deliverAppData(SSLEngine engine, DatagramSocket socket,
 290             ByteBuffer appData, SocketAddress peerAddr) throws Exception {
 291 
 292         // Note: have not consider the packet loses
 293         List<DatagramPacket> packets =
 294                 produceApplicationPackets(engine, appData, peerAddr);
 295         appData.flip();
 296         for (DatagramPacket p : packets) {
 297             socket.send(p);
 298         }
 299     }
 300 
 301     // receive application data
 302     void receiveAppData(SSLEngine engine,


 331     boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
 332             String side, List<DatagramPacket> packets) throws Exception {
 333 
 334         boolean endLoops = false;
 335         int loops = MAX_HANDSHAKE_LOOPS;
 336         while (!endLoops &&
 337                 (serverException == null) && (clientException == null)) {
 338 
 339             if (--loops < 0) {
 340                 throw new RuntimeException(
 341                         "Too much loops to produce handshake packets");
 342             }
 343 
 344             ByteBuffer oNet = ByteBuffer.allocate(32768);
 345             ByteBuffer oApp = ByteBuffer.allocate(0);
 346             SSLEngineResult r = engine.wrap(oApp, oNet);
 347             oNet.flip();
 348 
 349             SSLEngineResult.Status rs = r.getStatus();
 350             SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();

 351             if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
 352                 // the client maximum fragment size config does not work?
 353                 throw new Exception("Buffer overflow: " +
 354                             "incorrect server maximum fragment size");
 355             } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
 356                 log(side, "Produce handshake packets: BUFFER_UNDERFLOW occured");
 357                 log(side, "Produce handshake packets: Handshake status: " + hs);


 358                 // bad packet, or the client maximum fragment size
 359                 // config does not work?
 360                 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 361                     throw new Exception("Buffer underflow: " +
 362                             "incorrect server maximum fragment size");
 363                 } // otherwise, ignore this packet
 364             } else if (rs == SSLEngineResult.Status.CLOSED) {
 365                 throw new Exception("SSLEngine has closed");
 366             } else if (rs == SSLEngineResult.Status.OK) {
 367                 // OK
 368             } else {
 369                 throw new Exception("Can't reach here, result is " + rs);
 370             }
 371 
 372             // SSLEngineResult.Status.OK:
 373             if (oNet.hasRemaining()) {
 374                 byte[] ba = new byte[oNet.remaining()];
 375                 oNet.get(ba);
 376                 DatagramPacket packet = createHandshakePacket(ba, socketAddr);
 377                 packets.add(packet);


 436         } else if (rs == SSLEngineResult.Status.CLOSED) {
 437                 throw new Exception("SSLEngine has closed");
 438         } else if (rs == SSLEngineResult.Status.OK) {
 439             // OK
 440         } else {
 441             throw new Exception("Can't reach here, result is " + rs);
 442         }
 443 
 444         // SSLEngineResult.Status.OK:
 445         if (appNet.hasRemaining()) {
 446             byte[] ba = new byte[appNet.remaining()];
 447             appNet.get(ba);
 448             DatagramPacket packet =
 449                     new DatagramPacket(ba, ba.length, socketAddr);
 450             packets.add(packet);
 451         }
 452 
 453         return packets;
 454     }
 455 















































 456     // run delegated tasks
 457     void runDelegatedTasks(SSLEngine engine) throws Exception {
 458         Runnable runnable;
 459         while ((runnable = engine.getDelegatedTask()) != null) {
 460             runnable.run();
 461         }
 462 
 463         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
 464         if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
 465             throw new Exception("handshake shouldn't need additional tasks");
 466         }
 467     }
 468 
 469     // retransmission if timeout
 470     boolean onReceiveTimeout(SSLEngine engine, SocketAddress socketAddr,
 471             String side, List<DatagramPacket> packets) throws Exception {
 472 
 473         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
 474         if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 475             return false;




  31  * @modules java.base/sun.security.util
  32  * @run main/othervm DTLSOverDatagram
  33  */
  34 
  35 import java.io.*;
  36 import java.nio.*;
  37 import java.net.*;
  38 import java.util.*;
  39 import java.security.*;
  40 import java.security.cert.*;
  41 import javax.net.ssl.*;
  42 import java.util.concurrent.*;
  43 
  44 import sun.security.util.HexDumpEncoder;
  45 
  46 /**
  47  * An example to show the way to use SSLEngine in datagram connections.
  48  */
  49 public class DTLSOverDatagram {
  50 




  51     private static int MAX_HANDSHAKE_LOOPS = 200;
  52     private static int MAX_APP_READ_LOOPS = 60;
  53     private static int SOCKET_TIMEOUT = 10 * 1000; // in millis
  54     private static int BUFFER_SIZE = 1024;
  55     private static int MAXIMUM_PACKET_SIZE = 1024;
  56 
  57     /*
  58      * The following is to set up the keystores.
  59      */
  60     private static String pathToStores = "../etc";
  61     private static String keyStoreFile = "keystore";
  62     private static String trustStoreFile = "truststore";
  63     private static String passwd = "passphrase";
  64 
  65     private static String keyFilename =
  66             System.getProperty("test.src", ".") + "/" + pathToStores +
  67                 "/" + keyStoreFile;
  68     private static String trustFilename =
  69             System.getProperty("test.src", ".") + "/" + pathToStores +
  70                 "/" + trustStoreFile;


 139 
 140         return engine;
 141     }
 142 
 143     // handshake
 144     void handshake(SSLEngine engine, DatagramSocket socket,
 145             SocketAddress peerAddr, String side) throws Exception {
 146 
 147         boolean endLoops = false;
 148         int loops = MAX_HANDSHAKE_LOOPS;
 149         engine.beginHandshake();
 150         while (!endLoops &&
 151                 (serverException == null) && (clientException == null)) {
 152 
 153             if (--loops < 0) {
 154                 throw new RuntimeException(
 155                         "Too much loops to produce handshake packets");
 156             }
 157 
 158             SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
 159             log(side, "=======handshake(" + loops + ", " + hs + ")=======");
 160             if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
 161                 hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
 162 
 163                 log(side, "Receive DTLS records, handshake status is " + hs);
 164 
 165                 ByteBuffer iNet;
 166                 ByteBuffer iApp;
 167                 if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
 168                     byte[] buf = new byte[BUFFER_SIZE];
 169                     DatagramPacket packet = new DatagramPacket(buf, buf.length);
 170                     try {
 171                         socket.receive(packet);
 172                     } catch (SocketTimeoutException ste) {
 173                         log(side, "Warning: " + ste);
 174 
 175                         List<DatagramPacket> packets = new ArrayList<>();
 176                         boolean finished = onReceiveTimeout(
 177                                 engine, peerAddr, side, packets);
 178 
 179                         for (DatagramPacket p : packets) {


 219                     if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 220                         throw new Exception("Buffer underflow: " +
 221                             "incorrect client maximum fragment size");
 222                     } // otherwise, ignore this packet
 223                 } else if (rs == SSLEngineResult.Status.CLOSED) {
 224                     throw new Exception(
 225                             "SSL engine closed, handshake status is " + hs);
 226                 } else {
 227                     throw new Exception("Can't reach here, result is " + rs);
 228                 }
 229 
 230                 if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
 231                     log(side, "Handshake status is FINISHED, finish the loop");
 232                     endLoops = true;
 233                 }
 234             } else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
 235                 List<DatagramPacket> packets = new ArrayList<>();
 236                 boolean finished = produceHandshakePackets(
 237                     engine, peerAddr, side, packets);
 238 
 239                 log(side, "Produced " + packets.size() + " packets");
 240                 for (DatagramPacket p : packets) {
 241                     socket.send(p);
 242                 }
 243 
 244                 if (finished) {
 245                     log(side, "Handshake status is FINISHED "
 246                             + "after producing handshake packets, "
 247                             + "finish the loop");
 248                     endLoops = true;
 249                 }
 250             } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
 251                 runDelegatedTasks(engine);
 252             } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 253                 log(side,
 254                     "Handshake status is NOT_HANDSHAKING, finish the loop");
 255                 endLoops = true;
 256             } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
 257                 throw new Exception(
 258                         "Unexpected status, SSLEngine.getHandshakeStatus() "
 259                                 + "shouldn't return FINISHED");
 260             } else {
 261                 throw new Exception(
 262                         "Can't reach here, handshake status is " + hs);
 263             }
 264         }
 265 
 266         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
 267         log(side, "Handshake finished, status is " + hs);
 268 
 269         if (engine.getHandshakeSession() != null) {
 270             throw new Exception(
 271                     "Handshake finished, but handshake session is not null");
 272         }
 273 
 274         SSLSession session = engine.getSession();
 275         if (session == null) {
 276             throw new Exception("Handshake finished, but session is null");
 277         }
 278         log(side, "Negotiated protocol is " + session.getProtocol());
 279         log(side, "Negotiated cipher suite is " + session.getCipherSuite());
 280 
 281         // handshake status should be NOT_HANDSHAKING
 282         //
 283         // According to the spec, SSLEngine.getHandshakeStatus() can't
 284         // return FINISHED.
 285         if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 286             throw new Exception("Unexpected handshake status " + hs);
 287         }
 288     }
 289 
 290     // deliver application data
 291     void deliverAppData(SSLEngine engine, DatagramSocket socket,
 292             ByteBuffer appData, SocketAddress peerAddr) throws Exception {
 293 
 294         // Note: have not consider the packet loses
 295         List<DatagramPacket> packets =
 296                 produceApplicationPackets(engine, appData, peerAddr);
 297         appData.flip();
 298         for (DatagramPacket p : packets) {
 299             socket.send(p);
 300         }
 301     }
 302 
 303     // receive application data
 304     void receiveAppData(SSLEngine engine,


 333     boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
 334             String side, List<DatagramPacket> packets) throws Exception {
 335 
 336         boolean endLoops = false;
 337         int loops = MAX_HANDSHAKE_LOOPS;
 338         while (!endLoops &&
 339                 (serverException == null) && (clientException == null)) {
 340 
 341             if (--loops < 0) {
 342                 throw new RuntimeException(
 343                         "Too much loops to produce handshake packets");
 344             }
 345 
 346             ByteBuffer oNet = ByteBuffer.allocate(32768);
 347             ByteBuffer oApp = ByteBuffer.allocate(0);
 348             SSLEngineResult r = engine.wrap(oApp, oNet);
 349             oNet.flip();
 350 
 351             SSLEngineResult.Status rs = r.getStatus();
 352             SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
 353             log(side, "====packet(" + loops + ", " + rs + ", " + hs + ")====");
 354             if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
 355                 // the client maximum fragment size config does not work?
 356                 throw new Exception("Buffer overflow: " +
 357                             "incorrect server maximum fragment size");
 358             } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
 359                 log(side,
 360                         "Produce handshake packets: BUFFER_UNDERFLOW occured");
 361                 log(side,
 362                         "Produce handshake packets: Handshake status: " + hs);
 363                 // bad packet, or the client maximum fragment size
 364                 // config does not work?
 365                 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 366                     throw new Exception("Buffer underflow: " +
 367                             "incorrect server maximum fragment size");
 368                 } // otherwise, ignore this packet
 369             } else if (rs == SSLEngineResult.Status.CLOSED) {
 370                 throw new Exception("SSLEngine has closed");
 371             } else if (rs == SSLEngineResult.Status.OK) {
 372                 // OK
 373             } else {
 374                 throw new Exception("Can't reach here, result is " + rs);
 375             }
 376 
 377             // SSLEngineResult.Status.OK:
 378             if (oNet.hasRemaining()) {
 379                 byte[] ba = new byte[oNet.remaining()];
 380                 oNet.get(ba);
 381                 DatagramPacket packet = createHandshakePacket(ba, socketAddr);
 382                 packets.add(packet);


 441         } else if (rs == SSLEngineResult.Status.CLOSED) {
 442                 throw new Exception("SSLEngine has closed");
 443         } else if (rs == SSLEngineResult.Status.OK) {
 444             // OK
 445         } else {
 446             throw new Exception("Can't reach here, result is " + rs);
 447         }
 448 
 449         // SSLEngineResult.Status.OK:
 450         if (appNet.hasRemaining()) {
 451             byte[] ba = new byte[appNet.remaining()];
 452             appNet.get(ba);
 453             DatagramPacket packet =
 454                     new DatagramPacket(ba, ba.length, socketAddr);
 455             packets.add(packet);
 456         }
 457 
 458         return packets;
 459     }
 460 
 461     // Get a datagram packet for the specified handshake type.
 462     static DatagramPacket getPacket(
 463             List<DatagramPacket> packets, byte handshakeType) {
 464         boolean matched = false;
 465         for (DatagramPacket packet : packets) {
 466             byte[] data = packet.getData();
 467             int offset = packet.getOffset();
 468             int length = packet.getLength();
 469 
 470             // Normally, this pakcet should be a handshake message
 471             // record.  However, even if the underlying platform
 472             // splits the record more, we don't really worry about
 473             // the improper packet loss because DTLS implementation
 474             // should be able to handle packet loss properly.
 475             //
 476             // See RFC 6347 for the detailed format of DTLS records.
 477             if (handshakeType == -1) {      // ChangeCipherSpec
 478                 // Is it a ChangeCipherSpec message?
 479                 matched = (length == 14) && (data[offset] == 0x14);
 480             } else if ((length >= 25) &&    // 25: handshake mini size
 481                 (data[offset] == 0x16)) {   // a handshake message
 482 
 483                 // check epoch number for initial handshake only
 484                 if (data[offset + 3] == 0x00) {     // 3,4: epoch
 485                     if (data[offset + 4] == 0x00) { // plaintext
 486                         matched =
 487                             (data[offset + 13] == handshakeType);
 488                     } else {                        // cipherext
 489                         // The 1st ciphertext is a Finished message.
 490                         //
 491                         // If it is not proposed to loss the Finished
 492                         // message, it is not necessary to check the
 493                         // following packets any mroe as a Finished
 494                         // message is the last handshake message.
 495                         matched = (handshakeType == 20);
 496                     }
 497                 }
 498             }
 499 
 500             if (matched) {
 501                 return packet;
 502             }
 503         }
 504 
 505         return null;
 506     }
 507 
 508     // run delegated tasks
 509     void runDelegatedTasks(SSLEngine engine) throws Exception {
 510         Runnable runnable;
 511         while ((runnable = engine.getDelegatedTask()) != null) {
 512             runnable.run();
 513         }
 514 
 515         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
 516         if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
 517             throw new Exception("handshake shouldn't need additional tasks");
 518         }
 519     }
 520 
 521     // retransmission if timeout
 522     boolean onReceiveTimeout(SSLEngine engine, SocketAddress socketAddr,
 523             String side, List<DatagramPacket> packets) throws Exception {
 524 
 525         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
 526         if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
 527             return false;