| /* |
| * 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.Collections; |
| import java.util.List; |
| import java.util.LinkedList; |
| import java.util.HashMap; |
| import javax.net.ssl.SSLProtocolException; |
| |
| 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 static final boolean debugIsOn; |
| |
| private static final 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_FINISHED( |
| "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(); |
| } |
| |
| List<Byte> check(byte handshakeType) throws SSLProtocolException { |
| List<Byte> ignoredOptional = new LinkedList<>(); |
| 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 Collections.emptyList(); |
| } |
| |
| // Ignore the checking for HelloRequest messages as they |
| // may be sent by the server at any time. |
| if (handshakeType == HandshakeMessage.ht_hello_request) { |
| return Collections.emptyList(); |
| } |
| |
| for (HandshakeState handshakeState : upcomingStates) { |
| if (handshakeState.handshakeType == handshakeType) { |
| // It's the expected next handshake type. |
| return ignoredOptional; |
| } |
| |
| if (handshakeState.isOptional) { |
| ignoredOptional.add(handshakeState.handshakeType); |
| continue; |
| } else { |
| for (HandshakeState alternative : alternatives) { |
| if (alternative.handshakeType == handshakeType) { |
| return ignoredOptional; |
| } |
| |
| 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_FINISHED |
| // (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_FINISHED); |
| |
| // 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 CertificateURL extension yet. |
| // |
| // boolean hasCertificateUrlExt = |
| // (hes.get(ExtensionType EXT_CLIENT_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); |
| } |
| |
| // Optional CertificateStatus message |
| if (hes.get(ExtensionType.EXT_STATUS_REQUEST) != null || |
| hes.get(ExtensionType.EXT_STATUS_REQUEST_V2) != null) { |
| 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_FINISHED); |
| } |
| |
| 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 + "]"); |
| } |
| } |
| |