1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8044860
  27  * @summary Vectors and fixed length fields should be verified
  28  *          for allowed sizes.
  29  * @run main/othervm LengthCheckTest
  30  */
  31 
  32 /**
  33  * A SSLEngine usage example which simplifies the presentation
  34  * by removing the I/O and multi-threading concerns.
  35  *
  36  * The test creates two SSLEngines, simulating a client and server.
  37  * The "transport" layer consists two byte buffers:  think of them
  38  * as directly connected pipes.
  39  *
  40  * Note, this is a *very* simple example: real code will be much more
  41  * involved.  For example, different threading and I/O models could be
  42  * used, transport mechanisms could close unexpectedly, and so on.
  43  *
  44  * When this application runs, notice that several messages
  45  * (wrap/unwrap) pass before any application data is consumed or
  46  * produced.  (For more information, please see the SSL/TLS
  47  * specifications.)  There may several steps for a successful handshake,
  48  * so it's typical to see the following series of operations:
  49  *
  50  *      client          server          message
  51  *      ======          ======          =======
  52  *      wrap()          ...             ClientHello
  53  *      ...             unwrap()        ClientHello
  54  *      ...             wrap()          ServerHello/Certificate
  55  *      unwrap()        ...             ServerHello/Certificate
  56  *      wrap()          ...             ClientKeyExchange
  57  *      wrap()          ...             ChangeCipherSpec
  58  *      wrap()          ...             Finished
  59  *      ...             unwrap()        ClientKeyExchange
  60  *      ...             unwrap()        ChangeCipherSpec
  61  *      ...             unwrap()        Finished
  62  *      ...             wrap()          ChangeCipherSpec
  63  *      ...             wrap()          Finished
  64  *      unwrap()        ...             ChangeCipherSpec
  65  *      unwrap()        ...             Finished
  66  */
  67 
  68 import javax.net.ssl.*;
  69 import javax.net.ssl.SSLEngineResult.*;
  70 import java.io.*;
  71 import java.security.*;
  72 import java.nio.*;
  73 import java.util.List;
  74 import java.util.ArrayList;
  75 import sun.security.ssl.ProtocolVersion;
  76 
  77 public class LengthCheckTest {
  78 
  79     /*
  80      * Enables logging of the SSLEngine operations.
  81      */
  82     private static final boolean logging = true;
  83 
  84     /*
  85      * Enables the JSSE system debugging system property:
  86      *
  87      *     -Djavax.net.debug=all
  88      *
  89      * This gives a lot of low-level information about operations underway,
  90      * including specific handshake messages, and might be best examined
  91      * after gaining some familiarity with this application.
  92      */
  93     private static final boolean debug = false;
  94     private static final boolean dumpBufs = true;
  95 
  96     private final SSLContext sslc;
  97 
  98     private SSLEngine clientEngine;     // client Engine
  99     private ByteBuffer clientOut;       // write side of clientEngine
 100     private ByteBuffer clientIn;        // read side of clientEngine
 101 
 102     private SSLEngine serverEngine;     // server Engine
 103     private ByteBuffer serverOut;       // write side of serverEngine
 104     private ByteBuffer serverIn;        // read side of serverEngine
 105 
 106     private HandshakeTest handshakeTest;
 107 
 108     /*
 109      * For data transport, this example uses local ByteBuffers.  This
 110      * isn't really useful, but the purpose of this example is to show
 111      * SSLEngine concepts, not how to do network transport.
 112      */
 113     private ByteBuffer cTOs;            // "reliable" transport client->server
 114     private ByteBuffer sTOc;            // "reliable" transport server->client
 115 
 116     /*
 117      * The following is to set up the keystores.
 118      */
 119     private static final String pathToStores = "../../../../javax/net/ssl/etc";
 120     private static final String keyStoreFile = "keystore";
 121     private static final String trustStoreFile = "truststore";
 122     private static final String passwd = "passphrase";
 123 
 124     private static final String keyFilename =
 125             System.getProperty("test.src", ".") + "/" + pathToStores +
 126                 "/" + keyStoreFile;
 127     private static final String trustFilename =
 128             System.getProperty("test.src", ".") + "/" + pathToStores +
 129                 "/" + trustStoreFile;
 130 
 131     // Define a few basic TLS record and message types we might need
 132     private static final int TLS_RECTYPE_CCS = 0x14;
 133     private static final int TLS_RECTYPE_ALERT = 0x15;
 134     private static final int TLS_RECTYPE_HANDSHAKE = 0x16;
 135     private static final int TLS_RECTYPE_APPDATA = 0x17;
 136 
 137     private static final int TLS_HS_HELLO_REQUEST = 0x00;
 138     private static final int TLS_HS_CLIENT_HELLO = 0x01;
 139     private static final int TLS_HS_SERVER_HELLO = 0x02;
 140     private static final int TLS_HS_CERTIFICATE = 0x0B;
 141     private static final int TLS_HS_SERVER_KEY_EXCHG = 0x0C;
 142     private static final int TLS_HS_CERT_REQUEST = 0x0D;
 143     private static final int TLS_HS_SERVER_HELLO_DONE = 0x0E;
 144     private static final int TLS_HS_CERT_VERIFY = 0x0F;
 145     private static final int TLS_HS_CLIENT_KEY_EXCHG = 0x10;
 146     private static final int TLS_HS_FINISHED = 0x14;
 147 
 148     // We're not going to define all the alert types in TLS, just
 149     // the ones we think we'll need to reference by name.
 150     private static final int TLS_ALERT_LVL_WARNING = 0x01;
 151     private static final int TLS_ALERT_LVL_FATAL = 0x02;
 152 
 153     private static final int TLS_ALERT_UNEXPECTED_MSG = 0x0A;
 154     private static final int TLS_ALERT_HANDSHAKE_FAILURE = 0x28;
 155     private static final int TLS_ALERT_INTERNAL_ERROR = 0x50;
 156 
 157     public interface HandshakeTest {
 158         void execTest() throws Exception;
 159     }
 160 
 161     public final HandshakeTest servSendLongID = new HandshakeTest() {
 162         @Override
 163         public void execTest() throws Exception {
 164             boolean gotException = false;
 165             SSLEngineResult clientResult;   // results from client's last op
 166             SSLEngineResult serverResult;   // results from server's last op
 167 
 168             log("\n==== Test: Client receives 64-byte session ID ====");
 169 
 170             // Send Client Hello
 171             clientResult = clientEngine.wrap(clientOut, cTOs);
 172             log("client wrap: ", clientResult);
 173             runDelegatedTasks(clientResult, clientEngine);
 174             cTOs.flip();
 175             dumpByteBuffer("CLIENT-TO-SERVER", cTOs);
 176 
 177             // Server consumes Client Hello
 178             serverResult = serverEngine.unwrap(cTOs, serverIn);
 179             log("server unwrap: ", serverResult);
 180             runDelegatedTasks(serverResult, serverEngine);
 181             cTOs.compact();
 182 
 183             // Server generates ServerHello/Cert/Done record
 184             serverResult = serverEngine.wrap(serverOut, sTOc);
 185             log("server wrap: ", serverResult);
 186             runDelegatedTasks(serverResult, serverEngine);
 187             sTOc.flip();
 188 
 189             // Intercept the ServerHello messages and instead send
 190             // one that has a 64-byte session ID.
 191             if (isTlsMessage(sTOc, TLS_RECTYPE_HANDSHAKE,
 192                         TLS_HS_SERVER_HELLO)) {
 193                 ArrayList<ByteBuffer> recList = splitRecord(sTOc);
 194 
 195                 // Use the original ServerHello as a template to craft one
 196                 // with a longer-than-allowed session ID.
 197                 ByteBuffer servHelloBuf =
 198                         createEvilServerHello(recList.get(0), 64);
 199 
 200                 recList.set(0, servHelloBuf);
 201 
 202                 // Now send each ByteBuffer (each being a complete
 203                 // TLS record) into the client-side unwrap.
 204                 for (ByteBuffer bBuf : recList) {
 205                     dumpByteBuffer("SERVER-TO-CLIENT", bBuf);
 206                     try {
 207                         clientResult = clientEngine.unwrap(bBuf, clientIn);
 208                     } catch (SSLProtocolException e) {
 209                         log("Received expected SSLProtocolException: " + e);
 210                         gotException = true;
 211                     }
 212                     log("client unwrap: ", clientResult);
 213                     runDelegatedTasks(clientResult, clientEngine);
 214                 }
 215             } else {
 216                 dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
 217                 log("client unwrap: ", clientResult);
 218                 runDelegatedTasks(clientResult, clientEngine);
 219             }
 220             sTOc.compact();
 221 
 222             // The Client should now send a TLS Alert
 223             clientResult = clientEngine.wrap(clientOut, cTOs);
 224             log("client wrap: ", clientResult);
 225             runDelegatedTasks(clientResult, clientEngine);
 226             cTOs.flip();
 227             dumpByteBuffer("CLIENT-TO-SERVER", cTOs);
 228 
 229             // At this point we can verify that both an exception
 230             // was thrown and the proper action (a TLS alert) was
 231             // sent back to the server.
 232             if (gotException == false ||
 233                 !isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
 234                         TLS_ALERT_INTERNAL_ERROR)) {
 235                 throw new SSLException(
 236                     "Client failed to throw Alert:fatal:internal_error");
 237             }
 238         }
 239     };
 240 
 241     public final HandshakeTest clientSendLongID = new HandshakeTest() {
 242         @Override
 243         public void execTest() throws Exception {
 244             boolean gotException = false;
 245             SSLEngineResult clientResult;   // results from client's last op
 246             SSLEngineResult serverResult;   // results from server's last op
 247 
 248             log("\n==== Test: Server receives 64-byte session ID ====");
 249 
 250             // Send Client Hello
 251             ByteBuffer evilClientHello = createEvilClientHello(64);
 252             dumpByteBuffer("CLIENT-TO-SERVER", evilClientHello);
 253 
 254             try {
 255                 // Server consumes Client Hello
 256                 serverResult = serverEngine.unwrap(evilClientHello, serverIn);
 257                 log("server unwrap: ", serverResult);
 258                 runDelegatedTasks(serverResult, serverEngine);
 259                 evilClientHello.compact();
 260 
 261                 // Under normal circumstances this should be a ServerHello
 262                 // But should throw an exception instead due to the invalid
 263                 // session ID.
 264                 serverResult = serverEngine.wrap(serverOut, sTOc);
 265                 log("server wrap: ", serverResult);
 266                 runDelegatedTasks(serverResult, serverEngine);
 267                 sTOc.flip();
 268                 dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
 269             } catch (SSLProtocolException ssle) {
 270                 log("Received expected SSLProtocolException: " + ssle);
 271                 gotException = true;
 272             }
 273 
 274             // We expect to see the server generate an alert here
 275             serverResult = serverEngine.wrap(serverOut, sTOc);
 276             log("server wrap: ", serverResult);
 277             runDelegatedTasks(serverResult, serverEngine);
 278             sTOc.flip();
 279             dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
 280 
 281             // At this point we can verify that both an exception
 282             // was thrown and the proper action (a TLS alert) was
 283             // sent back to the client.
 284             if (gotException == false ||
 285                 !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
 286                         TLS_ALERT_INTERNAL_ERROR)) {
 287                 throw new SSLException(
 288                     "Server failed to throw Alert:fatal:internal_error");
 289             }
 290         }
 291     };
 292 
 293 
 294     /*
 295      * Main entry point for this test.
 296      */
 297     public static void main(String args[]) throws Exception {
 298         List<LengthCheckTest> ccsTests = new ArrayList<>();
 299 
 300         if (debug) {
 301             System.setProperty("javax.net.debug", "ssl");
 302         }
 303 
 304         ccsTests.add(new LengthCheckTest("ServSendLongID"));
 305         ccsTests.add(new LengthCheckTest("ClientSendLongID"));
 306 
 307         for (LengthCheckTest test : ccsTests) {
 308             test.runTest();
 309         }
 310 
 311         System.out.println("Test Passed.");
 312     }
 313 
 314     /*
 315      * Create an initialized SSLContext to use for these tests.
 316      */
 317     public LengthCheckTest(String testName) throws Exception {
 318 
 319         KeyStore ks = KeyStore.getInstance("JKS");
 320         KeyStore ts = KeyStore.getInstance("JKS");
 321 
 322         char[] passphrase = "passphrase".toCharArray();
 323 
 324         ks.load(new FileInputStream(keyFilename), passphrase);
 325         ts.load(new FileInputStream(trustFilename), passphrase);
 326 
 327         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 328         kmf.init(ks, passphrase);
 329 
 330         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
 331         tmf.init(ts);
 332 
 333         SSLContext sslCtx = SSLContext.getInstance("TLS");
 334 
 335         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 336 
 337         sslc = sslCtx;
 338 
 339         switch (testName) {
 340             case "ServSendLongID":
 341                 handshakeTest = servSendLongID;
 342                 break;
 343             case "ClientSendLongID":
 344                 handshakeTest = clientSendLongID;
 345                 break;
 346             default:
 347                 throw new IllegalArgumentException("Unknown test name: " +
 348                         testName);
 349         }
 350     }
 351 
 352     /*
 353      * Run the test.
 354      *
 355      * Sit in a tight loop, both engines calling wrap/unwrap regardless
 356      * of whether data is available or not.  We do this until both engines
 357      * report back they are closed.
 358      *
 359      * The main loop handles all of the I/O phases of the SSLEngine's
 360      * lifetime:
 361      *
 362      *     initial handshaking
 363      *     application data transfer
 364      *     engine closing
 365      *
 366      * One could easily separate these phases into separate
 367      * sections of code.
 368      */
 369     private void runTest() throws Exception {
 370         boolean dataDone = false;
 371 
 372         createSSLEngines();
 373         createBuffers();
 374 
 375         handshakeTest.execTest();
 376     }
 377 
 378     /*
 379      * Using the SSLContext created during object creation,
 380      * create/configure the SSLEngines we'll use for this test.
 381      */
 382     private void createSSLEngines() throws Exception {
 383         /*
 384          * Configure the serverEngine to act as a server in the SSL/TLS
 385          * handshake.  Also, require SSL client authentication.
 386          */
 387         serverEngine = sslc.createSSLEngine();
 388         serverEngine.setUseClientMode(false);
 389         serverEngine.setNeedClientAuth(false);
 390 
 391         /*
 392          * Similar to above, but using client mode instead.
 393          */
 394         clientEngine = sslc.createSSLEngine("client", 80);
 395         clientEngine.setUseClientMode(true);
 396 
 397         // In order to make a test that will be backwards compatible
 398         // going back to JDK 5, force the handshake to be TLS 1.0 and
 399         // use one of the older cipher suites.
 400         clientEngine.setEnabledProtocols(new String[]{"TLSv1"});
 401         clientEngine.setEnabledCipherSuites(
 402                 new String[]{"TLS_RSA_WITH_AES_128_CBC_SHA"});
 403     }
 404 
 405     /*
 406      * Create and size the buffers appropriately.
 407      */
 408     private void createBuffers() {
 409 
 410         /*
 411          * We'll assume the buffer sizes are the same
 412          * between client and server.
 413          */
 414         SSLSession session = clientEngine.getSession();
 415         int appBufferMax = session.getApplicationBufferSize();
 416         int netBufferMax = session.getPacketBufferSize();
 417 
 418         /*
 419          * We'll make the input buffers a bit bigger than the max needed
 420          * size, so that unwrap()s following a successful data transfer
 421          * won't generate BUFFER_OVERFLOWS.
 422          *
 423          * We'll use a mix of direct and indirect ByteBuffers for
 424          * tutorial purposes only.  In reality, only use direct
 425          * ByteBuffers when they give a clear performance enhancement.
 426          */
 427         clientIn = ByteBuffer.allocate(appBufferMax + 50);
 428         serverIn = ByteBuffer.allocate(appBufferMax + 50);
 429 
 430         cTOs = ByteBuffer.allocateDirect(netBufferMax);
 431         sTOc = ByteBuffer.allocateDirect(netBufferMax);
 432 
 433         clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
 434         serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
 435     }
 436 
 437     /*
 438      * If the result indicates that we have outstanding tasks to do,
 439      * go ahead and run them in this thread.
 440      */
 441     private static void runDelegatedTasks(SSLEngineResult result,
 442             SSLEngine engine) throws Exception {
 443 
 444         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
 445             Runnable runnable;
 446             while ((runnable = engine.getDelegatedTask()) != null) {
 447                 log("\trunning delegated task...");
 448                 runnable.run();
 449             }
 450             HandshakeStatus hsStatus = engine.getHandshakeStatus();
 451             if (hsStatus == HandshakeStatus.NEED_TASK) {
 452                 throw new Exception(
 453                     "handshake shouldn't need additional tasks");
 454             }
 455             log("\tnew HandshakeStatus: " + hsStatus);
 456         }
 457     }
 458 
 459     private static boolean isEngineClosed(SSLEngine engine) {
 460         return (engine.isOutboundDone() && engine.isInboundDone());
 461     }
 462 
 463     /*
 464      * Simple check to make sure everything came across as expected.
 465      */
 466     private static void checkTransfer(ByteBuffer a, ByteBuffer b)
 467             throws Exception {
 468         a.flip();
 469         b.flip();
 470 
 471         if (!a.equals(b)) {
 472             throw new Exception("Data didn't transfer cleanly");
 473         } else {
 474             log("\tData transferred cleanly");
 475         }
 476 
 477         a.position(a.limit());
 478         b.position(b.limit());
 479         a.limit(a.capacity());
 480         b.limit(b.capacity());
 481     }
 482 
 483     /*
 484      * Logging code
 485      */
 486     private static boolean resultOnce = true;
 487 
 488     private static void log(String str, SSLEngineResult result) {
 489         if (!logging) {
 490             return;
 491         }
 492         if (resultOnce) {
 493             resultOnce = false;
 494             System.out.println("The format of the SSLEngineResult is: \n" +
 495                 "\t\"getStatus() / getHandshakeStatus()\" +\n" +
 496                 "\t\"bytesConsumed() / bytesProduced()\"\n");
 497         }
 498         HandshakeStatus hsStatus = result.getHandshakeStatus();
 499         log(str +
 500             result.getStatus() + "/" + hsStatus + ", " +
 501             result.bytesConsumed() + "/" + result.bytesProduced() +
 502             " bytes");
 503         if (hsStatus == HandshakeStatus.FINISHED) {
 504             log("\t...ready for application data");
 505         }
 506     }
 507 
 508     private static void log(String str) {
 509         if (logging) {
 510             System.out.println(str);
 511         }
 512     }
 513 
 514     /**
 515      * Split a record consisting of multiple TLS handshake messages
 516      * into individual TLS records, each one in a ByteBuffer of its own.
 517      *
 518      * @param tlsRecord A ByteBuffer containing the tls record data.
 519      *        The position of the buffer should be at the first byte
 520      *        in the TLS record data.
 521      *
 522      * @return An ArrayList consisting of one or more ByteBuffers.  Each
 523      *         ByteBuffer will contain a single TLS record with one message.
 524      *         That message will be taken from the input record.  The order
 525      *         of the messages in the ArrayList will be the same as they
 526      *         were in the input record.
 527      */
 528     private ArrayList<ByteBuffer> splitRecord(ByteBuffer tlsRecord) {
 529         SSLSession session = clientEngine.getSession();
 530         int netBufferMax = session.getPacketBufferSize();
 531         ArrayList<ByteBuffer> recordList = new ArrayList<>();
 532 
 533         if (tlsRecord.hasRemaining()) {
 534             int type = Byte.toUnsignedInt(tlsRecord.get());
 535             byte ver_major = tlsRecord.get();
 536             byte ver_minor = tlsRecord.get();
 537             int recLen = Short.toUnsignedInt(tlsRecord.getShort());
 538             byte[] newMsgData = null;
 539             while (tlsRecord.hasRemaining()) {
 540                 ByteBuffer newRecord = ByteBuffer.allocateDirect(netBufferMax);
 541                 switch (type) {
 542                     case TLS_RECTYPE_CCS:
 543                     case TLS_RECTYPE_ALERT:
 544                     case TLS_RECTYPE_APPDATA:
 545                         // None of our tests have multiple non-handshake
 546                         // messages coalesced into a single record.
 547                         break;
 548                     case TLS_RECTYPE_HANDSHAKE:
 549                         newMsgData = getHandshakeMessage(tlsRecord);
 550                         break;
 551                 }
 552 
 553                 // Put a new TLS record on the destination ByteBuffer
 554                 newRecord.put((byte)type);
 555                 newRecord.put(ver_major);
 556                 newRecord.put(ver_minor);
 557                 newRecord.putShort((short)newMsgData.length);
 558 
 559                 // Now add the message content itself and attach to the
 560                 // returned ArrayList
 561                 newRecord.put(newMsgData);
 562                 newRecord.flip();
 563                 recordList.add(newRecord);
 564             }
 565         }
 566 
 567         return recordList;
 568     }
 569 
 570     private static ByteBuffer createEvilClientHello(int sessIdLen) {
 571         ByteBuffer newRecord = ByteBuffer.allocateDirect(4096);
 572 
 573         // Lengths will initially be place holders until we determine the
 574         // finished length of the ByteBuffer.  Then we'll go back and scribble
 575         // in the correct lengths.
 576 
 577         newRecord.put((byte)TLS_RECTYPE_HANDSHAKE);     // Record type
 578         newRecord.putShort((short)0x0301);              // Protocol (TLS 1.0)
 579         newRecord.putShort((short)0);                   // Length place holder
 580 
 581         newRecord.putInt(TLS_HS_CLIENT_HELLO << 24);    // HS type and length
 582         newRecord.putShort((short)0x0301);
 583         newRecord.putInt((int)(System.currentTimeMillis() / 1000));
 584         SecureRandom sr = new SecureRandom();
 585         byte[] randBuf = new byte[28];
 586         sr.nextBytes(randBuf);
 587         newRecord.put(randBuf);                         // Client Random
 588         newRecord.put((byte)sessIdLen);                 // Session ID length
 589         if (sessIdLen > 0) {
 590             byte[] sessId = new byte[sessIdLen];
 591             sr.nextBytes(sessId);
 592             newRecord.put(sessId);                      // Session ID
 593         }
 594         newRecord.putShort((short)2);                   // 2 bytes of ciphers
 595         newRecord.putShort((short)0x002F);              // TLS_RSA_AES_CBC_SHA
 596         newRecord.putShort((short)0x0100);              // only null compression
 597         newRecord.putShort((short)5);                   // 5 bytes of extensions
 598         newRecord.putShort((short)0xFF01);              // Renegotiation info
 599         newRecord.putShort((short)1);
 600         newRecord.put((byte)0);                         // No reneg info exts
 601 
 602         // Go back and fill in the correct length values for the record
 603         // and handshake message headers.
 604         int recordLength = newRecord.position();
 605         newRecord.putShort(3, (short)(recordLength - 5));
 606         int newTypeAndLen = (newRecord.getInt(5) & 0xFF000000) |
 607                 ((recordLength - 9) & 0x00FFFFFF);
 608         newRecord.putInt(5, newTypeAndLen);
 609 
 610         newRecord.flip();
 611         return newRecord;
 612     }
 613 
 614     private static ByteBuffer createEvilServerHello(ByteBuffer origHello,
 615             int newSessIdLen) {
 616         if (newSessIdLen < 0 || newSessIdLen > Byte.MAX_VALUE) {
 617             throw new RuntimeException("Length must be 0 <= X <= 127");
 618         }
 619 
 620         ByteBuffer newRecord = ByteBuffer.allocateDirect(4096);
 621         // Copy the bytes from the old hello to the new up to the session ID
 622         // field.  We will go back later and fill in a new length field in
 623         // the record header.  This includes the record header (5 bytes), the
 624         // Handshake message header (4 bytes), protocol version (2 bytes),
 625         // and the random (32 bytes).
 626         ByteBuffer scratchBuffer = origHello.slice();
 627         scratchBuffer.limit(43);
 628         newRecord.put(scratchBuffer);
 629 
 630         // Advance the position in the originial hello buffer past the
 631         // session ID.
 632         origHello.position(43);
 633         int origIDLen = Byte.toUnsignedInt(origHello.get());
 634         if (origIDLen > 0) {
 635             // Skip over the session ID
 636             origHello.position(origHello.position() + origIDLen);
 637         }
 638 
 639         // Now add our own sessionID to the new record
 640         SecureRandom sr = new SecureRandom();
 641         byte[] sessId = new byte[newSessIdLen];
 642         sr.nextBytes(sessId);
 643         newRecord.put((byte)newSessIdLen);
 644         newRecord.put(sessId);
 645 
 646         // Create another slice in the original buffer, based on the position
 647         // past the session ID.  Copy the remaining bytes into the new
 648         // hello buffer.  Then go back and fix up the length
 649         newRecord.put(origHello.slice());
 650 
 651         // Go back and fill in the correct length values for the record
 652         // and handshake message headers.
 653         int recordLength = newRecord.position();
 654         newRecord.putShort(3, (short)(recordLength - 5));
 655         int newTypeAndLen = (newRecord.getInt(5) & 0xFF000000) |
 656                 ((recordLength - 9) & 0x00FFFFFF);
 657         newRecord.putInt(5, newTypeAndLen);
 658 
 659         newRecord.flip();
 660         return newRecord;
 661     }
 662 
 663     /**
 664      * Look at an incoming TLS record and see if it is the desired
 665      * record type, and where appropriate the correct subtype.
 666      *
 667      * @param srcRecord The input TLS record to be evaluated.  This
 668      *        method will only look at the leading message if multiple
 669      *        TLS handshake messages are coalesced into a single record.
 670      * @param reqRecType The requested TLS record type
 671      * @param recParams Zero or more integer sub type fields.  For CCS
 672      *        and ApplicationData, no params are used.  For handshake records,
 673      *        one value corresponding to the HandshakeType is required.
 674      *        For Alerts, two values corresponding to AlertLevel and
 675      *        AlertDescription are necessary.
 676      *
 677      * @return true if the proper handshake message is the first one
 678      *         in the input record, false otherwise.
 679      */
 680     private boolean isTlsMessage(ByteBuffer srcRecord, int reqRecType,
 681             int... recParams) {
 682         boolean foundMsg = false;
 683 
 684         if (srcRecord.hasRemaining()) {
 685             srcRecord.mark();
 686 
 687             // Grab the fields from the TLS Record
 688             int recordType = Byte.toUnsignedInt(srcRecord.get());
 689             byte ver_major = srcRecord.get();
 690             byte ver_minor = srcRecord.get();
 691             int recLen = Short.toUnsignedInt(srcRecord.getShort());
 692 
 693             if (recordType == reqRecType) {
 694                 // For any zero-length recParams, making sure the requested
 695                 // type is sufficient.
 696                 if (recParams.length == 0) {
 697                     foundMsg = true;
 698                 } else {
 699                     switch (recordType) {
 700                         case TLS_RECTYPE_CCS:
 701                         case TLS_RECTYPE_APPDATA:
 702                             // We really shouldn't find ourselves here, but
 703                             // if someone asked for these types and had more
 704                             // recParams we can ignore them.
 705                             foundMsg = true;
 706                             break;
 707                         case TLS_RECTYPE_ALERT:
 708                             // Needs two params, AlertLevel and AlertDescription
 709                             if (recParams.length != 2) {
 710                                 throw new RuntimeException(
 711                                     "Test for Alert requires level and desc.");
 712                             } else {
 713                                 int level = Byte.toUnsignedInt(srcRecord.get());
 714                                 int desc = Byte.toUnsignedInt(srcRecord.get());
 715                                 if (level == recParams[0] &&
 716                                         desc == recParams[1]) {
 717                                     foundMsg = true;
 718                                 }
 719                             }
 720                             break;
 721                         case TLS_RECTYPE_HANDSHAKE:
 722                             // Needs one parameter, HandshakeType
 723                             if (recParams.length != 1) {
 724                                 throw new RuntimeException(
 725                                     "Test for Handshake requires only HS type");
 726                             } else {
 727                                 // Go into the first handhshake message in the
 728                                 // record and grab the handshake message header.
 729                                 // All we need to do is parse out the leading
 730                                 // byte.
 731                                 int msgHdr = srcRecord.getInt();
 732                                 int msgType = (msgHdr >> 24) & 0x000000FF;
 733                                 if (msgType == recParams[0]) {
 734                                 foundMsg = true;
 735                             }
 736                         }
 737                         break;
 738                     }
 739                 }
 740             }
 741 
 742             srcRecord.reset();
 743         }
 744 
 745         return foundMsg;
 746     }
 747 
 748     private byte[] getHandshakeMessage(ByteBuffer srcRecord) {
 749         // At the start of this routine, the position should be lined up
 750         // at the first byte of a handshake message.  Mark this location
 751         // so we can return to it after reading the type and length.
 752         srcRecord.mark();
 753         int msgHdr = srcRecord.getInt();
 754         int type = (msgHdr >> 24) & 0x000000FF;
 755         int length = msgHdr & 0x00FFFFFF;
 756 
 757         // Create a byte array that has enough space for the handshake
 758         // message header and body.
 759         byte[] data = new byte[length + 4];
 760         srcRecord.reset();
 761         srcRecord.get(data, 0, length + 4);
 762 
 763         return (data);
 764     }
 765 
 766     /**
 767      * Hex-dumps a ByteBuffer to stdout.
 768      */
 769     private static void dumpByteBuffer(String header, ByteBuffer bBuf) {
 770         if (dumpBufs == false) {
 771             return;
 772         }
 773 
 774         int bufLen = bBuf.remaining();
 775         if (bufLen > 0) {
 776             bBuf.mark();
 777 
 778             // We expect the position of the buffer to be at the
 779             // beginning of a TLS record.  Get the type, version and length.
 780             int type = Byte.toUnsignedInt(bBuf.get());
 781             int ver_major = Byte.toUnsignedInt(bBuf.get());
 782             int ver_minor = Byte.toUnsignedInt(bBuf.get());
 783             int recLen = Short.toUnsignedInt(bBuf.getShort());
 784             ProtocolVersion pv = ProtocolVersion.valueOf(ver_major, ver_minor);
 785 
 786             log("===== " + header + " (" + tlsRecType(type) + " / " +
 787                 pv + " / " + bufLen + " bytes) =====");
 788             bBuf.reset();
 789             for (int i = 0; i < bufLen; i++) {
 790                 if (i != 0 && i % 16 == 0) {
 791                     System.out.print("\n");
 792                 }
 793                 System.out.format("%02X ", bBuf.get(i));
 794             }
 795             log("\n===============================================");
 796             bBuf.reset();
 797         }
 798     }
 799 
 800     private static String tlsRecType(int type) {
 801         switch (type) {
 802             case 20:
 803                 return "Change Cipher Spec";
 804             case 21:
 805                 return "Alert";
 806             case 22:
 807                 return "Handshake";
 808             case 23:
 809                 return "Application Data";
 810             default:
 811                 return ("Unknown (" + type + ")");
 812         }
 813     }
 814 }