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