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