提交 40f676c5 编写于 作者: P pkoppula

8074462: Handshake messages can be strictly ordered

Reviewed-by: xuelei
Contributed-by: prasadarao.koppula@oracle.com, sean.coffey@oracle.com
上级 1f86a9b8
...@@ -191,19 +191,24 @@ final class ClientHandshaker extends Handshaker { ...@@ -191,19 +191,24 @@ final class ClientHandshaker extends Handshaker {
*/ */
@Override @Override
void processMessage(byte type, int messageLen) throws IOException { void processMessage(byte type, int messageLen) throws IOException {
if (state >= type
&& (type != HandshakeMessage.ht_hello_request)) { // check the handshake state
throw new SSLProtocolException( List<Byte> ignoredOptStates = handshakeState.check(type);
"Handshake message sequence violation, " + type);
}
switch (type) { switch (type) {
case HandshakeMessage.ht_hello_request: case HandshakeMessage.ht_hello_request:
this.serverHelloRequest(new HelloRequest(input)); HelloRequest helloRequest = new HelloRequest(input);
handshakeState.update(helloRequest, resumingSession);
this.serverHelloRequest(helloRequest);
break; break;
case HandshakeMessage.ht_server_hello: case HandshakeMessage.ht_server_hello:
this.serverHello(new ServerHello(input, messageLen)); ServerHello serverHello = new ServerHello(input, messageLen);
this.serverHello(serverHello);
// This handshake state update needs the resumingSession value
// set by serverHello().
handshakeState.update(serverHello, resumingSession);
break; break;
case HandshakeMessage.ht_certificate: case HandshakeMessage.ht_certificate:
...@@ -213,7 +218,9 @@ final class ClientHandshaker extends Handshaker { ...@@ -213,7 +218,9 @@ final class ClientHandshaker extends Handshaker {
"unexpected server cert chain"); "unexpected server cert chain");
// NOTREACHED // NOTREACHED
} }
this.serverCertificate(new CertificateMsg(input)); CertificateMsg certificateMsg = new CertificateMsg(input);
handshakeState.update(certificateMsg, resumingSession);
this.serverCertificate(certificateMsg);
serverKey = serverKey =
session.getPeerCertificates()[0].getPublicKey(); session.getPeerCertificates()[0].getPublicKey();
break; break;
...@@ -249,15 +256,20 @@ final class ClientHandshaker extends Handshaker { ...@@ -249,15 +256,20 @@ final class ClientHandshaker extends Handshaker {
} }
try { try {
this.serverKeyExchange(new RSA_ServerKeyExchange(input)); RSA_ServerKeyExchange rsaSrvKeyExchange =
new RSA_ServerKeyExchange(input);
handshakeState.update(rsaSrvKeyExchange, resumingSession);
this.serverKeyExchange(rsaSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throwSSLException("Server key", e);
} }
break; break;
case K_DH_ANON: case K_DH_ANON:
try { try {
this.serverKeyExchange(new DH_ServerKeyExchange( DH_ServerKeyExchange dhSrvKeyExchange =
input, protocolVersion)); new DH_ServerKeyExchange(input, protocolVersion);
handshakeState.update(dhSrvKeyExchange, resumingSession);
this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throwSSLException("Server key", e);
} }
...@@ -265,11 +277,14 @@ final class ClientHandshaker extends Handshaker { ...@@ -265,11 +277,14 @@ final class ClientHandshaker extends Handshaker {
case K_DHE_DSS: case K_DHE_DSS:
case K_DHE_RSA: case K_DHE_RSA:
try { try {
this.serverKeyExchange(new DH_ServerKeyExchange( DH_ServerKeyExchange dhSrvKeyExchange =
new DH_ServerKeyExchange(
input, serverKey, input, serverKey,
clnt_random.random_bytes, svr_random.random_bytes, clnt_random.random_bytes, svr_random.random_bytes,
messageLen, messageLen,
getLocalSupportedSignAlgs(), protocolVersion)); getLocalSupportedSignAlgs(), protocolVersion);
handshakeState.update(dhSrvKeyExchange, resumingSession);
this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throwSSLException("Server key", e);
} }
...@@ -278,10 +293,13 @@ final class ClientHandshaker extends Handshaker { ...@@ -278,10 +293,13 @@ final class ClientHandshaker extends Handshaker {
case K_ECDHE_RSA: case K_ECDHE_RSA:
case K_ECDH_ANON: case K_ECDH_ANON:
try { try {
this.serverKeyExchange(new ECDH_ServerKeyExchange ECDH_ServerKeyExchange ecdhSrvKeyExchange =
new ECDH_ServerKeyExchange
(input, serverKey, clnt_random.random_bytes, (input, serverKey, clnt_random.random_bytes,
svr_random.random_bytes, svr_random.random_bytes,
getLocalSupportedSignAlgs(), protocolVersion)); getLocalSupportedSignAlgs(), protocolVersion);
handshakeState.update(ecdhSrvKeyExchange, resumingSession);
this.serverKeyExchange(ecdhSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throwSSLException("Server key", e);
} }
...@@ -320,6 +338,7 @@ final class ClientHandshaker extends Handshaker { ...@@ -320,6 +338,7 @@ final class ClientHandshaker extends Handshaker {
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
certRequest.print(System.out); certRequest.print(System.out);
} }
handshakeState.update(certRequest, resumingSession);
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
Collection<SignatureAndHashAlgorithm> peerSignAlgs = Collection<SignatureAndHashAlgorithm> peerSignAlgs =
...@@ -345,33 +364,23 @@ final class ClientHandshaker extends Handshaker { ...@@ -345,33 +364,23 @@ final class ClientHandshaker extends Handshaker {
break; break;
case HandshakeMessage.ht_server_hello_done: case HandshakeMessage.ht_server_hello_done:
this.serverHelloDone(new ServerHelloDone(input)); ServerHelloDone serverHelloDone = new ServerHelloDone(input);
handshakeState.update(serverHelloDone, resumingSession);
this.serverHelloDone(serverHelloDone);
break; break;
case HandshakeMessage.ht_finished: case HandshakeMessage.ht_finished:
// A ChangeCipherSpec record must have been received prior to Finished serverFinished =
// reception of the Finished message (RFC 5246, 7.4.9). new Finished(protocolVersion, input, cipherSuite);
if (!receivedChangeCipherSpec()) { handshakeState.update(serverFinished, resumingSession);
fatalSE(Alerts.alert_handshake_failure, this.serverFinished(serverFinished);
"Received Finished message before ChangeCipherSpec");
}
this.serverFinished(
new Finished(protocolVersion, input, cipherSuite));
break; break;
default: default:
throw new SSLProtocolException( throw new SSLProtocolException(
"Illegal client handshake msg, " + type); "Illegal client handshake msg, " + type);
} }
//
// Move state machine forward if the message handling
// code didn't already do so
//
if (state < type) {
state = type;
}
} }
/* /*
...@@ -389,7 +398,7 @@ final class ClientHandshaker extends Handshaker { ...@@ -389,7 +398,7 @@ final class ClientHandshaker extends Handshaker {
// Could be (e.g. at connection setup) that we already // Could be (e.g. at connection setup) that we already
// sent the "client hello" but the server's not seen it. // sent the "client hello" but the server's not seen it.
// //
if (state < HandshakeMessage.ht_client_hello) { if (!clientHelloDelivered) {
if (!secureRenegotiation && !allowUnsafeRenegotiation) { if (!secureRenegotiation && !allowUnsafeRenegotiation) {
// renegotiation is not allowed. // renegotiation is not allowed.
if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
...@@ -613,7 +622,6 @@ final class ClientHandshaker extends Handshaker { ...@@ -613,7 +622,6 @@ final class ClientHandshaker extends Handshaker {
// looks fine; resume it, and update the state machine. // looks fine; resume it, and update the state machine.
resumingSession = true; resumingSession = true;
state = HandshakeMessage.ht_finished - 1;
calculateConnectionKeys(session.getMasterSecret()); calculateConnectionKeys(session.getMasterSecret());
if (debug != null && Debug.isOn("session")) { if (debug != null && Debug.isOn("session")) {
System.out.println("%% Server resumed " + session); System.out.println("%% Server resumed " + session);
...@@ -903,6 +911,7 @@ final class ClientHandshaker extends Handshaker { ...@@ -903,6 +911,7 @@ final class ClientHandshaker extends Handshaker {
m1.print(System.out); m1.print(System.out);
} }
m1.write(output); m1.write(output);
handshakeState.update(m1, resumingSession);
} }
} }
...@@ -1067,6 +1076,7 @@ final class ClientHandshaker extends Handshaker { ...@@ -1067,6 +1076,7 @@ final class ClientHandshaker extends Handshaker {
} }
m2.write(output); m2.write(output);
handshakeState.update(m2, resumingSession);
/* /*
* THIRD, send a "change_cipher_spec" record followed by the * THIRD, send a "change_cipher_spec" record followed by the
...@@ -1169,6 +1179,7 @@ final class ClientHandshaker extends Handshaker { ...@@ -1169,6 +1179,7 @@ final class ClientHandshaker extends Handshaker {
m3.print(System.out); m3.print(System.out);
} }
m3.write(output); m3.write(output);
handshakeState.update(m3, resumingSession);
output.doHashes(); output.doHashes();
} }
...@@ -1226,6 +1237,8 @@ final class ClientHandshaker extends Handshaker { ...@@ -1226,6 +1237,8 @@ final class ClientHandshaker extends Handshaker {
if (resumingSession) { if (resumingSession) {
input.digestNow(); input.digestNow();
sendChangeCipherAndFinish(true); sendChangeCipherAndFinish(true);
} else {
handshakeFinished = true;
} }
session.setLastAccessedTime(System.currentTimeMillis()); session.setLastAccessedTime(System.currentTimeMillis());
...@@ -1271,13 +1284,6 @@ final class ClientHandshaker extends Handshaker { ...@@ -1271,13 +1284,6 @@ final class ClientHandshaker extends Handshaker {
if (secureRenegotiation) { if (secureRenegotiation) {
clientVerifyData = mesg.getVerifyData(); clientVerifyData = mesg.getVerifyData();
} }
/*
* Update state machine so server MUST send 'finished' next.
* (In "long" handshake case; in short case, we're responding
* to its message.)
*/
state = HandshakeMessage.ht_finished - 1;
} }
......
...@@ -89,6 +89,8 @@ public abstract class HandshakeMessage { ...@@ -89,6 +89,8 @@ public abstract class HandshakeMessage {
static final byte ht_finished = 20; static final byte ht_finished = 20;
static final byte ht_not_applicable = -1; // N/A
/* Class and subclass dynamic debugging support */ /* Class and subclass dynamic debugging support */
public static final Debug debug = Debug.getInstance("ssl"); public static final Debug debug = Debug.getInstance("ssl");
......
/*
* 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) |
* | --------------------------------------------> |
* | |
* 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 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<>(8);
handshakeTypes.put(ht_hello_request, "hello_request");
handshakeTypes.put(ht_client_hello, "client_hello");
handshakeTypes.put(ht_server_hello, "server_hello");
handshakeTypes.put(ht_certificate, "certificate");
handshakeTypes.put(ht_server_key_exchange, "server_key_exchange");
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");
}
HandshakeStateManager() {
this.upcomingStates = new LinkedList<>();
this.alternatives = new LinkedList<>();
}
//
// 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_SERVER_HELLO(
"server_hello",
HandshakeMessage.ht_server_hello),
HS_SERVER_CERTIFICATE(
"server certificate",
HandshakeMessage.ht_certificate),
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_CERTIFICATE(
"client certificate",
HandshakeMessage.ht_certificate, 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_CLIENT_FINISHED(
"client finished",
HandshakeMessage.ht_finished),
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_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;
upcomingStates.add(HS_SERVER_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_CLIENT_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_CLIENT_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_CLIENT_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);
}
// 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_CLIENT_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;
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 + "]");
}
}
...@@ -118,10 +118,14 @@ abstract class Handshaker { ...@@ -118,10 +118,14 @@ abstract class Handshaker {
HandshakeHash handshakeHash; HandshakeHash handshakeHash;
HandshakeInStream input; HandshakeInStream input;
HandshakeOutStream output; HandshakeOutStream output;
int state;
SSLContextImpl sslContext; SSLContextImpl sslContext;
RandomCookie clnt_random, svr_random; RandomCookie clnt_random, svr_random;
SSLSessionImpl session; SSLSessionImpl session;
HandshakeStateManager handshakeState;
boolean clientHelloDelivered;
boolean serverHelloRequested;
boolean handshakeActivated;
boolean handshakeFinished;
// current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
CipherSuite cipherSuite; CipherSuite cipherSuite;
...@@ -135,10 +139,6 @@ abstract class Handshaker { ...@@ -135,10 +139,6 @@ abstract class Handshaker {
// True if it's OK to start a new SSL session // True if it's OK to start a new SSL session
boolean enableNewSession; boolean enableNewSession;
// True if session keys have been calculated and the caller may receive
// and process a ChangeCipherSpec message
private boolean sessKeysCalculated;
// Whether local cipher suites preference should be honored during // Whether local cipher suites preference should be honored during
// handshaking? // handshaking?
// //
...@@ -277,9 +277,13 @@ abstract class Handshaker { ...@@ -277,9 +277,13 @@ abstract class Handshaker {
this.secureRenegotiation = secureRenegotiation; this.secureRenegotiation = secureRenegotiation;
this.clientVerifyData = clientVerifyData; this.clientVerifyData = clientVerifyData;
this.serverVerifyData = serverVerifyData; this.serverVerifyData = serverVerifyData;
enableNewSession = true; this.enableNewSession = true;
invalidated = false; this.invalidated = false;
sessKeysCalculated = false; this.handshakeState = new HandshakeStateManager();
this.clientHelloDelivered = false;
this.serverHelloRequested = false;
this.handshakeActivated = false;
this.handshakeFinished = false;
setCipherSuite(CipherSuite.C_NULL); setCipherSuite(CipherSuite.C_NULL);
setEnabledProtocols(enabledProtocols); setEnabledProtocols(enabledProtocols);
...@@ -289,22 +293,6 @@ abstract class Handshaker { ...@@ -289,22 +293,6 @@ abstract class Handshaker {
} else { // engine != null } else { // engine != null
algorithmConstraints = new SSLAlgorithmConstraints(engine, true); algorithmConstraints = new SSLAlgorithmConstraints(engine, true);
} }
//
// In addition to the connection state machine, controlling
// how the connection deals with the different sorts of records
// that get sent (notably handshake transitions!), there's
// also a handshaking state machine that controls message
// sequencing.
//
// It's a convenient artifact of the protocol that this can,
// with only a couple of minor exceptions, be driven by the
// type constant for the last message seen: except for the
// client's cert verify, those constants are in a convenient
// order to drastically simplify state machine checking.
//
state = -2; // initialized but not activated
} }
/* /*
...@@ -386,14 +374,6 @@ abstract class Handshaker { ...@@ -386,14 +374,6 @@ abstract class Handshaker {
} }
} }
final boolean receivedChangeCipherSpec() {
if (conn != null) {
return conn.receivedChangeCipherSpec();
} else {
return engine.receivedChangeCipherSpec();
}
}
String getEndpointIdentificationAlgorithmSE() { String getEndpointIdentificationAlgorithmSE() {
SSLParameters paras; SSLParameters paras;
if (conn != null) { if (conn != null) {
...@@ -573,8 +553,7 @@ abstract class Handshaker { ...@@ -573,8 +553,7 @@ abstract class Handshaker {
engine.outputRecord.setHelloVersion(helloVersion); engine.outputRecord.setHelloVersion(helloVersion);
} }
// move state to activated handshakeActivated = true;
state = -1;
} }
/** /**
...@@ -919,10 +898,9 @@ abstract class Handshaker { ...@@ -919,10 +898,9 @@ abstract class Handshaker {
* this freshly created session can become the current one. * this freshly created session can become the current one.
*/ */
boolean isDone() { boolean isDone() {
return state == HandshakeMessage.ht_finished; return started() && handshakeState.isEmpty() && handshakeFinished;
} }
/* /*
* Returns the session which was created through this * Returns the session which was created through this
* handshake sequence ... should be called after isDone() * handshake sequence ... should be called after isDone()
...@@ -1028,6 +1006,13 @@ abstract class Handshaker { ...@@ -1028,6 +1006,13 @@ abstract class Handshaker {
return; return;
} }
// Set the flags in the message receiving side.
if (messageType == HandshakeMessage.ht_client_hello) {
clientHelloDelivered = true;
} else if (messageType == HandshakeMessage.ht_hello_request) {
serverHelloRequested = true;
}
/* /*
* Process the message. We require * Process the message. We require
* that processMessage() consumes the entire message. In * that processMessage() consumes the entire message. In
...@@ -1062,15 +1047,14 @@ abstract class Handshaker { ...@@ -1062,15 +1047,14 @@ abstract class Handshaker {
* In activated state, the handshaker may not send any messages out. * In activated state, the handshaker may not send any messages out.
*/ */
boolean activated() { boolean activated() {
return state >= -1; return handshakeActivated;
} }
/** /**
* Returns true iff the handshaker has sent any messages. * Returns true iff the handshaker has sent any messages.
*/ */
boolean started() { boolean started() {
return state >= 0; // 0: HandshakeMessage.ht_hello_request return (serverHelloRequested || clientHelloDelivered);
// 1: HandshakeMessage.ht_client_hello
} }
...@@ -1080,11 +1064,13 @@ abstract class Handshaker { ...@@ -1080,11 +1064,13 @@ abstract class Handshaker {
* the subclass returns. NOP if handshaking's already started. * the subclass returns. NOP if handshaking's already started.
*/ */
void kickstart() throws IOException { void kickstart() throws IOException {
if (state >= 0) { if ((isClient && clientHelloDelivered) ||
(!isClient && serverHelloRequested)) {
return; return;
} }
HandshakeMessage m = getKickstartMessage(); HandshakeMessage m = getKickstartMessage();
handshakeState.update(m, resumingSession);
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
m.print(System.out); m.print(System.out);
...@@ -1092,7 +1078,14 @@ abstract class Handshaker { ...@@ -1092,7 +1078,14 @@ abstract class Handshaker {
m.write(output); m.write(output);
output.flush(); output.flush();
state = m.messageType(); // Set the flags in the message delivering side.
int handshakeType = m.messageType();
if (handshakeType == HandshakeMessage.ht_hello_request) {
serverHelloRequested = true;
} else { // HandshakeMessage.ht_client_hello
clientHelloDelivered = true;
}
} }
/** /**
...@@ -1124,16 +1117,6 @@ abstract class Handshaker { ...@@ -1124,16 +1117,6 @@ abstract class Handshaker {
output.flush(); // i.e. handshake data output.flush(); // i.e. handshake data
/*
* The write cipher state is protected by the connection write lock
* so we must grab it while making the change. We also
* make sure no writes occur between sending the ChangeCipherSpec
* message, installing the new cipher state, and sending the
* Finished message.
*
* We already hold SSLEngine/SSLSocket "this" by virtue
* of this being called from the readRecord code.
*/
OutputRecord r; OutputRecord r;
if (conn != null) { if (conn != null) {
r = new OutputRecord(Record.ct_change_cipher_spec); r = new OutputRecord(Record.ct_change_cipher_spec);
...@@ -1144,14 +1127,27 @@ abstract class Handshaker { ...@@ -1144,14 +1127,27 @@ abstract class Handshaker {
r.setVersion(protocolVersion); r.setVersion(protocolVersion);
r.write(1); // single byte of data r.write(1); // single byte of data
/*
* The write cipher state is protected by the connection write lock
* so we must grab it while making the change. We also
* make sure no writes occur between sending the ChangeCipherSpec
* message, installing the new cipher state, and sending the
* Finished message.
*
* We already hold SSLEngine/SSLSocket "this" by virtue
* of this being called from the readRecord code.
*/
if (conn != null) { if (conn != null) {
conn.writeLock.lock(); conn.writeLock.lock();
try { try {
handshakeState.changeCipherSpec(false, isClient);
conn.writeRecord(r); conn.writeRecord(r);
conn.changeWriteCiphers(); conn.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out); mesg.print(System.out);
} }
handshakeState.update(mesg, resumingSession);
mesg.write(output); mesg.write(output);
output.flush(); output.flush();
} finally { } finally {
...@@ -1159,19 +1155,28 @@ abstract class Handshaker { ...@@ -1159,19 +1155,28 @@ abstract class Handshaker {
} }
} else { } else {
synchronized (engine.writeLock) { synchronized (engine.writeLock) {
handshakeState.changeCipherSpec(false, isClient);
engine.writeRecord((EngineOutputRecord)r); engine.writeRecord((EngineOutputRecord)r);
engine.changeWriteCiphers(); engine.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out); mesg.print(System.out);
} }
mesg.write(output);
handshakeState.update(mesg, resumingSession);
mesg.write(output);
if (lastMessage) { if (lastMessage) {
output.setFinishedMsg(); output.setFinishedMsg();
} }
output.flush(); output.flush();
} }
} }
if (lastMessage) {
handshakeFinished = true;
}
}
void receiveChangeCipherSpec() throws IOException {
handshakeState.changeCipherSpec(true, isClient);
} }
/* /*
...@@ -1353,10 +1358,6 @@ abstract class Handshaker { ...@@ -1353,10 +1358,6 @@ abstract class Handshaker {
throw new ProviderException(e); throw new ProviderException(e);
} }
// Mark a flag that allows outside entities (like SSLSocket/SSLEngine)
// determine if a ChangeCipherSpec message could be processed.
sessKeysCalculated = true;
// //
// Dump the connection keys as they're generated. // Dump the connection keys as they're generated.
// //
...@@ -1411,15 +1412,6 @@ abstract class Handshaker { ...@@ -1411,15 +1412,6 @@ abstract class Handshaker {
} }
} }
/**
* Return whether or not the Handshaker has derived session keys for
* this handshake. This is used for determining readiness to process
* an incoming ChangeCipherSpec message.
*/
boolean sessionKeysCalculated() {
return sessKeysCalculated;
}
private static void printHex(HexDumpEncoder dump, byte[] bytes) { private static void printHex(HexDumpEncoder dump, byte[] bytes) {
if (bytes == null) { if (bytes == null) {
System.out.println("(key bytes not available)"); System.out.println("(key bytes not available)");
......
...@@ -211,11 +211,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -211,11 +211,6 @@ final public class SSLEngineImpl extends SSLEngine {
static final byte clauth_requested = 1; static final byte clauth_requested = 1;
static final byte clauth_required = 2; static final byte clauth_required = 2;
/*
* Flag indicating that the engine has received a ChangeCipherSpec message.
*/
private boolean receivedCCS;
/* /*
* Flag indicating if the next record we receive MUST be a Finished * Flag indicating if the next record we receive MUST be a Finished
* message. Temporarily set during the handshake to ensure that * message. Temporarily set during the handshake to ensure that
...@@ -377,7 +372,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -377,7 +372,6 @@ final public class SSLEngineImpl extends SSLEngine {
*/ */
roleIsServer = true; roleIsServer = true;
connectionState = cs_START; connectionState = cs_START;
receivedCCS = false;
// default server name indication // default server name indication
serverNames = serverNames =
...@@ -525,7 +519,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -525,7 +519,6 @@ final public class SSLEngineImpl extends SSLEngine {
return HandshakeStatus.NEED_UNWRAP; return HandshakeStatus.NEED_UNWRAP;
} // else not handshaking } // else not handshaking
} }
return HandshakeStatus.NOT_HANDSHAKING; return HandshakeStatus.NOT_HANDSHAKING;
} }
} }
...@@ -587,14 +580,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -587,14 +580,6 @@ final public class SSLEngineImpl extends SSLEngine {
* Synchronized on "this" from readRecord. * Synchronized on "this" from readRecord.
*/ */
private void changeReadCiphers() throws SSLException { private void changeReadCiphers() throws SSLException {
if (connectionState != cs_HANDSHAKE
&& connectionState != cs_RENEGOTIATE) {
throw new SSLProtocolException(
"State error, change cipher specs");
}
// ... create decompressor
CipherBox oldCipher = readCipher; CipherBox oldCipher = readCipher;
try { try {
...@@ -780,6 +765,10 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -780,6 +765,10 @@ final public class SSLEngineImpl extends SSLEngine {
synchronized (unwrapLock) { synchronized (unwrapLock) {
return readNetRecord(ea); return readNetRecord(ea);
} }
} catch (SSLProtocolException spe) {
// may be an unexpected handshake message
fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
return null; // make compiler happy
} catch (Exception e) { } catch (Exception e) {
/* /*
* Don't reset position so it looks like we didn't * Don't reset position so it looks like we didn't
...@@ -1004,6 +993,7 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1004,6 +993,7 @@ final public class SSLEngineImpl extends SSLEngine {
* session (new keys exchanged) with just this connection * session (new keys exchanged) with just this connection
* in it. * in it.
*/ */
initHandshaker(); initHandshaker();
if (!handshaker.activated()) { if (!handshaker.activated()) {
// prior to handshaking, activate the handshake // prior to handshaking, activate the handshake
...@@ -1027,7 +1017,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1027,7 +1017,6 @@ final public class SSLEngineImpl extends SSLEngine {
if (handshaker.invalidated) { if (handshaker.invalidated) {
handshaker = null; handshaker = null;
receivedCCS = false;
// if state is cs_RENEGOTIATE, revert it to cs_DATA // if state is cs_RENEGOTIATE, revert it to cs_DATA
if (connectionState == cs_RENEGOTIATE) { if (connectionState == cs_RENEGOTIATE) {
connectionState = cs_DATA; connectionState = cs_DATA;
...@@ -1046,8 +1035,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1046,8 +1035,6 @@ final public class SSLEngineImpl extends SSLEngine {
} }
handshaker = null; handshaker = null;
connectionState = cs_DATA; connectionState = cs_DATA;
receivedCCS = false;
// No handshakeListeners here. That's a // No handshakeListeners here. That's a
// SSLSocket thing. // SSLSocket thing.
} else if (handshaker.taskOutstanding()) { } else if (handshaker.taskOutstanding()) {
...@@ -1085,14 +1072,11 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1085,14 +1072,11 @@ final public class SSLEngineImpl extends SSLEngine {
case Record.ct_change_cipher_spec: case Record.ct_change_cipher_spec:
if ((connectionState != cs_HANDSHAKE if ((connectionState != cs_HANDSHAKE
&& connectionState != cs_RENEGOTIATE) && connectionState != cs_RENEGOTIATE)) {
|| !handshaker.sessionKeysCalculated()
|| receivedCCS) {
// For the CCS message arriving in the wrong state // For the CCS message arriving in the wrong state
fatal(Alerts.alert_unexpected_message, fatal(Alerts.alert_unexpected_message,
"illegal change cipher spec msg, conn state = " "illegal change cipher spec msg, conn state = "
+ connectionState + ", handshake state = " + connectionState);
+ handshaker.state);
} else if (inputRecord.available() != 1 } else if (inputRecord.available() != 1
|| inputRecord.read() != 1) { || inputRecord.read() != 1) {
// For structural/content issues with the CCS // For structural/content issues with the CCS
...@@ -1100,11 +1084,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1100,11 +1084,6 @@ final public class SSLEngineImpl extends SSLEngine {
"Malformed change cipher spec msg"); "Malformed change cipher spec msg");
} }
// Once we've received CCS, update the flag.
// If the remote endpoint sends it again in this handshake
// we won't process it.
receivedCCS = true;
// //
// The first message after a change_cipher_spec // The first message after a change_cipher_spec
// record MUST be a "Finished" handshake record, // record MUST be a "Finished" handshake record,
...@@ -1112,6 +1091,7 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1112,6 +1091,7 @@ final public class SSLEngineImpl extends SSLEngine {
// to be checked by a minor tweak to the state // to be checked by a minor tweak to the state
// machine. // machine.
// //
handshaker.receiveChangeCipherSpec();
changeReadCiphers(); changeReadCiphers();
// next message MUST be a finished message // next message MUST be a finished message
expectingFinished = true; expectingFinished = true;
...@@ -1151,7 +1131,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1151,7 +1131,6 @@ final public class SSLEngineImpl extends SSLEngine {
} }
} // synchronized (this) } // synchronized (this)
} }
return hsStatus; return hsStatus;
} }
...@@ -1185,6 +1164,10 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1185,6 +1164,10 @@ final public class SSLEngineImpl extends SSLEngine {
synchronized (wrapLock) { synchronized (wrapLock) {
return writeAppRecord(ea); return writeAppRecord(ea);
} }
} catch (SSLProtocolException spe) {
// may be an unexpected handshake message
fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
return null; // make compiler happy
} catch (Exception e) { } catch (Exception e) {
ea.resetPos(); ea.resetPos();
...@@ -1212,7 +1195,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1212,7 +1195,6 @@ final public class SSLEngineImpl extends SSLEngine {
* See if the handshaker needs to report back some SSLException. * See if the handshaker needs to report back some SSLException.
*/ */
checkTaskThrown(); checkTaskThrown();
/* /*
* short circuit if we're closed/closing. * short circuit if we're closed/closing.
*/ */
...@@ -1234,7 +1216,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -1234,7 +1216,6 @@ final public class SSLEngineImpl extends SSLEngine {
* without trying to wrap anything. * without trying to wrap anything.
*/ */
hsStatus = getHSStatus(null); hsStatus = getHSStatus(null);
if (hsStatus == HandshakeStatus.NEED_UNWRAP) { if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
return new SSLEngineResult(Status.OK, hsStatus, 0, 0); return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
} }
...@@ -2148,14 +2129,6 @@ final public class SSLEngineImpl extends SSLEngine { ...@@ -2148,14 +2129,6 @@ final public class SSLEngineImpl extends SSLEngine {
} }
} }
/*
* Returns a boolean indicating whether the ChangeCipherSpec message
* has been received for this handshake.
*/
boolean receivedChangeCipherSpec() {
return receivedCCS;
}
/** /**
* Returns a printable representation of this end of the connection. * Returns a printable representation of this end of the connection.
*/ */
......
...@@ -174,12 +174,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -174,12 +174,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
*/ */
private volatile int connectionState; private volatile int connectionState;
/*
* Flag indicating that the engine's handshaker has done the necessary
* steps so the engine may process a ChangeCipherSpec message.
*/
private boolean receivedCCS;
/* /*
* Flag indicating if the next record we receive MUST be a Finished * Flag indicating if the next record we receive MUST be a Finished
* message. Temporarily set during the handshake to ensure that * message. Temporarily set during the handshake to ensure that
...@@ -610,7 +604,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -610,7 +604,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
*/ */
roleIsServer = isServer; roleIsServer = isServer;
connectionState = cs_START; connectionState = cs_START;
receivedCCS = false;
/* /*
* default read and write side cipher and MAC support * default read and write side cipher and MAC support
...@@ -940,7 +933,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -940,7 +933,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
readRecord(r, true); readRecord(r, true);
} }
/* /*
* Clear the pipeline of records from the peer, optionally returning * Clear the pipeline of records from the peer, optionally returning
* application data. Caller is responsible for knowing that it's * application data. Caller is responsible for knowing that it's
...@@ -1074,7 +1066,8 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -1074,7 +1066,8 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
if (handshaker.invalidated) { if (handshaker.invalidated) {
handshaker = null; handshaker = null;
receivedCCS = false; inrec.setHandshakeHash(null);
// if state is cs_RENEGOTIATE, revert it to cs_DATA // if state is cs_RENEGOTIATE, revert it to cs_DATA
if (connectionState == cs_RENEGOTIATE) { if (connectionState == cs_RENEGOTIATE) {
connectionState = cs_DATA; connectionState = cs_DATA;
...@@ -1090,7 +1083,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -1090,7 +1083,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
handshakeSession = null; handshakeSession = null;
handshaker = null; handshaker = null;
connectionState = cs_DATA; connectionState = cs_DATA;
receivedCCS = false;
// //
// Tell folk about handshake completion, but do // Tell folk about handshake completion, but do
...@@ -1137,25 +1129,17 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -1137,25 +1129,17 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
case Record.ct_change_cipher_spec: case Record.ct_change_cipher_spec:
if ((connectionState != cs_HANDSHAKE if ((connectionState != cs_HANDSHAKE
&& connectionState != cs_RENEGOTIATE) && connectionState != cs_RENEGOTIATE)) {
|| !handshaker.sessionKeysCalculated()
|| receivedCCS) {
// For the CCS message arriving in the wrong state // For the CCS message arriving in the wrong state
fatal(Alerts.alert_unexpected_message, fatal(Alerts.alert_unexpected_message,
"illegal change cipher spec msg, conn state = " "illegal change cipher spec msg, conn state = "
+ connectionState + ", handshake state = " + connectionState);
+ handshaker.state);
} else if (r.available() != 1 || r.read() != 1) { } else if (r.available() != 1 || r.read() != 1) {
// For structural/content issues with the CCS // For structural/content issues with the CCS
fatal(Alerts.alert_unexpected_message, fatal(Alerts.alert_unexpected_message,
"Malformed change cipher spec msg"); "Malformed change cipher spec msg");
} }
// Once we've received CCS, update the flag.
// If the remote endpoint sends it again in this handshake
// we won't process it.
receivedCCS = true;
// //
// The first message after a change_cipher_spec // The first message after a change_cipher_spec
// record MUST be a "Finished" handshake record, // record MUST be a "Finished" handshake record,
...@@ -1163,6 +1147,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -1163,6 +1147,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
// to be checked by a minor tweak to the state // to be checked by a minor tweak to the state
// machine. // machine.
// //
handshaker.receiveChangeCipherSpec();
changeReadCiphers(); changeReadCiphers();
// next message MUST be a finished message // next message MUST be a finished message
expectingFinished = true; expectingFinished = true;
...@@ -1361,13 +1346,10 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -1361,13 +1346,10 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
kickstartHandshake(); kickstartHandshake();
/* /*
* All initial handshaking goes through this * All initial handshaking goes through this operation
* InputRecord until we have a valid SSL connection. * until we have a valid SSL connection.
* Once initial handshaking is finished, AppInputStream's
* InputRecord can handle any future renegotiation.
* *
* Keep this local so that it goes out of scope and is * Handle handshake messages only, need no application data.
* eventually GC'd.
*/ */
if (inrec == null) { if (inrec == null) {
inrec = new InputRecord(); inrec = new InputRecord();
...@@ -2675,14 +2657,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { ...@@ -2675,14 +2657,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl {
} }
} }
/*
* Returns a boolean indicating whether the ChangeCipherSpec message
* has been received for this handshake.
*/
boolean receivedChangeCipherSpec() {
return receivedCCS;
}
// //
// We allocate a separate thread to deliver handshake completion // We allocate a separate thread to deliver handshake completion
// events. This ensures that the notifications don't block the // events. This ensures that the notifications don't block the
......
...@@ -207,21 +207,14 @@ final class ServerHandshaker extends Handshaker { ...@@ -207,21 +207,14 @@ final class ServerHandshaker extends Handshaker {
@Override @Override
void processMessage(byte type, int message_len) void processMessage(byte type, int message_len)
throws IOException { throws IOException {
//
// In SSLv3 and TLS, messages follow strictly increasing // check the handshake state
// numerical order _except_ for one annoying special case. handshakeState.check(type);
//
if ((state >= type)
&& (state != HandshakeMessage.ht_client_key_exchange
&& type != HandshakeMessage.ht_certificate_verify)) {
throw new SSLProtocolException(
"Handshake message sequence violation, state = " + state
+ ", type = " + type);
}
switch (type) { switch (type) {
case HandshakeMessage.ht_client_hello: case HandshakeMessage.ht_client_hello:
ClientHello ch = new ClientHello(input, message_len); ClientHello ch = new ClientHello(input, message_len);
handshakeState.update(ch, resumingSession);
/* /*
* send it off for processing. * send it off for processing.
*/ */
...@@ -234,7 +227,9 @@ final class ServerHandshaker extends Handshaker { ...@@ -234,7 +227,9 @@ final class ServerHandshaker extends Handshaker {
"client sent unsolicited cert chain"); "client sent unsolicited cert chain");
// NOTREACHED // NOTREACHED
} }
this.clientCertificate(new CertificateMsg(input)); CertificateMsg certificateMsg = new CertificateMsg(input);
handshakeState.update(certificateMsg, resumingSession);
this.clientCertificate(certificateMsg);
break; break;
case HandshakeMessage.ht_client_key_exchange: case HandshakeMessage.ht_client_key_exchange:
...@@ -252,17 +247,20 @@ final class ServerHandshaker extends Handshaker { ...@@ -252,17 +247,20 @@ final class ServerHandshaker extends Handshaker {
protocolVersion, clientRequestedVersion, protocolVersion, clientRequestedVersion,
sslContext.getSecureRandom(), input, sslContext.getSecureRandom(), input,
message_len, privateKey); message_len, privateKey);
handshakeState.update(pms, resumingSession);
preMasterSecret = this.clientKeyExchange(pms); preMasterSecret = this.clientKeyExchange(pms);
break; break;
case K_KRB5: case K_KRB5:
case K_KRB5_EXPORT: case K_KRB5_EXPORT:
preMasterSecret = this.clientKeyExchange( KerberosClientKeyExchange kke =
new KerberosClientKeyExchange(protocolVersion, new KerberosClientKeyExchange(protocolVersion,
clientRequestedVersion, clientRequestedVersion,
sslContext.getSecureRandom(), sslContext.getSecureRandom(),
input, input,
this.getAccSE(), this.getAccSE(),
serviceCreds)); serviceCreds);
handshakeState.update(kke, resumingSession);
preMasterSecret = this.clientKeyExchange(kke);
break; break;
case K_DHE_RSA: case K_DHE_RSA:
case K_DHE_DSS: case K_DHE_DSS:
...@@ -273,16 +271,19 @@ final class ServerHandshaker extends Handshaker { ...@@ -273,16 +271,19 @@ final class ServerHandshaker extends Handshaker {
* protocol difference in these five flavors is in how * protocol difference in these five flavors is in how
* the ServerKeyExchange message was constructed! * the ServerKeyExchange message was constructed!
*/ */
preMasterSecret = this.clientKeyExchange( DHClientKeyExchange dhcke = new DHClientKeyExchange(input);
new DHClientKeyExchange(input)); handshakeState.update(dhcke, resumingSession);
preMasterSecret = this.clientKeyExchange(dhcke);
break; break;
case K_ECDH_RSA: case K_ECDH_RSA:
case K_ECDH_ECDSA: case K_ECDH_ECDSA:
case K_ECDHE_RSA: case K_ECDHE_RSA:
case K_ECDHE_ECDSA: case K_ECDHE_ECDSA:
case K_ECDH_ANON: case K_ECDH_ANON:
preMasterSecret = this.clientKeyExchange ECDHClientKeyExchange ecdhcke =
(new ECDHClientKeyExchange(input)); new ECDHClientKeyExchange(input);
handshakeState.update(ecdhcke, resumingSession);
preMasterSecret = this.clientKeyExchange(ecdhcke);
break; break;
default: default:
throw new SSLProtocolException throw new SSLProtocolException
...@@ -302,38 +303,24 @@ final class ServerHandshaker extends Handshaker { ...@@ -302,38 +303,24 @@ final class ServerHandshaker extends Handshaker {
break; break;
case HandshakeMessage.ht_certificate_verify: case HandshakeMessage.ht_certificate_verify:
this.clientCertificateVerify(new CertificateVerify(input, CertificateVerify cvm =
getLocalSupportedSignAlgs(), protocolVersion)); new CertificateVerify(input,
getLocalSupportedSignAlgs(), protocolVersion);
handshakeState.update(cvm, resumingSession);
this.clientCertificateVerify(cvm);
break; break;
case HandshakeMessage.ht_finished: case HandshakeMessage.ht_finished:
// A ChangeCipherSpec record must have been received prior to Finished cfm =
// reception of the Finished message (RFC 5246, 7.4.9). new Finished(protocolVersion, input, cipherSuite);
if (!receivedChangeCipherSpec()) { handshakeState.update(cfm, resumingSession);
fatalSE(Alerts.alert_handshake_failure, this.clientFinished(cfm);
"Received Finished message before ChangeCipherSpec");
}
this.clientFinished(
new Finished(protocolVersion, input, cipherSuite));
break; break;
default: default:
throw new SSLProtocolException( throw new SSLProtocolException(
"Illegal server handshake msg, " + type); "Illegal server handshake msg, " + type);
} }
//
// Move state machine forward if the message handling
// code didn't already do so
//
if (state < type) {
if(type == HandshakeMessage.ht_certificate_verify) {
state = type + 2; // an annoying special case
} else {
state = type;
}
}
} }
...@@ -364,7 +351,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -364,7 +351,7 @@ final class ServerHandshaker extends Handshaker {
// //
// This will not have any impact on server initiated renegotiation. // This will not have any impact on server initiated renegotiation.
if (rejectClientInitiatedRenego && !isInitialHandshake && if (rejectClientInitiatedRenego && !isInitialHandshake &&
state != HandshakeMessage.ht_hello_request) { !serverHelloRequested) {
fatalSE(Alerts.alert_handshake_failure, fatalSE(Alerts.alert_handshake_failure,
"Client initiated renegotiation is not allowed"); "Client initiated renegotiation is not allowed");
} }
...@@ -856,6 +843,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -856,6 +843,7 @@ final class ServerHandshaker extends Handshaker {
System.out.println("Cipher suite: " + session.getSuite()); System.out.println("Cipher suite: " + session.getSuite());
} }
m1.write(output); m1.write(output);
handshakeState.update(m1, resumingSession);
// //
// If we are resuming a session, we finish writing handshake // If we are resuming a session, we finish writing handshake
...@@ -895,6 +883,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -895,6 +883,7 @@ final class ServerHandshaker extends Handshaker {
m2.print(System.out); m2.print(System.out);
} }
m2.write(output); m2.write(output);
handshakeState.update(m2, resumingSession);
// XXX has some side effects with OS TCP buffering, // XXX has some side effects with OS TCP buffering,
// leave it out for now // leave it out for now
...@@ -990,6 +979,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -990,6 +979,7 @@ final class ServerHandshaker extends Handshaker {
m3.print(System.out); m3.print(System.out);
} }
m3.write(output); m3.write(output);
handshakeState.update(m3, resumingSession);
} }
// //
...@@ -1039,6 +1029,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -1039,6 +1029,7 @@ final class ServerHandshaker extends Handshaker {
m4.print(System.out); m4.print(System.out);
} }
m4.write(output); m4.write(output);
handshakeState.update(m4, resumingSession);
} }
/* /*
...@@ -1050,6 +1041,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -1050,6 +1041,7 @@ final class ServerHandshaker extends Handshaker {
m5.print(System.out); m5.print(System.out);
} }
m5.write(output); m5.write(output);
handshakeState.update(m5, resumingSession);
/* /*
* Flush any buffered messages so the client will see them. * Flush any buffered messages so the client will see them.
...@@ -1797,6 +1789,8 @@ final class ServerHandshaker extends Handshaker { ...@@ -1797,6 +1789,8 @@ final class ServerHandshaker extends Handshaker {
if (!resumingSession) { if (!resumingSession) {
input.digestNow(); input.digestNow();
sendChangeCipherAndFinish(true); sendChangeCipherAndFinish(true);
} else {
handshakeFinished = true;
} }
/* /*
...@@ -1844,16 +1838,6 @@ final class ServerHandshaker extends Handshaker { ...@@ -1844,16 +1838,6 @@ final class ServerHandshaker extends Handshaker {
if (secureRenegotiation) { if (secureRenegotiation) {
serverVerifyData = mesg.getVerifyData(); serverVerifyData = mesg.getVerifyData();
} }
/*
* Update state machine so client MUST send 'finished' next
* The update should only take place if it is not in the fast
* handshake mode since the server has to wait for a finished
* message from the client.
*/
if (finishedTag) {
state = HandshakeMessage.ht_finished;
}
} }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8044860 * @bug 8044860 8074462
* @summary Vectors and fixed length fields should be verified * @summary Vectors and fixed length fields should be verified
* for allowed sizes. * for allowed sizes.
* @run main/othervm LengthCheckTest * @run main/othervm LengthCheckTest
...@@ -231,7 +231,7 @@ public class LengthCheckTest { ...@@ -231,7 +231,7 @@ public class LengthCheckTest {
// sent back to the server. // sent back to the server.
if (gotException == false || if (gotException == false ||
!isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL, !isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
TLS_ALERT_INTERNAL_ERROR)) { TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException( throw new SSLException(
"Client failed to throw Alert:fatal:internal_error"); "Client failed to throw Alert:fatal:internal_error");
} }
...@@ -283,7 +283,7 @@ public class LengthCheckTest { ...@@ -283,7 +283,7 @@ public class LengthCheckTest {
// sent back to the client. // sent back to the client.
if (gotException == false || if (gotException == false ||
!isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL, !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
TLS_ALERT_INTERNAL_ERROR)) { TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException( throw new SSLException(
"Server failed to throw Alert:fatal:internal_error"); "Server failed to throw Alert:fatal:internal_error");
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册