--- /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 + "]");
+ }
+}
+