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