< prev index next >
src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
+ * Copyright (c) 2017, 2018, Red Hat, Inc. and/or its affiliates.
* 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
@@ -24,48 +24,362 @@
*/
package sun.security.ssl;
import java.io.IOException;
+import java.nio.ByteBuffer;
import javax.net.ssl.SSLProtocolException;
+import static sun.security.ssl.SSLConfiguration.allowLegacyMasterSecret;
+import static sun.security.ssl.SSLConfiguration.allowLegacyResumption;
+import static sun.security.ssl.SSLExtension.CH_EXTENDED_MASTER_SECRET;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import static sun.security.ssl.SSLExtension.SH_EXTENDED_MASTER_SECRET;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
- * Extended Master Secret TLS extension (TLS 1.0+). This extension
- * defines how to calculate the TLS connection master secret and
- * mitigates some types of man-in-the-middle attacks.
- *
- * See further information in
- * <a href="https://tools.ietf.org/html/rfc7627">RFC 7627</a>.
- *
- * @author Martin Balao (mbalao@redhat.com)
+ * Pack of the "extended_master_secret" extensions [RFC 5746].
+ */
+final class ExtendedMasterSecretExtension {
+ static final HandshakeProducer chNetworkProducer =
+ new CHExtendedMasterSecretProducer();
+ static final ExtensionConsumer chOnLoadConcumer =
+ new CHExtendedMasterSecretConsumer();
+ static final HandshakeAbsence chOnLoadAbsence =
+ new CHExtendedMasterSecretAbsence();
+
+ static final HandshakeProducer shNetworkProducer =
+ new SHExtendedMasterSecretProducer();
+ static final ExtensionConsumer shOnLoadConcumer =
+ new SHExtendedMasterSecretConsumer();
+ static final HandshakeAbsence shOnLoadAbsence =
+ new SHExtendedMasterSecretAbsence();
+
+ static final SSLStringize emsStringize =
+ new ExtendedMasterSecretStringize();
+
+ /**
+ * The "extended_master_secret" extension.
*/
-final class ExtendedMasterSecretExtension extends HelloExtension {
- ExtendedMasterSecretExtension() {
- super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+ static final class ExtendedMasterSecretSpec implements SSLExtensionSpec {
+ // A nominal object that does not holding any real renegotiation info.
+ static final ExtendedMasterSecretSpec NOMINAL =
+ new ExtendedMasterSecretSpec();
+
+ private ExtendedMasterSecretSpec() {
+ // blank
}
- ExtendedMasterSecretExtension(HandshakeInStream s,
- int len) throws IOException {
- super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+ private ExtendedMasterSecretSpec(ByteBuffer m) throws IOException {
+ // Parse the extension.
+ if (m.hasRemaining()) {
+ throw new SSLProtocolException(
+ "Invalid extended_master_secret extension data: " +
+ "not empty");
+ }
+ }
- if (len != 0) {
- throw new SSLProtocolException("Invalid " + type + " extension");
+ @Override
+ public String toString() {
+ return "<empty>";
}
}
+ private static final
+ class ExtendedMasterSecretStringize implements SSLStringize {
@Override
- int length() {
- return 4; // 4: extension type and length fields
+ public String toString(ByteBuffer buffer) {
+ try {
+ return (new ExtendedMasterSecretSpec(buffer)).toString();
+ } catch (IOException ioe) {
+ // For debug logging only, so please swallow exceptions.
+ return ioe.getMessage();
+ }
+ }
+ }
+
+ /**
+ * Network data producer of a "extended_master_secret" extension in
+ * the ClientHello handshake message.
+ */
+ private static final
+ class CHExtendedMasterSecretProducer implements HandshakeProducer {
+ // Prevent instantiation of this class.
+ private CHExtendedMasterSecretProducer() {
+ // blank
+ }
+
+ @Override
+ public byte[] produce(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The producing happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ // Is it a supported and enabled extension?
+ if (!chc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET)) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "Ignore unavailable extended_master_secret extension");
+ }
+
+ return null;
+ }
+
+ if (chc.handshakeSession == null ||
+ chc.handshakeSession.useExtendedMasterSecret) {
+ byte[] extData = new byte[0];
+ chc.handshakeExtensions.put(CH_EXTENDED_MASTER_SECRET,
+ ExtendedMasterSecretSpec.NOMINAL);
+
+ return extData;
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Network data producer of a "extended_master_secret" extension in
+ * the ServerHello handshake message.
+ */
+ private static final
+ class CHExtendedMasterSecretConsumer implements ExtensionConsumer {
+ // Prevent instantiation of this class.
+ private CHExtendedMasterSecretConsumer() {
+ // blank
}
@Override
- void send(HandshakeOutStream s) throws IOException {
- s.putInt16(type.id); // ExtensionType extension_type;
- s.putInt16(0); // extension_data length
+ public void consume(ConnectionContext context,
+ HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+ // The comsuming happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ // Is it a supported and enabled extension?
+ if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET)) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine("Ignore unavailable extension: " +
+ CH_EXTENDED_MASTER_SECRET.name);
+ }
+ return; // ignore the extension
}
+ // Parse the extension.
+ ExtendedMasterSecretSpec spec;
+ try {
+ spec = new ExtendedMasterSecretSpec(buffer);
+ } catch (IOException ioe) {
+ shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+ return; // fatal() always throws, make the compiler happy.
+ }
+
+ if (shc.isResumption && shc.resumingSession != null &&
+ !shc.resumingSession.useExtendedMasterSecret) {
+ // For abbreviated handshake request, If the original
+ // session did not use the "extended_master_secret"
+ // extension but the new ClientHello contains the
+ // extension, then the server MUST NOT perform the
+ // abbreviated handshake. Instead, it SHOULD continue
+ // with a full handshake.
+ shc.isResumption = false;
+ shc.resumingSession = null;
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "abort session resumption which did not use " +
+ "Extended Master Secret extension");
+ }
+ }
+
+ // Update the context.
+ //
+ shc.handshakeExtensions.put(
+ CH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
+
+ // No impact on session resumption.
+ }
+ }
+
+ /**
+ * The absence processing if a "extended_master_secret" extension is
+ * not present in the ClientHello handshake message.
+ */
+ private static final
+ class CHExtendedMasterSecretAbsence implements HandshakeAbsence {
@Override
- public String toString() {
- return "Extension " + type;
+ public void absent(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The producing happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ // Is it a supported and enabled extension?
+ if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET)) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine("Ignore unavailable extension: " +
+ CH_EXTENDED_MASTER_SECRET.name);
+ }
+ return; // ignore the extension
+ }
+
+ if (!allowLegacyMasterSecret) {
+ // For full handshake, if the server receives a ClientHello
+ // without the extension, it SHOULD abort the handshake if
+ // it does not wish to interoperate with legacy clients.
+ //
+ // As if extended master extension is required for full
+ // handshake, it MUST be used in abbreviated handshake too.
+ shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "Extended Master Secret extension is required");
+ }
+
+ if (shc.isResumption && shc.resumingSession != null) {
+ if (shc.resumingSession.useExtendedMasterSecret) {
+ // For abbreviated handshake request, if the original
+ // session used the "extended_master_secret" extension
+ // but the new ClientHello does not contain it, the
+ // server MUST abort the abbreviated handshake.
+ shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "Missing Extended Master Secret extension " +
+ "on session resumption");
+ } else {
+ // For abbreviated handshake request, if neither the
+ // original session nor the new ClientHello uses the
+ // extension, the server SHOULD abort the handshake.
+ if (!allowLegacyResumption) {
+ shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "Missing Extended Master Secret extension " +
+ "on session resumption");
+ } else { // Otherwise, continue with a full handshake.
+ shc.isResumption = false;
+ shc.resumingSession = null;
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "abort session resumption, " +
+ "missing Extended Master Secret extension");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Network data producer of a "extended_master_secret" extension in
+ * the ServerHello handshake message.
+ */
+ private static final
+ class SHExtendedMasterSecretProducer implements HandshakeProducer {
+ // Prevent instantiation of this class.
+ private SHExtendedMasterSecretProducer() {
+ // blank
+ }
+
+ @Override
+ public byte[] produce(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The producing happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ if (shc.handshakeSession.useExtendedMasterSecret) {
+ byte[] extData = new byte[0];
+ shc.handshakeExtensions.put(SH_EXTENDED_MASTER_SECRET,
+ ExtendedMasterSecretSpec.NOMINAL);
+
+ return extData;
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Network data consumer of a "extended_master_secret" extension in
+ * the ServerHello handshake message.
+ */
+ private static final
+ class SHExtendedMasterSecretConsumer implements ExtensionConsumer {
+ // Prevent instantiation of this class.
+ private SHExtendedMasterSecretConsumer() {
+ // blank
+ }
+
+ @Override
+ public void consume(ConnectionContext context,
+ HandshakeMessage message, ByteBuffer buffer) throws IOException {
+ // The producing happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ // In response to the client extended_master_secret extension
+ // request, which is mandatory for ClientHello message.
+ ExtendedMasterSecretSpec requstedSpec = (ExtendedMasterSecretSpec)
+ chc.handshakeExtensions.get(CH_EXTENDED_MASTER_SECRET);
+ if (requstedSpec == null) {
+ chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
+ "Server sent the extended_master_secret " +
+ "extension improperly");
+ }
+
+ // Parse the extension.
+ ExtendedMasterSecretSpec spec;
+ try {
+ spec = new ExtendedMasterSecretSpec(buffer);
+ } catch (IOException ioe) {
+ chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+ return; // fatal() always throws, make the compiler happy.
+ }
+
+ if (chc.isResumption && chc.resumingSession != null &&
+ !chc.resumingSession.useExtendedMasterSecret) {
+ chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
+ "Server sent an unexpected extended_master_secret " +
+ "extension on session resumption");
+ }
+
+ // Update the context.
+ chc.handshakeExtensions.put(
+ SH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
+
+ // No impact on session resumption.
+ }
+ }
+
+ /**
+ * The absence processing if a "extended_master_secret" extension is
+ * not present in the ServerHello handshake message.
+ */
+ private static final
+ class SHExtendedMasterSecretAbsence implements HandshakeAbsence {
+ @Override
+ public void absent(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The producing happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ if (SSLConfiguration.useExtendedMasterSecret
+ && !SSLConfiguration.allowLegacyMasterSecret) {
+ // For full handshake, if a client receives a ServerHello
+ // without the extension, it SHOULD abort the handshake if
+ // it does not wish to interoperate with legacy servers.
+ chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "Extended Master Secret extension is required");
+ }
+
+ if (chc.isResumption && chc.resumingSession != null) {
+ if (chc.resumingSession.useExtendedMasterSecret) {
+ // For abbreviated handshake, if the original session used
+ // the "extended_master_secret" extension but the new
+ // ServerHello does not contain the extension, the client
+ // MUST abort the handshake.
+ chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "Missing Extended Master Secret extension " +
+ "on session resumption");
+ } else if (SSLConfiguration.useExtendedMasterSecret &&
+ !SSLConfiguration.allowLegacyResumption) {
+ // Unlikely, abbreviated handshake should be discarded.
+ chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "Extended Master Secret extension is required");
+ }
+ }
+ }
}
}
< prev index next >