--- old/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Fri May 29 00:47:46 2015 +++ new/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Fri May 29 00:47:46 2015 @@ -141,11 +141,20 @@ private final static boolean allowUnsafeServerCertChange = Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false); + // To switch off the max_fragment_length extension. + private final static boolean enableMFLExtension = + Debug.getBooleanProperty("jsse.enableMFLExtension", false); + private List requestedServerNames = Collections.emptyList(); + // maximum fragment length + private int requestedMFLength = -1; // -1: no fragment length limit + private boolean serverNamesAccepted = false; + private ClientHello initialClientHelloMsg = null; // DTLS only + /* * the reserved server certificate chain in previous handshaking * @@ -172,11 +181,12 @@ ProtocolList enabledProtocols, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData) { + byte[] clientVerifyData, byte[] serverVerifyData, + boolean isDTLS) { super(engine, context, enabledProtocols, true, true, activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData); + clientVerifyData, serverVerifyData, isDTLS); } /* @@ -191,19 +201,35 @@ */ @Override void processMessage(byte type, int messageLen) throws IOException { - if (state >= type - && (type != HandshakeMessage.ht_hello_request)) { - throw new SSLProtocolException( - "Handshake message sequence violation, " + type); - } + // check the handshake state + handshakeState.check(type); switch (type) { case HandshakeMessage.ht_hello_request: - this.serverHelloRequest(new HelloRequest(input)); + HelloRequest helloRequest = new HelloRequest(input); + handshakeState.update(helloRequest, resumingSession); + this.serverHelloRequest(helloRequest); break; + case HandshakeMessage.ht_hello_verify_request: + if (!isDTLS) { + throw new SSLProtocolException( + "hello_verify_request is not a SSL/TLS handshake message"); + } + + HelloVerifyRequest helloVerifyRequest = + new HelloVerifyRequest(input, messageLen); + handshakeState.update(helloVerifyRequest, resumingSession); + this.helloVerifyRequest(helloVerifyRequest); + break; + 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; case HandshakeMessage.ht_certificate: @@ -213,7 +239,9 @@ "unexpected server cert chain"); // NOTREACHED } - this.serverCertificate(new CertificateMsg(input)); + CertificateMsg certificateMsg = new CertificateMsg(input); + handshakeState.update(certificateMsg, resumingSession); + this.serverCertificate(certificateMsg); serverKey = session.getPeerCertificates()[0].getPublicKey(); break; @@ -249,29 +277,37 @@ } try { - this.serverKeyExchange(new RSA_ServerKeyExchange(input)); + RSA_ServerKeyExchange rsaSrvKeyExchange = + new RSA_ServerKeyExchange(input); + handshakeState.update(rsaSrvKeyExchange, resumingSession); + this.serverKeyExchange(rsaSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_DH_ANON: try { - this.serverKeyExchange(new DH_ServerKeyExchange( - input, protocolVersion)); + DH_ServerKeyExchange dhSrvKeyExchange = + new DH_ServerKeyExchange(input, protocolVersion); + handshakeState.update(dhSrvKeyExchange, resumingSession); + this.serverKeyExchange(dhSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_DHE_DSS: case K_DHE_RSA: try { - this.serverKeyExchange(new DH_ServerKeyExchange( - input, serverKey, - clnt_random.random_bytes, svr_random.random_bytes, - messageLen, - localSupportedSignAlgs, protocolVersion)); + DH_ServerKeyExchange dhSrvKeyExchange = + new DH_ServerKeyExchange( + input, serverKey, + clnt_random.random_bytes, svr_random.random_bytes, + messageLen, + localSupportedSignAlgs, protocolVersion); + handshakeState.update(dhSrvKeyExchange, resumingSession); + this.serverKeyExchange(dhSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_ECDHE_ECDSA: @@ -278,12 +314,15 @@ case K_ECDHE_RSA: case K_ECDH_ANON: try { - this.serverKeyExchange(new ECDH_ServerKeyExchange - (input, serverKey, clnt_random.random_bytes, - svr_random.random_bytes, - localSupportedSignAlgs, protocolVersion)); + ECDH_ServerKeyExchange ecdhSrvKeyExchange = + new ECDH_ServerKeyExchange + (input, serverKey, clnt_random.random_bytes, + svr_random.random_bytes, + localSupportedSignAlgs, protocolVersion); + handshakeState.update(ecdhSrvKeyExchange, resumingSession); + this.serverKeyExchange(ecdhSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_RSA: @@ -320,8 +359,9 @@ if (debug != null && Debug.isOn("handshake")) { certRequest.print(System.out); } + handshakeState.update(certRequest, resumingSession); - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { Collection peerSignAlgs = certRequest.getSignAlgorithms(); if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { @@ -345,19 +385,18 @@ break; case HandshakeMessage.ht_server_hello_done: - this.serverHelloDone(new ServerHelloDone(input)); + ServerHelloDone serverHelloDone = new ServerHelloDone(input); + handshakeState.update(serverHelloDone, resumingSession); + this.serverHelloDone(serverHelloDone); + break; case HandshakeMessage.ht_finished: - // A ChangeCipherSpec record must have been received prior to - // reception of the Finished message (RFC 5246, 7.4.9). - if (!receivedChangeCipherSpec()) { - fatalSE(Alerts.alert_handshake_failure, - "Received Finished message before ChangeCipherSpec"); - } + Finished serverFinished = + new Finished(protocolVersion, input, cipherSuite); + handshakeState.update(serverFinished, resumingSession); + this.serverFinished(serverFinished); - this.serverFinished( - new Finished(protocolVersion, input, cipherSuite)); break; default: @@ -364,14 +403,6 @@ throw new SSLProtocolException( "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,10 +420,10 @@ // Could be (e.g. at connection setup) that we already // sent the "client hello" but the server's not seen it. // - if (state < HandshakeMessage.ht_client_hello) { + if (!clientHelloDelivered) { if (!secureRenegotiation && !allowUnsafeRenegotiation) { // renegotiation is not allowed. - if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { + if (activeProtocolVersion.useTLS10PlusSpec()) { // response with a no_renegotiation warning, warningSE(Alerts.alert_no_renegotiation); @@ -428,7 +459,30 @@ } } + private void helloVerifyRequest( + HelloVerifyRequest mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + // + // Note that HelloVerifyRequest.server_version is used solely to + // indicate packet formatting, and not as part of version negotiation. + // Need not to check version values match for HelloVerifyRequest + // message. + // + initialClientHelloMsg.cookie = mesg.cookie.clone(); + + if (debug != null && Debug.isOn("handshake")) { + initialClientHelloMsg.print(System.out); + } + + // deliver the ClientHello message with cookie + initialClientHelloMsg.write(output); + handshakeState.update(initialClientHelloMsg, resumingSession); + } + /* * Server chooses session parameters given options created by the * client -- basically, cipher options, session id, and someday a @@ -441,6 +495,9 @@ * probably authentication getting done. */ private void serverHello(ServerHello mesg) throws IOException { + // Dispose the reserved ClientHello message (if exists). + initialClientHelloMsg = null; + serverKeyExchangeReceived = false; if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); @@ -536,7 +593,7 @@ } setCipherSuite(mesg.cipherSuite); - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); } @@ -611,9 +668,8 @@ } } - // looks fine; resume it, and update the state machine. + // looks fine; resume it. resumingSession = true; - state = HandshakeMessage.ht_finished - 1; calculateConnectionKeys(session.getMasterSecret()); if (debug != null && Debug.isOn("session")) { System.out.println("%% Server resumed " + session); @@ -627,6 +683,24 @@ } } + // check the "max_fragment_length" extension + MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension) + mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH); + if (maxFragLenExt != null) { + if ((requestedMFLength == -1) || + maxFragLenExt.getMaxFragLen() != requestedMFLength) { + // If the client did not request this extension, or the + // response value is different from the length it requested, + // abort the handshake with a fatal illegal_parameter alert. + fatalSE(Alerts.alert_illegal_parameter, + "Failed to negotiate the max_fragment_length"); + } + } else if (!resumingSession) { + // no "max_fragment_length" extension + requestedMFLength = -1; + } // Otherwise, using the value negotiated during the original + // session initiation + if (resumingSession && session != null) { setHandshakeSessionSE(session); // Reserve the handshake state if this is a session-resumption @@ -657,6 +731,8 @@ getLocalSupportedSignAlgs(), mesg.sessionId, getHostSE(), getPortSE()); session.setRequestedServerNames(requestedServerNames); + session.setNegotiatedMaxFragSize(requestedMFLength); + session.setMaximumPacketSize(maximumPacketSize); setHandshakeSessionSE(session); if (debug != null && Debug.isOn("handshake")) { System.out.println("** " + cipherSuite); @@ -681,7 +757,6 @@ ephemeralServerKey = mesg.getPublicKey(); } - /* * Diffie-Hellman key exchange. We save the server public key and * our own D-H algorithm object so we can defer key calculations @@ -716,13 +791,6 @@ if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } - /* - * Always make sure the input has been digested before we - * start emitting data, to ensure the hashes are correctly - * computed for the Finished and CertificateVerify messages - * which we send (here). - */ - input.digestNow(); /* * FIRST ... if requested, send an appropriate Certificate chain @@ -817,7 +885,7 @@ // server. For SSLv3, send the no_certificate alert; // TLS uses an empty cert chain instead. // - if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (protocolVersion.useTLS10PlusSpec()) { m1 = new CertificateMsg(new X509Certificate [0]); } else { warningSE(Alerts.alert_no_certificate); @@ -837,6 +905,7 @@ m1.print(System.out); } m1.write(output); + handshakeState.update(m1, resumingSession); } } @@ -1000,8 +1069,8 @@ m2.print(System.out); } m2.write(output); + handshakeState.update(m2, resumingSession); - /* * THIRD, send a "change_cipher_spec" record followed by the * "Finished" message. We flush the messages we've queued up, to @@ -1010,8 +1079,6 @@ * to compute the "Finished" message, and to compute the keys used * to protect all records following the change_cipher_spec. */ - - output.doHashes(); output.flush(); /* @@ -1069,7 +1136,7 @@ CertificateVerify m3; try { SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { preferableSignatureAlgorithm = SignatureAndHashAlgorithm.getPreferableAlgorithm( peerSupportedSignAlgs, signingKey.getAlgorithm(), @@ -1103,7 +1170,8 @@ m3.print(System.out); } m3.write(output); - output.doHashes(); + handshakeState.update(m3, resumingSession); + output.flush(); } /* @@ -1110,6 +1178,9 @@ * OK, that's that! */ sendChangeCipherAndFinish(false); + + // expecting the final ChangeCipherSpec and Finished messages + expectingFinishFlightSE(); } @@ -1158,8 +1229,9 @@ * completed handshakes. */ if (resumingSession) { - input.digestNow(); sendChangeCipherAndFinish(true); + } else { + handshakeFinished = true; } session.setLastAccessedTime(System.currentTimeMillis()); @@ -1188,6 +1260,10 @@ */ private void sendChangeCipherAndFinish(boolean finishedTag) throws IOException { + + // Reload if this message has been reserved. + handshakeHash.reload(); + Finished mesg = new Finished(protocolVersion, handshakeHash, Finished.CLIENT, session.getMasterSecret(), cipherSuite); @@ -1205,13 +1281,6 @@ if (secureRenegotiation) { 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; } @@ -1361,10 +1430,10 @@ // create the ClientHello message ClientHello clientHelloMessage = new ClientHello( sslContext.getSecureRandom(), maxProtocolVersion, - sessionId, cipherSuites); + sessionId, cipherSuites, isDTLS); // add signature_algorithm extension - if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) { + if (maxProtocolVersion.useTLS12PlusSpec()) { // we will always send the signature_algorithm extension Collection localSignAlgs = getLocalSupportedSignAlgs(); @@ -1389,6 +1458,37 @@ } } + // add max_fragment_length extension + if (enableMFLExtension) { + if (session != null) { + // The same extension should be sent for resumption. + requestedMFLength = session.getNegotiatedMaxFragSize(); + } else if (maximumPacketSize != 0) { + // Maybe we can calculate the fragment size more accurate + // by condering the enabled cipher suites in the future. + requestedMFLength = maximumPacketSize; + if (isDTLS) { + requestedMFLength -= DTLSRecord.maxPlaintextPlusSize; + } else { + requestedMFLength -= SSLRecord.maxPlaintextPlusSize; + } + } else { + // Need no max_fragment_length extension. + requestedMFLength = -1; + } + + if ((requestedMFLength > 0) && + MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) { + + requestedMFLength = + MaxFragmentLengthExtension.getValidMaxFragLen( + requestedMFLength); + clientHelloMessage.addMFLExtension(requestedMFLength); + } else { + requestedMFLength = -1; + } + } + // reset the client random cookie clnt_random = clientHelloMessage.clnt_random; @@ -1403,6 +1503,11 @@ clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData); } + if (isDTLS) { + // Cookie exchange need to reserve the initial ClientHello message. + initialClientHelloMsg = clientHelloMessage; + } + return clientHelloMessage; }