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