/*
 * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
 * Copyright (c) 2018, 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.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLProtocolException;
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;

Pack of the "extended_master_secret" extensions [RFC 7627].
/** * Pack of the "extended_master_secret" extensions [RFC 7627]. */
final class ExtendedMasterSecretExtension { static final HandshakeProducer chNetworkProducer = new CHExtendedMasterSecretProducer(); static final ExtensionConsumer chOnLoadConsumer = new CHExtendedMasterSecretConsumer(); static final HandshakeAbsence chOnLoadAbsence = new CHExtendedMasterSecretAbsence(); static final HandshakeProducer shNetworkProducer = new SHExtendedMasterSecretProducer(); static final ExtensionConsumer shOnLoadConsumer = new SHExtendedMasterSecretConsumer(); static final HandshakeAbsence shOnLoadAbsence = new SHExtendedMasterSecretAbsence(); static final SSLStringizer emsStringizer = new ExtendedMasterSecretStringizer();
The "extended_master_secret" extension.
/** * The "extended_master_secret" extension. */
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 } private ExtendedMasterSecretSpec(ByteBuffer m) throws IOException { // Parse the extension. if (m.hasRemaining()) { throw new SSLProtocolException( "Invalid extended_master_secret extension data: " + "not empty"); } } @Override public String toString() { return "<empty>"; } } private static final class ExtendedMasterSecretStringizer implements SSLStringizer { @Override 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.
/** * 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) || !SSLConfiguration.useExtendedMasterSecret || !chc.conContext.protocolVersion.useTLS10PlusSpec()) { 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.
/** * 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 public void consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer) throws IOException { // The consuming happens in server side only. ServerHandshakeContext shc = (ServerHandshakeContext)context; // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) || !SSLConfiguration.useExtendedMasterSecret || !shc.negotiatedProtocol.useTLS10PlusSpec()) { 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) { throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); } 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.
/** * 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 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) || !SSLConfiguration.useExtendedMasterSecret) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + CH_EXTENDED_MASTER_SECRET.name); } return; // ignore the extension } if (shc.negotiatedProtocol.useTLS10PlusSpec() && !SSLConfiguration.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. throw 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. throw 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 (!SSLConfiguration.allowLegacyResumption) { throw 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.
/** * 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.
/** * 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) { throw 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) { throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); } if (chc.isResumption && chc.resumingSession != null && !chc.resumingSession.useExtendedMasterSecret) { throw 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.
/** * 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. throw 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. throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Missing Extended Master Secret extension " + "on session resumption"); } else if (SSLConfiguration.useExtendedMasterSecret && !SSLConfiguration.allowLegacyResumption && chc.negotiatedProtocol.useTLS10PlusSpec()) { // Unlikely, abbreviated handshake should be discarded. throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Extended Master Secret extension is required"); } } } } }