8210918: Add test to exercise server-side client hello processing
authorjnimeh
Mon, 24 Sep 2018 00:13:02 -0700
changeset 51844 354fb27fd38a
parent 51843 16f0deae8fa6
child 51845 f5daffd7ec7a
8210918: Add test to exercise server-side client hello processing Reviewed-by: xuelei
test/jdk/javax/net/ssl/compatibility/ClientHelloProcessing.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/net/ssl/compatibility/ClientHelloProcessing.java	Mon Sep 24 00:13:02 2018 -0700
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8210918 8210334 8209916
+ * @summary Add test to exercise server-side client hello processing
+ * @run main/othervm ClientHelloProcessing noPskNoKexModes
+ * @run main/othervm ClientHelloProcessing noPskYesKexModes
+ * @run main/othervm ClientHelloProcessing yesPskNoKexModes
+ * @run main/othervm ClientHelloProcessing yesPskYesKexModes
+ * @run main/othervm ClientHelloProcessing supGroupsSect163k1
+ */
+
+/*
+ * SunJSSE does not support dynamic system properties, no way to re-use
+ * system properties in samevm/agentvm mode.
+ */
+
+import java.io.FileInputStream;
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Objects;
+
+/*
+ * If you wish to add test cases, the following must be done:
+ * 1. Add a @run line with the parameter being a name for the test case
+ * 2. Create the ClientHello as a byte[].  It should be a complete TLS
+ *    record, but does not need upper layer headers like TCP, IP, Ethernet, etc.
+ * 3. Create a new TestCase instance, see "noPskNoKexModes" as an example
+ * 4. Create a mapping between the test case name in your @run line and the
+ *    TestCase object you created in step #3.  Add this to TESTMAP.
+ */
+
+public class ClientHelloProcessing {
+
+    private static final ByteBuffer SERVOUTBUF =
+            ByteBuffer.wrap("Server Side".getBytes());
+
+    private static final String pathToStores = "../etc";
+    private static final String keyStoreFile = "keystore";
+    private static final String trustStoreFile = "truststore";
+    private static final String passwd = "passphrase";
+
+    private static final String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + keyStoreFile;
+    private static final String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+    private static TrustManagerFactory trustMgrFac = null;
+    private static KeyManagerFactory keyMgrFac = null;
+
+    // Canned client hello messages
+    // These were created from packet captures using openssl 1.1.1's
+    // s_client utility.  The captured TLS record containing the client
+    // hello was then manually edited to remove or add fields if the s_client
+    // utility could not be used to generate a message with the desired
+    // extensions.  When manually altering the hello messages, care must
+    // be taken to change the lengths of the extensions themselves, the
+    // extensions vector length, the handshake message length, and the TLS
+    // record length.
+
+    // Client Hello with the pre_shared_key and psk_key_exchange_modes
+    // both absent.  Required manual removal of the psk_key_exchange_modes
+    // extension.  Similarly formed Client Hello messages may be generated
+    // by clients that don't support pre-shared keys.
+    //
+    //    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
+    //        Content Type: Handshake (22)
+    //        Version: TLS 1.0 (0x0301)
+    //        Length: 256
+    //        Handshake Protocol: Client Hello
+    //            Handshake Type: Client Hello (1)
+    //            Length: 252
+    //            Version: TLS 1.2 (0x0303)
+    //            Random: 9b796ad0cbd559fb48fc4ba32da5bb8c1ef9a7da85231860...
+    //            Session ID Length: 32
+    //            Session ID: fe8411205bc99a506952f5c28569facb96ff0f37621be072...
+    //            Cipher Suites Length: 8
+    //            Cipher Suites (4 suites)
+    //            Compression Methods Length: 1
+    //            Compression Methods (1 method)
+    //            Extensions Length: 171
+    //            Extension: server_name (len=14)
+    //            Extension: ec_point_formats (len=4)
+    //            Extension: supported_groups (len=4)
+    //            Extension: SessionTicket TLS (len=0)
+    //            Extension: status_request (len=5)
+    //            Extension: encrypt_then_mac (len=0)
+    //            Extension: extended_master_secret (len=0)
+    //            Extension: signature_algorithms (len=30)
+    //            Extension: supported_versions (len=3)
+    //            Extension: key_share (len=71)
+    private static final byte[] CLIHELLO_NOPSK_NOPSKEXMODE = {
+        22,    3,    1,    1,    0,    1,    0,    0,
+        -4,    3,    3, -101,  121,  106,  -48,  -53,
+       -43,   89,   -5,   72,   -4,   75,  -93,   45,
+       -91,  -69, -116,   30,   -7,  -89,  -38, -123,
+        35,   24,   96,   29,  -93,  -22,   10,  -97,
+       -15,  -11,    3,   32,   -2, -124,   17,   32,
+        91,  -55, -102,   80,  105,   82,  -11,  -62,
+      -123,  105,   -6,  -53, -106,   -1,   15,   55,
+        98,   27,  -32,  114, -126,  -13,   42, -104,
+      -102,   37,  -65,   52,    0,    8,   19,    2,
+        19,    3,   19,    1,    0,   -1,    1,    0,
+         0,  -85,    0,    0,    0,   14,    0,   12,
+         0,    0,    9,  108,  111,   99,   97,  108,
+       104,  111,  115,  116,    0,   11,    0,    4,
+         3,    0,    1,    2,    0,   10,    0,    4,
+         0,    2,    0,   23,    0,   35,    0,    0,
+         0,    5,    0,    5,    1,    0,    0,    0,
+         0,    0,   22,    0,    0,    0,   23,    0,
+         0,    0,   13,    0,   30,    0,   28,    4,
+         3,    5,    3,    6,    3,    8,    7,    8,
+         8,    8,    9,    8,   10,    8,   11,    8,
+         4,    8,    5,    8,    6,    4,    1,    5,
+         1,    6,    1,    0,   43,    0,    3,    2,
+         3,    4,    0,   51,    0,   71,    0,   69,
+         0,   23,    0,   65,    4,  125,  -92,  -50,
+       -91,  -39,  -55, -114,    0,   22,    2,  -50,
+       123, -126,    0,  -94,  100, -119, -106,  125,
+       -81,  -24,   51,  -84,   25,   25, -115,   13,
+       -17,  -20,   93,   68,  -97,  -79,  -98,   91,
+        86,   91, -114,  123,  119,  -87,  -12,   32,
+        63,  -41,   50,  126,  -70,   96,   33,   -6,
+        94,   -7,  -68,   54,  -47,   53,    0,   88,
+        40,  -48, -102,  -50,   88
+    };
+
+    // Client Hello with the pre_shared_key extension absent but
+    // containing the psk_key_exchange_modes extension asserted.  No
+    // manual modification was necessary.
+    //
+    //    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
+    //        Content Type: Handshake (22)
+    //        Version: TLS 1.0 (0x0301)
+    //        Length: 262
+    //        Handshake Protocol: Client Hello
+    //            Handshake Type: Client Hello (1)
+    //            Length: 258
+    //            Version: TLS 1.2 (0x0303)
+    //            Random: 9b796ad0cbd559fb48fc4ba32da5bb8c1ef9a7da85231860...
+    //            Session ID Length: 32
+    //            Session ID: fe8411205bc99a506952f5c28569facb96ff0f37621be072...
+    //            Cipher Suites Length: 8
+    //            Cipher Suites (4 suites)
+    //            Compression Methods Length: 1
+    //            Compression Methods (1 method)
+    //            Extensions Length: 177
+    //            Extension: server_name (len=14)
+    //            Extension: ec_point_formats (len=4)
+    //            Extension: supported_groups (len=4)
+    //            Extension: SessionTicket TLS (len=0)
+    //            Extension: status_request (len=5)
+    //            Extension: encrypt_then_mac (len=0)
+    //            Extension: extended_master_secret (len=0)
+    //            Extension: signature_algorithms (len=30)
+    //            Extension: supported_versions (len=3)
+    //            Extension: psk_key_exchange_modes (len=2)
+    //                Type: psk_key_exchange_modes (45)
+    //                Length: 2
+    //                PSK Key Exchange Modes Length: 1
+    //                PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
+    //            Extension: key_share (len=71)
+    private static final byte[] CLIHELLO_NOPSK_YESPSKEXMODE = {
+        22,    3,    1,    1,    6,    1,    0,    1,
+         2,    3,    3, -101,  121,  106,  -48,  -53,
+       -43,   89,   -5,   72,   -4,   75,  -93,   45,
+       -91,  -69, -116,   30,   -7,  -89,  -38, -123,
+        35,   24,   96,   29,  -93,  -22,   10,  -97,
+       -15,  -11,    3,   32,   -2, -124,   17,   32,
+        91,  -55, -102,   80,  105,   82,  -11,  -62,
+      -123,  105,   -6,  -53, -106,   -1,   15,   55,
+        98,   27,  -32,  114, -126,  -13,   42, -104,
+      -102,   37,  -65,   52,    0,    8,   19,    2,
+        19,    3,   19,    1,    0,   -1,    1,    0,
+         0,  -79,    0,    0,    0,   14,    0,   12,
+         0,    0,    9,  108,  111,   99,   97,  108,
+       104,  111,  115,  116,    0,   11,    0,    4,
+         3,    0,    1,    2,    0,   10,    0,    4,
+         0,    2,    0,   23,    0,   35,    0,    0,
+         0,    5,    0,    5,    1,    0,    0,    0,
+         0,    0,   22,    0,    0,    0,   23,    0,
+         0,    0,   13,    0,   30,    0,   28,    4,
+         3,    5,    3,    6,    3,    8,    7,    8,
+         8,    8,    9,    8,   10,    8,   11,    8,
+         4,    8,    5,    8,    6,    4,    1,    5,
+         1,    6,    1,    0,   43,    0,    3,    2,
+         3,    4,    0,   45,    0,    2,    1,    1,
+         0,   51,    0,   71,    0,   69,    0,   23,
+         0,   65,    4,  125,  -92,  -50,  -91,  -39,
+       -55, -114,    0,   22,    2,  -50,  123, -126,
+         0,  -94,  100, -119, -106,  125,  -81,  -24,
+        51,  -84,   25,   25, -115,   13,  -17,  -20,
+        93,   68,  -97,  -79,  -98,   91,   86,   91,
+      -114,  123,  119,  -87,  -12,   32,   63,  -41,
+        50,  126,  -70,   96,   33,   -6,   94,   -7,
+       -68,   54,  -47,   53,    0,   88,   40,  -48,
+      -102,  -50,   88
+    };
+
+    // Client Hello with pre_shared_key asserted and psk_key_exchange_modes
+    // absent.  This is a violation of RFC 8446.  This required manual
+    // removal of the psk_key_exchange_modes extension.
+    //
+    //    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
+    //        Content Type: Handshake (22)
+    //        Version: TLS 1.0 (0x0301)
+    //        Length: 318
+    //        Handshake Protocol: Client Hello
+    //            Handshake Type: Client Hello (1)
+    //            Length: 314
+    //            Version: TLS 1.2 (0x0303)
+    //            Random: e730e42336a19ed9fdb42919c65769132e9e779a797f188c...
+    //            Session ID Length: 32
+    //            Session ID: 6c6ed31408042fabd0c47fdeee6d19de2d6795e37590f00e...
+    //            Cipher Suites Length: 8
+    //            Cipher Suites (4 suites)
+    //            Compression Methods Length: 1
+    //            Compression Methods (1 method)
+    //            Extensions Length: 233
+    //            Extension: server_name (len=14)
+    //            Extension: ec_point_formats (len=4)
+    //            Extension: supported_groups (len=4)
+    //            Extension: SessionTicket TLS (len=0)
+    //            Extension: status_request (len=5)
+    //            Extension: encrypt_then_mac (len=0)
+    //            Extension: extended_master_secret (len=0)
+    //            Extension: signature_algorithms (len=30)
+    //            Extension: supported_versions (len=3)
+    //            Extension: key_share (len=71)
+    //            Extension: pre_shared_key (len=58)
+    //                Type: pre_shared_key (41)
+    //                Length: 58
+    //                Pre-Shared Key extension
+    //                    Identities Length: 21
+    //                    PSK Identity (length: 15)
+    //                        Identity Length: 15
+    //                        Identity: 436c69656e745f6964656e74697479
+    //                        Obfuscated Ticket Age: 0
+    //                    PSK Binders length: 33
+    //                    PSK Binders
+    private static final byte[] CLIHELLO_YESPSK_NOPSKEXMODE = {
+        22,    3,    1,    1,   62,    1,    0,    1,
+        58,    3,    3,  -25,   48,  -28,   35,   54,
+       -95,  -98,  -39,   -3,  -76,   41,   25,  -58,
+        87,  105,   19,   46,  -98,  119, -102,  121,
+       127,   24, -116,   -9,  -99,   22,  116,  -97,
+        90,   73,  -18,   32,  108,  110,  -45,   20,
+         8,    4,   47,  -85,  -48,  -60,  127,  -34,
+       -18,  109,   25,  -34,   45,  103, -107,  -29,
+       117, -112,  -16,   14,   -5,  -24,   24,   61,
+        -9,   28, -119,  -73,    0,    8,   19,    2,
+        19,    3,   19,    1,    0,   -1,    1,    0,
+         0,  -23,    0,    0,    0,   14,    0,   12,
+         0,    0,    9,  108,  111,   99,   97,  108,
+       104,  111,  115,  116,    0,   11,    0,    4,
+         3,    0,    1,    2,    0,   10,    0,    4,
+         0,    2,    0,   23,    0,   35,    0,    0,
+         0,    5,    0,    5,    1,    0,    0,    0,
+         0,    0,   22,    0,    0,    0,   23,    0,
+         0,    0,   13,    0,   30,    0,   28,    4,
+         3,    5,    3,    6,    3,    8,    7,    8,
+         8,    8,    9,    8,   10,    8,   11,    8,
+         4,    8,    5,    8,    6,    4,    1,    5,
+         1,    6,    1,    0,   43,    0,    3,    2,
+         3,    4,    0,   51,    0,   71,    0,   69,
+         0,   23,    0,   65,    4,   -6,  101,  105,
+        -2,   -6,   85,  -99,  -37,  112,   90,   44,
+      -123, -107,    4,  -12,  -64,   92,   40,  100,
+        22,  -53, -124,   54,   56,  102,   25,   76,
+       -86,   -1,    6,  110,   95,   92,  -86,  -35,
+      -101,  115,   85,   99,   19,    6,  -43,  105,
+       -37,  -92,   53,  -97,   84,   -1,  -53,   87,
+       -53, -107,  -13,  -14,   32,  101,  -35,   39,
+       102,  -17, -119,  -25,  -51,    0,   41,    0,
+        58,    0,   21,    0,   15,   67,  108,  105,
+       101,  110,  116,   95,  105,  100,  101,  110,
+       116,  105,  116,  121,    0,    0,    0,    0,
+         0,   33,   32, -113,  -27,  -44,  -71,  -68,
+       -26,  -47,   57,  -82,  -29,  -13,  -61,   77,
+        52,  -60,   27,   74, -120, -104,  102,   21,
+       121,    0,   48,   43,  -40,  -19,  -67,   57,
+       -20,   97,   23
+    };
+
+    // Client Hello containing both pre_shared_key and psk_key_exchange_modes
+    // extensions.  Generation of this hello was done by adding
+    // "-psk a1b2c3d4" to the s_client command.
+    //
+    //    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
+    //        Content Type: Handshake (22)
+    //        Version: TLS 1.0 (0x0301)
+    //        Length: 324
+    //        Handshake Protocol: Client Hello
+    //            Handshake Type: Client Hello (1)
+    //            Length: 320
+    //            Version: TLS 1.2 (0x0303)
+    //            Random: e730e42336a19ed9fdb42919c65769132e9e779a797f188c...
+    //            Session ID Length: 32
+    //            Session ID: 6c6ed31408042fabd0c47fdeee6d19de2d6795e37590f00e...
+    //            Cipher Suites Length: 8
+    //            Cipher Suites (4 suites)
+    //            Compression Methods Length: 1
+    //            Compression Methods (1 method)
+    //            Extensions Length: 239
+    //            Extension: server_name (len=14)
+    //            Extension: ec_point_formats (len=4)
+    //            Extension: supported_groups (len=4)
+    //            Extension: SessionTicket TLS (len=0)
+    //            Extension: status_request (len=5)
+    //            Extension: encrypt_then_mac (len=0)
+    //            Extension: extended_master_secret (len=0)
+    //            Extension: signature_algorithms (len=30)
+    //            Extension: supported_versions (len=3)
+    //            Extension: psk_key_exchange_modes (len=2)
+    //                Type: psk_key_exchange_modes (45)
+    //                Length: 2
+    //                PSK Key Exchange Modes Length: 1
+    //                PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
+    //            Extension: key_share (len=71)
+    //            Extension: pre_shared_key (len=58)
+    //                Type: pre_shared_key (41)
+    //                Length: 58
+    //                Pre-Shared Key extension
+    //                    Identities Length: 21
+    //                    PSK Identity (length: 15)
+    //                        Identity Length: 15
+    //                        Identity: 436c69656e745f6964656e74697479
+    //                        Obfuscated Ticket Age: 0
+    //                    PSK Binders length: 33
+    //                    PSK Binders
+    private static final byte[] CLIHELLO_YESPSK_YESPSKEXMODE = {
+        22,    3,    1,    1,   68,    1,    0,    1,
+        64,    3,    3,  -25,   48,  -28,   35,   54,
+       -95,  -98,  -39,   -3,  -76,   41,   25,  -58,
+        87,  105,   19,   46,  -98,  119, -102,  121,
+       127,   24, -116,   -9,  -99,   22,  116,  -97,
+        90,   73,  -18,   32,  108,  110,  -45,   20,
+         8,    4,   47,  -85,  -48,  -60,  127,  -34,
+       -18,  109,   25,  -34,   45,  103, -107,  -29,
+       117, -112,  -16,   14,   -5,  -24,   24,   61,
+        -9,   28, -119,  -73,    0,    8,   19,    2,
+        19,    3,   19,    1,    0,   -1,    1,    0,
+         0,  -17,    0,    0,    0,   14,    0,   12,
+         0,    0,    9,  108,  111,   99,   97,  108,
+       104,  111,  115,  116,    0,   11,    0,    4,
+         3,    0,    1,    2,    0,   10,    0,    4,
+         0,    2,    0,   23,    0,   35,    0,    0,
+         0,    5,    0,    5,    1,    0,    0,    0,
+         0,    0,   22,    0,    0,    0,   23,    0,
+         0,    0,   13,    0,   30,    0,   28,    4,
+         3,    5,    3,    6,    3,    8,    7,    8,
+         8,    8,    9,    8,   10,    8,   11,    8,
+         4,    8,    5,    8,    6,    4,    1,    5,
+         1,    6,    1,    0,   43,    0,    3,    2,
+         3,    4,    0,   45,    0,    2,    1,    1,
+         0,   51,    0,   71,    0,   69,    0,   23,
+         0,   65,    4,   -6,  101,  105,   -2,   -6,
+        85,  -99,  -37,  112,   90,   44, -123, -107,
+         4,  -12,  -64,   92,   40,  100,   22,  -53,
+      -124,   54,   56,  102,   25,   76,  -86,   -1,
+         6,  110,   95,   92,  -86,  -35, -101,  115,
+        85,   99,   19,    6,  -43,  105,  -37,  -92,
+        53,  -97,   84,   -1,  -53,   87,  -53, -107,
+       -13,  -14,   32,  101,  -35,   39,  102,  -17,
+      -119,  -25,  -51,    0,   41,    0,   58,    0,
+        21,    0,   15,   67,  108,  105,  101,  110,
+       116,   95,  105,  100,  101,  110,  116,  105,
+       116,  121,    0,    0,    0,    0,    0,   33,
+        32, -113,  -27,  -44,  -71,  -68,  -26,  -47,
+        57,  -82,  -29,  -13,  -61,   77,   52,  -60,
+        27,   74, -120, -104,  102,   21,  121,    0,
+        48,   43,  -40,  -19,  -67,   57,  -20,   97,
+        23
+    };
+
+    // Client Hello with sect163k1 and secp256r1 as supported groups.  This
+    // test covers an error condition where a known, supported curve that is
+    // not in the default enabled set of curves would cause failures.
+    // Generation of this hello was done using "-curves sect163k1:prime256v1"
+    // as an option to s_client.
+    //
+    //    TLSv1.2 Record Layer: Handshake Protocol: Client Hello
+    //        Content Type: Handshake (22)
+    //        Version: TLS 1.0 (0x0301)
+    //        Length: 210
+    //        Handshake Protocol: Client Hello
+    //            Handshake Type: Client Hello (1)
+    //            Length: 206
+    //            Version: TLS 1.2 (0x0303)
+    //            Random: 05cbae9b834851d856355b72601cb67b7cd4eb51f29ed50b...
+    //            Session ID Length: 0
+    //            Cipher Suites Length: 56
+    //            Cipher Suites (28 suites)
+    //            Compression Methods Length: 1
+    //            Compression Methods (1 method)
+    //            Extensions Length: 109
+    //            Extension: server_name (len=14)
+    //            Extension: ec_point_formats (len=4)
+    //            Extension: supported_groups (len=6)
+    //                Type: supported_groups (10)
+    //                Length: 6
+    //                Supported Groups List Length: 4
+    //                Supported Groups (2 groups)
+    //                    Supported Group: sect163k1 (0x0001)
+    //                    Supported Group: secp256r1 (0x0017)
+    //            Extension: SessionTicket TLS (len=0)
+    //            Extension: status_request (len=5)
+    //            Extension: encrypt_then_mac (len=0)
+    //            Extension: extended_master_secret (len=0)
+    //            Extension: signature_algorithms (len=48)
+    private static final byte[] CLIHELLO_SUPGRP_SECT163K1 = {
+        22,    3,    1,    0,  -46,    1,    0,    0,
+       -50,    3,    3,    5,  -53,  -82, -101, -125,
+        72,   81,  -40,   86,   53,   91,  114,   96,
+        28,  -74,  123,  124,  -44,  -21,   81,  -14,
+       -98,  -43,   11,   90,  -87, -106,   13,   63,
+       -62,  100,  111,    0,    0,   56,  -64,   44,
+       -64,   48,    0,  -97,  -52,  -87,  -52,  -88,
+       -52,  -86,  -64,   43,  -64,   47,    0,  -98,
+       -64,   36,  -64,   40,    0,  107,  -64,   35,
+       -64,   39,    0,  103,  -64,   10,  -64,   20,
+         0,   57,  -64,    9,  -64,   19,    0,   51,
+         0,  -99,    0, -100,    0,   61,    0,   60,
+         0,   53,    0,   47,    0,   -1,    1,    0,
+         0,  109,    0,    0,    0,   14,    0,   12,
+         0,    0,    9,  108,  111,   99,   97,  108,
+       104,  111,  115,  116,    0,   11,    0,    4,
+         3,    0,    1,    2,    0,   10,    0,    6,
+         0,    4,    0,    1,    0,   23,    0,   35,
+         0,    0,    0,    5,    0,    5,    1,    0,
+         0,    0,    0,    0,   22,    0,    0,    0,
+        23,    0,    0,    0,   13,    0,   48,    0,
+        46,    4,    3,    5,    3,    6,    3,    8,
+         7,    8,    8,    8,    9,    8,   10,    8,
+        11,    8,    4,    8,    5,    8,    6,    4,
+         1,    5,    1,    6,    1,    3,    3,    2,
+         3,    3,    1,    2,    1,    3,    2,    2,
+         2,    4,    2,    5,    2,    6,    2
+    };
+
+    public static interface TestCase {
+        void execTest() throws Exception;
+    }
+
+    private static final Map<String, TestCase> TESTMAP = new HashMap<>();
+
+    public static void main(String[] args) throws Exception {
+        boolean allGood = true;
+        System.setProperty("javax.net.debug", "ssl:handshake");
+        trustMgrFac = makeTrustManagerFactory(trustFilename, passwd);
+        keyMgrFac = makeKeyManagerFactory(keyFilename, passwd);
+
+        // Populate the test map
+        TESTMAP.put("noPskNoKexModes", noPskNoKexModes);
+        TESTMAP.put("noPskYesKexModes", noPskYesKexModes);
+        TESTMAP.put("yesPskNoKexModes", yesPskNoKexModes);
+        TESTMAP.put("yesPskYesKexModes", yesPskYesKexModes);
+        TESTMAP.put("supGroupsSect163k1", supGroupsSect163k1);
+
+        if (args == null || args.length < 1) {
+            throw new Exception("FAIL: Test @run line is missing a test label");
+        }
+
+        // Pull the test to run from the test map.
+        TestCase test = Objects.requireNonNull(TESTMAP.get(args[0]),
+                "No TestCase found for test label " + args[0]);
+        test.execTest();
+    }
+
+    /**
+     * Test case to cover hellos with no pre_shared_key nor
+     * psk_key_exchange_modes extensions.  Clients not supporting PSK at all
+     * may send hellos like this.
+     */
+    private static final TestCase noPskNoKexModes = new TestCase() {
+        @Override
+        public void execTest() throws Exception {
+            System.out.println("\nTest: PSK = No, PSKEX = No");
+            processClientHello("TLS", CLIHELLO_NOPSK_NOPSKEXMODE);
+            System.out.println("PASS");
+        }
+    };
+
+    /**
+     * Test case to cover hellos with no pre_shared_key but have the
+     * psk_key_exchange_modes extension.  This kind of hello is seen from
+     * some popular browsers and test clients.
+     */
+    private static final TestCase noPskYesKexModes = new TestCase() {
+        @Override
+        public void execTest() throws Exception {
+            System.out.println("\nTest: PSK = No, PSKEX = Yes");
+            processClientHello("TLS", CLIHELLO_NOPSK_YESPSKEXMODE);
+            System.out.println("PASS");
+        }
+    };
+
+    /**
+     * Test case using a client hello with the pre_shared_key extension but
+     * no psk_key_exchange_modes extension present.  This is a violation of
+     * 8446 and should cause an exception when unwrapped and processed by
+     * SSLEngine.
+     */
+    private static final TestCase yesPskNoKexModes = new TestCase() {
+        @Override
+        public void execTest() throws Exception {
+            try {
+                System.out.println("\nTest: PSK = Yes, PSKEX = No");
+                processClientHello("TLS", CLIHELLO_YESPSK_NOPSKEXMODE);
+                throw new Exception(
+                    "FAIL: Client Hello processed without expected error");
+            } catch (SSLHandshakeException sslhe) {
+                System.out.println("PASS: Caught expected exception: " + sslhe);
+            }
+        }
+    };
+
+    /**
+     * Test case using a client hello asserting the pre_shared_key and
+     * psk_key_exchange_modes extensions.
+     */
+    private static final TestCase yesPskYesKexModes = new TestCase() {
+        @Override
+        public void execTest() throws Exception {
+            System.out.println("\nTest: PSK = Yes, PSKEX = Yes");
+            processClientHello("TLS", CLIHELLO_YESPSK_YESPSKEXMODE);
+            System.out.println("PASS");
+        }
+    };
+
+    /**
+     * Test case with a client hello asserting two named curves in the
+     * supported_groups extension: sect163k1 and secp256r1.
+     */
+    private static final TestCase supGroupsSect163k1 = new TestCase() {
+        @Override
+        public void execTest() throws Exception {
+            System.out.println("\nTest: Use of non-default-enabled " +
+                    "Supported Group (sect163k1)");
+            processClientHello("TLS", CLIHELLO_SUPGRP_SECT163K1);
+            System.out.println("PASS");
+        }
+    };
+
+    /**
+     * Send a ClientHello message to an SSLEngine instance configured as a
+     * server.
+     *
+     * @param proto the protocol used to create the SSLContext.  This will
+     *      default to "TLS" if null is passed in.
+     * @param message the ClientHello as a complete TLS record.
+     *
+     * @throws Exception if any processing errors occur.  The caller (TestCase)
+     *      is expected to deal with the exception in whatever way appropriate
+     *      for the test.
+     */
+    private static void processClientHello(String proto, byte[] message)
+            throws Exception {
+        SSLEngine serverEng = makeServerEngine(proto, keyMgrFac, trustMgrFac);
+        ByteBuffer sTOc = makePacketBuf(serverEng);
+        SSLEngineResult serverResult;
+
+        ByteBuffer cTOs = ByteBuffer.wrap(message);
+        System.out.println("CLIENT-TO-SERVER\n" +
+                dumpHexBytes(cTOs, 16, "\n", " "));
+        serverResult = serverEng.unwrap(cTOs, SERVOUTBUF);
+        printResult("server unwrap: ", serverResult);
+        runDelegatedTasks(serverResult, serverEng);
+        serverEng.wrap(SERVOUTBUF, sTOc);
+    }
+
+    /**
+     * Create a TrustManagerFactory from a given keystore.
+     *
+     * @param tsPath the path to the trust store file.
+     * @param pass the password for the trust store.
+     *
+     * @return a new TrustManagerFactory built from the trust store provided.
+     *
+     * @throws GeneralSecurityException if any processing errors occur
+     *      with the Keystore instantiation or TrustManagerFactory creation.
+     * @throws IOException if any loading error with the trust store occurs.
+     */
+    private static TrustManagerFactory makeTrustManagerFactory(String tsPath,
+            String pass) throws GeneralSecurityException, IOException {
+        KeyStore ts = KeyStore.getInstance("JKS");
+        char[] passphrase = pass.toCharArray();
+
+        ts.load(new FileInputStream(tsPath), passphrase);
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(ts);
+        return tmf;
+    }
+
+    /**
+     * Create a KeyManagerFactory from a given keystore.
+     *
+     * @param ksPath the path to the keystore file.
+     * @param pass the password for the keystore.
+     *
+     * @return a new TrustManagerFactory built from the keystore provided.
+     *
+     * @throws GeneralSecurityException if any processing errors occur
+     *      with the Keystore instantiation or KeyManagerFactory creation.
+     * @throws IOException if any loading error with the keystore occurs
+     */
+    private static KeyManagerFactory makeKeyManagerFactory(String ksPath,
+            String pass) throws GeneralSecurityException, IOException {
+        KeyStore ks = KeyStore.getInstance("JKS");
+        char[] passphrase = pass.toCharArray();
+
+        ks.load(new FileInputStream(ksPath), passphrase);
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(ks, passphrase);
+        return kmf;
+    }
+
+    /**
+     * Create an SSLEngine instance from a given protocol specifier,
+     * KeyManagerFactory and TrustManagerFactory.
+     *
+     * @param proto the protocol specifier for the SSLContext.  This will
+     *      default to "TLS" if null is provided.
+     * @param kmf an initialized KeyManagerFactory.  May be null.
+     * @param tmf an initialized TrustManagerFactory.  May be null.
+     *
+     * @return an SSLEngine instance configured as a server and with client
+     *      authentication disabled.
+     *
+     * @throws GeneralSecurityException if any errors occur during the
+     *      creation of the SSLEngine.
+     */
+    private static SSLEngine makeServerEngine(String proto,
+            KeyManagerFactory kmf, TrustManagerFactory tmf)
+            throws GeneralSecurityException {
+        SSLContext ctx = SSLContext.getInstance(proto != null ? proto : "TLS");
+        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+        SSLEngine ssle = ctx.createSSLEngine();
+        ssle.setUseClientMode(false);
+        ssle.setNeedClientAuth(false);
+        return ssle;
+    }
+
+    /**
+     * Make a ByteBuffer sized for TLS records that can be used by an SSLEngine.
+     *
+     * @param engine the SSLEngine used to determine the packet buffer size.
+     *
+     * @return a ByteBuffer sized for TLS packets.
+     */
+    private static ByteBuffer makePacketBuf(SSLEngine engine) {
+        SSLSession sess = engine.getSession();
+        ByteBuffer packetBuf = ByteBuffer.allocate(sess.getPacketBufferSize());
+        return packetBuf;
+    }
+
+    /**
+     * Runs any delegated tasks after unwrapping TLS records.
+     *
+     * @param result the most recent result from an unwrap operation on
+     *      an SSLEngine.
+     * @param engine the SSLEngine used to unwrap the data.
+     *
+     * @throws Exception if any errors occur while running the delegated
+     *      tasks.
+     */
+    private static void runDelegatedTasks(SSLEngineResult result,
+            SSLEngine engine) throws Exception {
+        HandshakeStatus hsStatus = result.getHandshakeStatus();
+        if (hsStatus == HandshakeStatus.NEED_TASK) {
+            Runnable runnable;
+            while ((runnable = engine.getDelegatedTask()) != null) {
+                System.out.println("\trunning delegated task...");
+                runnable.run();
+            }
+            hsStatus = engine.getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_TASK) {
+                throw new Exception(
+                    "handshake shouldn't need additional tasks");
+            }
+            System.out.println("\tnew HandshakeStatus: " + hsStatus);
+        }
+    }
+
+    /**
+     * Display the results of a wrap or unwrap operation from an SSLEngine.
+     *
+     * @param str a label to be prefixed to the result display.
+     * @param result the result returned from the wrap/unwrap operation.
+     */
+    private static void printResult(String str, SSLEngineResult result) {
+        System.out.println("The format of the SSLEngineResult is: \n" +
+            "\t\"getStatus() / getHandshakeStatus()\" +\n" +
+            "\t\"bytesConsumed() / bytesProduced()\"\n");
+        HandshakeStatus hsStatus = result.getHandshakeStatus();
+        System.out.println(str + result.getStatus() + "/" + hsStatus + ", " +
+            result.bytesConsumed() + "/" + result.bytesProduced() + " bytes");
+        if (hsStatus == HandshakeStatus.FINISHED) {
+            System.out.println("\t...ready for application data");
+        }
+    }
+
+    /**
+     * Dump the hex bytes of a buffer into string form.
+     *
+     * @param data The array of bytes to dump to stdout.
+     * @param itemsPerLine The number of bytes to display per line
+     *      if the {@code lineDelim} character is blank then all bytes
+     *      will be printed on a single line.
+     * @param lineDelim The delimiter between lines
+     * @param itemDelim The delimiter between bytes
+     *
+     * @return The hexdump of the byte array
+     */
+    private static String dumpHexBytes(byte[] data, int itemsPerLine,
+            String lineDelim, String itemDelim) {
+        return dumpHexBytes(ByteBuffer.wrap(data), itemsPerLine, lineDelim,
+                itemDelim);
+    }
+
+    /**
+     * Dump the hex bytes of a buffer into string form.
+     *
+     * @param data The ByteBuffer to dump to stdout.
+     * @param itemsPerLine The number of bytes to display per line
+     *      if the {@code lineDelim} character is blank then all bytes
+     *      will be printed on a single line.
+     * @param lineDelim The delimiter between lines
+     * @param itemDelim The delimiter between bytes
+     *
+     * @return The hexdump of the byte array
+     */
+    private static String dumpHexBytes(ByteBuffer data, int itemsPerLine,
+            String lineDelim, String itemDelim) {
+        StringBuilder sb = new StringBuilder();
+        if (data != null) {
+            data.mark();
+            int i = 0;
+            while (data.remaining() > 0) {
+                if (i % itemsPerLine == 0 && i != 0) {
+                    sb.append(lineDelim);
+                }
+                sb.append(String.format("%02X", data.get())).append(itemDelim);
+                i++;
+            }
+            data.reset();
+        }
+
+        return sb.toString();
+    }
+}