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

Print this page
8167680 DTLS implementation bugs

*** 46,59 **** /** * 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; --- 46,55 ----
*** 158,167 **** --- 154,164 ---- 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,246 **** --- 234,244 ---- } 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,267 **** 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"); 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); } } SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); log(side, "Handshake finished, status is " + hs); --- 248,267 ---- 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"); 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); } } SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus(); log(side, "Handshake finished, status is " + hs);
*** 277,287 **** } 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 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { throw new Exception("Unexpected handshake status " + hs); } } --- 277,289 ---- } 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. if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { throw new Exception("Unexpected handshake status " + hs); } }
*** 346,362 **** SSLEngineResult r = engine.wrap(oApp, oNet); oNet.flip(); SSLEngineResult.Status rs = r.getStatus(); SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus(); 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); // 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"); --- 348,367 ---- 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); // 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,460 **** --- 456,512 ---- } 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();