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