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