jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java
changeset 30904 ec0224270f90
child 32032 22badc53802f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java	Tue Jun 02 04:01:04 2015 +0000
@@ -0,0 +1,925 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.security.ssl;
+
+import java.util.LinkedList;
+import java.util.HashMap;
+import javax.net.ssl.SSLProtocolException;
+
+import sun.security.ssl.HandshakeMessage.*;
+
+import static sun.security.ssl.CipherSuite.KeyExchange;
+import static sun.security.ssl.CipherSuite.KeyExchange.*;
+import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
+import static sun.security.ssl.HandshakeMessage.*;
+
+/*
+ * Handshake state manager.
+ *
+ * Messages flow for a full handshake:
+ *
+ *      -                                                         -
+ *      |          HelloRequest       (No.0, RFC 5246) [*]        |
+ *      |     <--------------------------------------------       |
+ *      |                                                         |
+ *      |          ClientHello        (No.1, RFC 5246)            |
+ *      |     -------------------------------------------->       |
+ *      |                                                         |
+ *      |   -      HelloVerifyRequest (No.3, RFC 6347)      -     |
+ *      | D | <-------------------------------------------- | D   |
+ *      | T |                                               | T   |
+ *      | L |      ClientHello        (No.1, RFC 5246)      | L   |
+ *      | S | --------------------------------------------> | S   |
+ *      |   -                                               -     |
+ *      |                                                         |
+ *   C  |          ServerHello        (No.2, RFC 5246)            |  S
+ *   L  |          SupplementalData   (No.23, RFC4680) [*]        |  E
+ *   I  |          Certificate        (No.11, RFC 5246) [*]       |  R
+ *   E  |          CertificateStatus  (No.22, RFC 6066) [*]       |  V
+ *   N  |          ServerKeyExchange  (No.12, RFC 5246) [*]       |  E
+ *   T  |          CertificateRequest (No.13, RFC 5246) [*]       |  R
+ *      |          ServerHelloDone    (No.14, RFC 5246)           |
+ *      |     <--------------------------------------------       |
+ *      |                                                         |
+ *      |          SupplementalData   (No.23, RFC4680) [*]        |
+ *      |          Certificate        (No.11, RFC 5246) [*] Or    |
+ *      |              CertificateURL (No.21, RFC6066) [*]        |
+ *      |          ClientKeyExchange  (No.16, RFC 5246)           |
+ *      |          CertificateVerify  (No.15, RFC 5246) [*]       |
+ *      |          [ChangeCipherSpec] (RFC 5246)                  |
+ *      |          Finished           (No.20, RFC 5246)           |
+ *      |     -------------------------------------------->       |
+ *      |                                                         |
+ *      |          NewSessionTicket   (No.4, RFC4507) [*]         |
+ *      |          [ChangeCipherSpec] (RFC 5246)                  |
+ *      |          Finished           (No.20, RFC 5246)           |
+ *      |     <--------------------------------------------       |
+ *      -                                                         -
+ * [*] Indicates optional or situation-dependent messages that are not
+ * always sent.
+ *
+ * Message flow for an abbreviated handshake:
+ *      -                                                         -
+ *      |          ClientHello        (No.1, RFC 5246)            |
+ *      |     -------------------------------------------->       |
+ *      |                                                         |
+ *   C  |          ServerHello        (No.2, RFC 5246)            |  S
+ *   L  |          NewSessionTicket   (No.4, RFC4507) [*]         |  E
+ *   I  |          [ChangeCipherSpec] (RFC 5246)                  |  R
+ *   E  |          Finished           (No.20, RFC 5246)           |  V
+ *   N  |     <--------------------------------------------       |  E
+ *   T  |                                                         |  R
+ *      |          [ChangeCipherSpec] (RFC 5246)                  |
+ *      |          Finished           (No.20, RFC 5246)           |
+ *      |     -------------------------------------------->       |
+ *      -                                                         -
+ *
+ *
+ * State machine of handshake states:
+ *
+ *                   +--------------+
+ *      START -----> | HelloRequest |
+ *        |          +--------------+
+ *        |               |
+ *        v               v
+ *     +---------------------+   -->  +---------------------+
+ *     |    ClientHello      |        | HelloVerifyRequest  |
+ *     +---------------------+   <--  +---------------------+
+ *               |
+ *               |
+ * =========================================================================
+ *               |
+ *               v
+ *     +---------------------+
+ *     |    ServerHello      |  ----------------------------------+------+
+ *     +---------------------+  -->  +-------------------------+  |      |
+ *                    |              | Server SupplementalData |  |      |
+ *                    |              +-------------------------+  |      |
+ *                    |                |                          |      |
+ *                    v                v                          |      |
+ *                +---------------------+                         |      |
+ *         +----  | Server Certificate  |                         |      |
+ *         |      +---------------------+                         |      |
+ *         |          |                                           |      |
+ *         |          |   +--------------------+                  |      |
+ *         |          +-> | CertificateStatus  |                  |      |
+ *         |          |   +--------------------+                  v      |
+ *         |          |      |          |     +--------------------+     |
+ *         |          v      v          +-->  | ServerKeyExchange  |     |
+ *         |  +---------------------+   |     +--------------------+     |
+ *         |  | CertificateRequest  |   |         |                      |
+ *         |  +---------------------+ <-+---------+                      |
+ *         |            |               |         |                      |
+ *         v            v               |         |                      |
+ *     +---------------------+  <-------+         |                      |
+ *     |  ServerHelloDone    |  <-----------------+                      |
+ *     +---------------------+                                           |
+ *       |         |                                                     |
+ *       |         |                                                     |
+ *       |         |                                                     |
+ * =========================================================================
+ *       |         |                                                     |
+ *       |         v                                                     |
+ *       |   +-------------------------+                                 |
+ *       |   | Client SupplementalData | --------------+                 |
+ *       |   +-------------------------+               |                 |
+ *       |             |                               |                 |
+ *       |             v                               |                 |
+ *       |   +--------------------+                    |                 |
+ *       +-> | Client Certificate | ALT.               |                 |
+ *       |   +--------------------+----------------+   |                 |
+ *       |                        | CertificateURL |   |                 |
+ *       |                        +----------------+   |                 |
+ *       v                                             |                 |
+ *     +-------------------+  <------------------------+                 |
+ *     | ClientKeyExchange |                                             |
+ *     +-------------------+                                             |
+ *          |           |                                                |
+ *          |           v                                                |
+ *          |      +-------------------+                                 |
+ *          |      | CertificateVerify |                                 |
+ *          |      +-------------------+                                 |
+ *          |          |                                                 |
+ *          v          v                                                 |
+ *     +-------------------------+                                       |
+ *     | Client ChangeCipherSpec |  <---------------+                    |
+ *     +-------------------------+                  |                    |
+ *               |                                  |                    |
+ *               v                                  |                    |
+ *     +-----------------+  (abbreviated)           |                    |
+ *     | Client Finished |  -------------> END      |                    |
+ *     +-----------------+  (Abbreviated handshake) |                    |
+ *                      |                           |                    |
+ *                      | (full)                    |                    |
+ *                      |                           |                    |
+ * ================================                 |                    |
+ *                      |                           |                    |
+ *                      |                   ================================
+ *                      |                           |                    |
+ *                      v                           |                    |
+ *                 +------------------+             |    (abbreviated)   |
+ *                 | NewSessionTicket | <--------------------------------+
+ *                 +------------------+             |                    |
+ *                      |                           |                    |
+ *                      v                           |                    |
+ *     +-------------------------+                  |    (abbreviated)   |
+ *     | Server ChangeCipherSpec | <-------------------------------------+
+ *     +-------------------------+                  |
+ *               |                                  |
+ *               v                                  |
+ *     +-----------------+    (abbreviated)         |
+ *     | Server Finished | -------------------------+
+ *     +-----------------+
+ *            | (full)
+ *            v
+ *        END (Full handshake)
+ *
+ *
+ * The scenarios of the use of this class:
+ * 1. Create an instance of HandshakeStateManager during the initializtion
+ *    handshake.
+ * 2. If receiving a handshake message, call HandshakeStateManager.check()
+ *    to make sure that the message is of the expected handshake type.  And
+ *    then call HandshakeStateManager.update() in case handshake states may
+ *    be impacted by this new incoming handshake message.
+ * 3. On delivering a handshake message, call HandshakeStateManager.update()
+ *    in case handshake states may by thie new outgoing handshake message.
+ * 4. On receiving and delivering ChangeCipherSpec message, call
+ *    HandshakeStateManager.changeCipherSpec() to check the present sequence
+ *    of this message, and update the states if necessary.
+ */
+final class HandshakeStateManager {
+    // upcoming handshake states.
+    private LinkedList<HandshakeState> upcomingStates;
+    private LinkedList<HandshakeState> alternatives;
+
+    private boolean isDTLS;
+
+    private final static boolean debugIsOn;
+
+    private final static HashMap<Byte, String> handshakeTypes;
+
+    static {
+        debugIsOn = (Handshaker.debug != null) &&
+                Debug.isOn("handshake") && Debug.isOn("verbose");
+        handshakeTypes = new HashMap<>(15);
+
+        handshakeTypes.put(ht_hello_request,            "hello_request");
+        handshakeTypes.put(ht_client_hello,             "client_hello");
+        handshakeTypes.put(ht_server_hello,             "server_hello");
+        handshakeTypes.put(ht_hello_verify_request,     "hello_verify_request");
+        handshakeTypes.put(ht_new_session_ticket,       "session_ticket");
+        handshakeTypes.put(ht_certificate,              "certificate");
+        handshakeTypes.put(ht_server_key_exchange,      "server_key_exchange");
+        handshakeTypes.put(ht_certificate_request,      "certificate_request");
+        handshakeTypes.put(ht_server_hello_done,        "server_hello_done");
+        handshakeTypes.put(ht_certificate_verify,       "certificate_verify");
+        handshakeTypes.put(ht_client_key_exchange,      "client_key_exchange");
+        handshakeTypes.put(ht_finished,                 "finished");
+        handshakeTypes.put(ht_certificate_url,          "certificate_url");
+        handshakeTypes.put(ht_certificate_status,       "certificate_status");
+        handshakeTypes.put(ht_supplemental_data,        "supplemental_data");
+    }
+
+    HandshakeStateManager(boolean isDTLS) {
+        this.upcomingStates = new LinkedList<>();
+        this.alternatives = new LinkedList<>();
+        this.isDTLS = isDTLS;
+    }
+
+    //
+    // enumation of handshake type
+    //
+    static enum HandshakeState {
+        HS_HELLO_REQUEST(
+                "hello_request",
+                HandshakeMessage.ht_hello_request),
+        HS_CLIENT_HELLO(
+                "client_hello",
+                HandshakeMessage.ht_client_hello),
+        HS_HELLO_VERIFY_REQUEST(
+                "hello_verify_request",
+                HandshakeMessage.ht_hello_verify_request),
+        HS_SERVER_HELLO(
+                "server_hello",
+                HandshakeMessage.ht_server_hello),
+        HS_SERVER_SUPPLEMENTAL_DATA(
+                "server supplemental_data",
+                HandshakeMessage.ht_supplemental_data, true),
+        HS_SERVER_CERTIFICATE(
+                "server certificate",
+                HandshakeMessage.ht_certificate),
+        HS_CERTIFICATE_STATUS(
+                "certificate_status",
+                HandshakeMessage.ht_certificate_status, true),
+        HS_SERVER_KEY_EXCHANGE(
+                "server_key_exchange",
+                HandshakeMessage.ht_server_key_exchange, true),
+        HS_CERTIFICATE_REQUEST(
+                "certificate_request",
+                HandshakeMessage.ht_certificate_request, true),
+        HS_SERVER_HELLO_DONE(
+                "server_hello_done",
+                HandshakeMessage.ht_server_hello_done),
+        HS_CLIENT_SUPPLEMENTAL_DATA(
+                "client supplemental_data",
+                HandshakeMessage.ht_supplemental_data, true),
+        HS_CLIENT_CERTIFICATE(
+                "client certificate",
+                HandshakeMessage.ht_certificate, true),
+        HS_CERTIFICATE_URL(
+                "certificate_url",
+                HandshakeMessage.ht_certificate_url, true),
+        HS_CLIENT_KEY_EXCHANGE(
+                "client_key_exchange",
+                HandshakeMessage.ht_client_key_exchange),
+        HS_CERTIFICATE_VERIFY(
+                "certificate_verify",
+                HandshakeMessage.ht_certificate_verify, true),
+        HS_CLIENT_CHANGE_CIPHER_SPEC(
+                "client change_cipher_spec",
+                HandshakeMessage.ht_not_applicable),
+        HS_CLEINT_FINISHED(
+                "client finished",
+                HandshakeMessage.ht_finished),
+        HS_NEW_SESSION_TICKET(
+                "session_ticket",
+                HandshakeMessage.ht_new_session_ticket),
+        HS_SERVER_CHANGE_CIPHER_SPEC(
+                "server change_cipher_spec",
+                HandshakeMessage.ht_not_applicable),
+        HS_SERVER_FINISHDE(
+                "server finished",
+                HandshakeMessage.ht_finished);
+
+        final String description;
+        final byte handshakeType;
+        final boolean isOptional;
+
+        HandshakeState(String description, byte handshakeType) {
+            this.description = description;
+            this.handshakeType = handshakeType;
+            this.isOptional = false;
+        }
+
+        HandshakeState(String description,
+                byte handshakeType, boolean isOptional) {
+
+            this.description = description;
+            this.handshakeType = handshakeType;
+            this.isOptional = isOptional;
+        }
+
+        public String toString() {
+            return description + "[" + handshakeType + "]" +
+                    (isOptional ? "(optional)" : "");
+        }
+    }
+
+    boolean isEmpty() {
+        return upcomingStates.isEmpty();
+    }
+
+    void check(byte handshakeType) throws SSLProtocolException {
+        String exceptionMsg =
+                 "Handshake message sequence violation, " + handshakeType;
+
+        if (debugIsOn) {
+            System.out.println(
+                    "check handshake state: " + toString(handshakeType));
+        }
+
+        if (upcomingStates.isEmpty()) {
+            // Is it a kickstart message?
+            if ((handshakeType != HandshakeMessage.ht_hello_request) &&
+                (handshakeType != HandshakeMessage.ht_client_hello)) {
+
+                throw new SSLProtocolException(
+                    "Handshake message sequence violation, " + handshakeType);
+            }
+
+            // It is a kickstart message.
+            return;
+        }
+
+        // Ignore the checking for HelloRequest messages as they are
+        // may be sent by the server at any time.
+        if (handshakeType == HandshakeMessage.ht_hello_request) {
+            return;
+        }
+
+        for (HandshakeState handshakeState : upcomingStates) {
+            if (handshakeState.handshakeType == handshakeType) {
+                // It's the expected next handshake type.
+                return;
+            }
+
+            if (handshakeState.isOptional) {
+                continue;
+            } else {
+                for (HandshakeState alternative : alternatives) {
+                    if (alternative.handshakeType == handshakeType) {
+                        return;
+                    }
+
+                    if (alternative.isOptional) {
+                        continue;
+                    } else {
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+                }
+            }
+
+            throw new SSLProtocolException(exceptionMsg);
+        }
+
+        // Not an expected Handshake message.
+        throw new SSLProtocolException(
+                "Handshake message sequence violation, " + handshakeType);
+    }
+
+    void update(HandshakeMessage handshakeMessage,
+            boolean isAbbreviated) throws SSLProtocolException {
+
+        byte handshakeType = (byte)handshakeMessage.messageType();
+        String exceptionMsg =
+                 "Handshake message sequence violation, " + handshakeType;
+
+        if (debugIsOn) {
+            System.out.println(
+                    "update handshake state: " + toString(handshakeType));
+        }
+
+        boolean hasPresentState = false;
+        switch (handshakeType) {
+        case HandshakeMessage.ht_hello_request:
+            //
+            // State machine:
+            //     PRESENT: START
+            //        TO  : ClientHello
+            //
+
+            // No old state to update.
+
+            // Add the upcoming states.
+            if (!upcomingStates.isEmpty()) {
+                // A ClientHello message should be followed.
+                upcomingStates.add(HS_CLIENT_HELLO);
+
+            }   // Otherwise, ignore this HelloRequest message.
+
+            break;
+
+        case HandshakeMessage.ht_client_hello:
+            //
+            // State machine:
+            //     PRESENT: START
+            //              HS_CLIENT_HELLO
+            //        TO  : HS_HELLO_VERIFY_REQUEST (DTLS)
+            //              HS_SERVER_HELLO
+            //
+
+            // Check and update the present state.
+            if (!upcomingStates.isEmpty()) {
+                // The current state should be HS_CLIENT_HELLO.
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState != HS_CLIENT_HELLO) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }
+            }
+
+            // Add the upcoming states.
+            ClientHello clientHello = (ClientHello)handshakeMessage;
+            if (isDTLS) {
+                // Is it an initial ClientHello message?
+                if (clientHello.cookie == null ||
+                        clientHello.cookie.length == 0) {
+                    // Is it an abbreviated handshake?
+                    if (clientHello.sessionId.length() != 0) {
+                        // A HelloVerifyRequest message or a ServerHello
+                        // message may follow the abbreviated session
+                        // resuming handshake request.
+                        upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
+                        alternatives.add(HS_SERVER_HELLO);
+                    } else {
+                        // A HelloVerifyRequest message should follow
+                        // the initial ClientHello message.
+                        upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
+                    }
+                } else {
+                    // A HelloVerifyRequest may be followed if the cookie
+                    // cannot be verified.
+                    upcomingStates.add(HS_SERVER_HELLO);
+                    alternatives.add(HS_HELLO_VERIFY_REQUEST);
+                }
+            } else {
+                upcomingStates.add(HS_SERVER_HELLO);
+            }
+
+            break;
+
+        case HandshakeMessage.ht_hello_verify_request:
+            //
+            // State machine:
+            //     PRESENT: HS_HELLO_VERIFY_REQUEST
+            //        TO  : HS_CLIENT_HELLO
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            if (!upcomingStates.isEmpty()) {
+                // The current state should be HS_HELLO_VERIFY_REQUEST.
+                HandshakeState handshakeState = upcomingStates.pop();
+                HandshakeState alternative = null;
+                if (!alternatives.isEmpty()) {
+                    alternative = alternatives.pop();
+                }
+
+                if ((handshakeState != HS_HELLO_VERIFY_REQUEST) &&
+                        (alternative != HS_HELLO_VERIFY_REQUEST)) {
+
+                    throw new SSLProtocolException(exceptionMsg);
+                }
+            } else {
+                // No present state.
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // Add the upcoming states.
+            upcomingStates.add(HS_CLIENT_HELLO);
+
+            break;
+
+        case HandshakeMessage.ht_server_hello:
+            //
+            // State machine:
+            //     PRESENT: HS_SERVER_HELLO
+            //        TO  :
+            //          Full handshake state stacks
+            //              (ServerHello Flight)
+            //              HS_SERVER_SUPPLEMENTAL_DATA [optional]
+            //          --> HS_SERVER_CERTIFICATE [optional]
+            //          --> HS_CERTIFICATE_STATUS [optional]
+            //          --> HS_SERVER_KEY_EXCHANGE [optional]
+            //          --> HS_CERTIFICATE_REQUEST [optional]
+            //          --> HS_SERVER_HELLO_DONE
+            //              (Client ClientKeyExchange Flight)
+            //          --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
+            //          --> HS_CLIENT_CERTIFICATE or
+            //              HS_CERTIFICATE_URL
+            //          --> HS_CLIENT_KEY_EXCHANGE
+            //          --> HS_CERTIFICATE_VERIFY [optional]
+            //          --> HS_CLIENT_CHANGE_CIPHER_SPEC
+            //          --> HS_CLEINT_FINISHED
+            //              (Server Finished Flight)
+            //          --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
+            //
+            //          Abbreviated handshake state stacks
+            //              (Server Finished Flight)
+            //              HS_NEW_SESSION_TICKET
+            //          --> HS_SERVER_CHANGE_CIPHER_SPEC
+            //          --> HS_SERVER_FINISHDE
+            //              (Client Finished Flight)
+            //          --> HS_CLIENT_CHANGE_CIPHER_SPEC
+            //          --> HS_CLEINT_FINISHED
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            if (!upcomingStates.isEmpty()) {
+                // The current state should be HS_SERVER_HELLO
+                HandshakeState handshakeState = upcomingStates.pop();
+                HandshakeState alternative = null;
+                if (!alternatives.isEmpty()) {
+                    alternative = alternatives.pop();
+                }
+
+                if ((handshakeState != HS_SERVER_HELLO) &&
+                        (alternative != HS_SERVER_HELLO)) {
+
+                    throw new SSLProtocolException(exceptionMsg);
+                }
+            } else {
+                // No present state.
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // Add the upcoming states.
+            ServerHello serverHello = (ServerHello)handshakeMessage;
+            HelloExtensions hes = serverHello.extensions;
+
+
+            // Not support SessionTicket extension yet.
+            //
+            // boolean hasSessionTicketExt =
+            //     (hes.get(HandshakeMessage.ht_new_session_ticket) != null);
+
+            if (isAbbreviated) {
+                // Not support SessionTicket extension yet.
+                //
+                // // Mandatory NewSessionTicket message
+                // if (hasSessionTicketExt) {
+                //     upcomingStates.add(HS_NEW_SESSION_TICKET);
+                // }
+
+                // Mandatory server ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_SERVER_FINISHDE);
+
+                // Mandatory client ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_CLEINT_FINISHED);
+            } else {
+                // Not support SupplementalData extension yet.
+                //
+                // boolean hasSupplementalDataExt =
+                //     (hes.get(HandshakeMessage.ht_supplemental_data) != null);
+
+                // Not support CertificateStatus extension yet.
+                //
+                // boolean hasCertificateStatusExt =
+                //    (hes.get(HandshakeMessage.ht_certificate_status) != null);
+
+                // Not support CertificateURL extension yet.
+                //
+                // boolean hasCertificateUrlExt =
+                //     (hes.get(HandshakeMessage.ht_certificate_url) != null);
+
+                // Not support SupplementalData extension yet.
+                //
+                // // Optional SupplementalData message
+                // if (hasSupplementalDataExt) {
+                //     upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA);
+                // }
+
+                // Need server Certificate message or not?
+                KeyExchange keyExchange = serverHello.cipherSuite.keyExchange;
+                if ((keyExchange != K_KRB5) &&
+                        (keyExchange != K_KRB5_EXPORT) &&
+                        (keyExchange != K_DH_ANON) &&
+                        (keyExchange != K_ECDH_ANON)) {
+                    // Mandatory Certificate message
+                    upcomingStates.add(HS_SERVER_CERTIFICATE);
+                }
+
+                // Not support CertificateStatus extension yet.
+                //
+                // // Optional CertificateStatus message
+                // if (hasCertificateStatusExt) {
+                //     upcomingStates.add(HS_CERTIFICATE_STATUS);
+                // }
+
+                // Need ServerKeyExchange message or not?
+                if ((keyExchange == K_RSA_EXPORT) ||
+                        (keyExchange == K_DHE_RSA) ||
+                        (keyExchange == K_DHE_DSS) ||
+                        (keyExchange == K_DH_ANON) ||
+                        (keyExchange == K_ECDHE_RSA) ||
+                        (keyExchange == K_ECDHE_ECDSA) ||
+                        (keyExchange == K_ECDH_ANON)) {
+                    // Optional ServerKeyExchange message
+                    upcomingStates.add(HS_SERVER_KEY_EXCHANGE);
+                }
+
+                // Optional CertificateRequest message
+                upcomingStates.add(HS_CERTIFICATE_REQUEST);
+
+                // Mandatory ServerHelloDone message
+                upcomingStates.add(HS_SERVER_HELLO_DONE);
+
+                // Not support SupplementalData extension yet.
+                //
+                // // Optional SupplementalData message
+                // if (hasSupplementalDataExt) {
+                //     upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA);
+                // }
+
+                // Optional client Certificate message
+                upcomingStates.add(HS_CLIENT_CERTIFICATE);
+
+                // Not support CertificateURL extension yet.
+                //
+                // // Alternative CertificateURL message, optional too.
+                // //
+                // // Please put CertificateURL rather than Certificate
+                // // message in the alternatives list.  So that we can
+                // // simplify the process of this alternative pair later.
+                // if (hasCertificateUrlExt) {
+                //     alternatives.add(HS_CERTIFICATE_URL);
+                // }
+
+                // Mandatory ClientKeyExchange message
+                upcomingStates.add(HS_CLIENT_KEY_EXCHANGE);
+
+                // Optional CertificateVerify message
+                upcomingStates.add(HS_CERTIFICATE_VERIFY);
+
+                // Mandatory client ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_CLEINT_FINISHED);
+
+                // Not support SessionTicket extension yet.
+                //
+                // // Mandatory NewSessionTicket message
+                // if (hasSessionTicketExt) {
+                //     upcomingStates.add(HS_NEW_SESSION_TICKET);
+                // }
+
+                // Mandatory server ChangeCipherSpec and Finished messages
+                upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
+                upcomingStates.add(HS_SERVER_FINISHDE);
+            }
+
+            break;
+
+        case HandshakeMessage.ht_certificate:
+            //
+            // State machine:
+            //     PRESENT: HS_CERTIFICATE_URL or
+            //              HS_CLIENT_CERTIFICATE
+            //        TO  : HS_CLIENT_KEY_EXCHANGE
+            //
+            //     Or
+            //
+            //     PRESENT: HS_SERVER_CERTIFICATE
+            //        TO  : HS_CERTIFICATE_STATUS [optional]
+            //              HS_SERVER_KEY_EXCHANGE [optional]
+            //              HS_CERTIFICATE_REQUEST [optional]
+            //              HS_SERVER_HELLO_DONE
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            while (!upcomingStates.isEmpty()) {
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState.handshakeType == handshakeType) {
+                    hasPresentState = true;
+
+                    // The current state should be HS_CLIENT_CERTIFICATE or
+                    // HS_SERVER_CERTIFICATE.
+                    //
+                    // Note that we won't put HS_CLIENT_CERTIFICATE into
+                    // the alternative list.
+                    if ((handshakeState != HS_CLIENT_CERTIFICATE) &&
+                            (handshakeState != HS_SERVER_CERTIFICATE)) {
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+
+                    // Is it an expected client Certificate message?
+                    boolean isClientMessage = false;
+                    if (!upcomingStates.isEmpty()) {
+                        // If the next expected message is ClientKeyExchange,
+                        // this one should be an expected client Certificate
+                        // message.
+                        HandshakeState nextState = upcomingStates.getFirst();
+                        if (nextState == HS_CLIENT_KEY_EXCHANGE) {
+                            isClientMessage = true;
+                        }
+                    }
+
+                    if (isClientMessage) {
+                        if (handshakeState != HS_CLIENT_CERTIFICATE) {
+                            throw new SSLProtocolException(exceptionMsg);
+                        }
+
+                        // Not support CertificateURL extension yet.
+                        /*******************************************
+                        // clear up the alternatives list
+                        if (!alternatives.isEmpty()) {
+                            HandshakeState alternative = alternatives.pop();
+
+                            if (alternative != HS_CERTIFICATE_URL) {
+                                throw new SSLProtocolException(exceptionMsg);
+                            }
+                        }
+                        ********************************************/
+                    } else {
+                        if ((handshakeState != HS_SERVER_CERTIFICATE)) {
+                            throw new SSLProtocolException(exceptionMsg);
+                        }
+                    }
+
+                    break;
+                } else if (!handshakeState.isOptional) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }   // Otherwise, looking for next state track.
+            }
+
+            // No present state.
+            if (!hasPresentState) {
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // no new upcoming states.
+
+            break;
+
+        // Not support CertificateURL extension yet.
+        /*************************************************/
+        case HandshakeMessage.ht_certificate_url:
+            //
+            // State machine:
+            //     PRESENT: HS_CERTIFICATE_URL or
+            //              HS_CLIENT_CERTIFICATE
+            //        TO  : HS_CLIENT_KEY_EXCHANGE
+            //
+            // Note that this state may have an alternative option.
+
+            // Check and update the present state.
+            while (!upcomingStates.isEmpty()) {
+                // The current state should be HS_CLIENT_CERTIFICATE.
+                //
+                // Note that we won't put HS_CLIENT_CERTIFICATE into
+                // the alternative list.
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState.handshakeType ==
+                        HS_CLIENT_CERTIFICATE.handshakeType) {
+                    hasPresentState = true;
+
+                    // Look for HS_CERTIFICATE_URL state track.
+                    if (!alternatives.isEmpty()) {
+                        HandshakeState alternative = alternatives.pop();
+
+                        if (alternative != HS_CERTIFICATE_URL) {
+                            throw new SSLProtocolException(exceptionMsg);
+                        }
+                    } else {
+                        // No alternative CertificateUR state track.
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+
+                    if ((handshakeState != HS_CLIENT_CERTIFICATE)) {
+                        throw new SSLProtocolException(exceptionMsg);
+                    }
+
+                    break;
+                } else if (!handshakeState.isOptional) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }   // Otherwise, looking for next state track.
+
+            }
+
+            // No present state.
+            if (!hasPresentState) {
+                // No present state.
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // no new upcoming states.
+
+            break;
+        /*************************************************/
+
+        default:
+            // Check and update the present state.
+            while (!upcomingStates.isEmpty()) {
+                HandshakeState handshakeState = upcomingStates.pop();
+                if (handshakeState.handshakeType == handshakeType) {
+                    hasPresentState = true;
+                    break;
+                } else if (!handshakeState.isOptional) {
+                    throw new SSLProtocolException(exceptionMsg);
+                }   // Otherwise, looking for next state track.
+            }
+
+            // No present state.
+            if (!hasPresentState) {
+                throw new SSLProtocolException(exceptionMsg);
+            }
+
+            // no new upcoming states.
+        }
+
+        if (debugIsOn) {
+            for (HandshakeState handshakeState : upcomingStates) {
+                System.out.println(
+                    "upcoming handshake states: " + handshakeState);
+            }
+            for (HandshakeState handshakeState : alternatives) {
+                System.out.println(
+                    "upcoming handshake alternative state: " + handshakeState);
+            }
+        }
+    }
+
+    void changeCipherSpec(boolean isInput,
+            boolean isClient) throws SSLProtocolException {
+
+        if (debugIsOn) {
+            System.out.println(
+                    "update handshake state: change_cipher_spec");
+        }
+
+        String exceptionMsg = "ChangeCipherSpec message sequence violation";
+
+        HandshakeState expectedState;
+        if ((isClient && isInput) || (!isClient && !isInput)) {
+            expectedState = HS_SERVER_CHANGE_CIPHER_SPEC;
+        } else {
+            expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC;
+        }
+
+        boolean hasPresentState = false;
+
+        // Check and update the present state.
+        while (!upcomingStates.isEmpty()) {
+            HandshakeState handshakeState = upcomingStates.pop();
+            if (handshakeState == expectedState) {
+                hasPresentState = true;
+                break;
+            } else if (!handshakeState.isOptional) {
+                throw new SSLProtocolException(exceptionMsg);
+            }   // Otherwise, looking for next state track.
+        }
+
+        // No present state.
+        if (!hasPresentState) {
+            throw new SSLProtocolException(exceptionMsg);
+        }
+
+        // no new upcoming states.
+
+        if (debugIsOn) {
+            for (HandshakeState handshakeState : upcomingStates) {
+                System.out.println(
+                    "upcoming handshake states: " + handshakeState);
+            }
+            for (HandshakeState handshakeState : alternatives) {
+                System.out.println(
+                    "upcoming handshake alternative state: " + handshakeState);
+            }
+        }
+    }
+
+    private static String toString(byte handshakeType) {
+        String s = handshakeTypes.get(handshakeType);
+        if (s == null) {
+            s = "unknown";
+        }
+        return (s + "[" + handshakeType + "]");
+    }
+}
+