/*
 * 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();
    }
}