/*
 * 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 java.text.MessageFormat;
import java.util.*;
import javax.net.ssl.SSLProtocolException;
import sun.security.ssl.SSLExtension.ExtensionConsumer;

import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;

Pack of the "psk_key_exchange_modes" extensions.
/** * Pack of the "psk_key_exchange_modes" extensions. */
final class PskKeyExchangeModesExtension { static final HandshakeProducer chNetworkProducer = new PskKeyExchangeModesProducer(); static final ExtensionConsumer chOnLoadConsumer = new PskKeyExchangeModesConsumer(); static final HandshakeAbsence chOnLoadAbsence = new PskKeyExchangeModesOnLoadAbsence(); static final HandshakeAbsence chOnTradeAbsence = new PskKeyExchangeModesOnTradeAbsence(); static final SSLStringizer pkemStringizer = new PskKeyExchangeModesStringizer(); enum PskKeyExchangeMode { PSK_KE ((byte)0, "psk_ke"), PSK_DHE_KE ((byte)1, "psk_dhe_ke"); final byte id; final String name; PskKeyExchangeMode(byte id, String name) { this.id = id; this.name = name; } static PskKeyExchangeMode valueOf(byte id) { for(PskKeyExchangeMode pkem : values()) { if (pkem.id == id) { return pkem; } } return null; } static String nameOf(byte id) { for (PskKeyExchangeMode pkem : PskKeyExchangeMode.values()) { if (pkem.id == id) { return pkem.name; } } return "<UNKNOWN PskKeyExchangeMode TYPE: " + (id & 0x0FF) + ">"; } } static final class PskKeyExchangeModesSpec implements SSLExtensionSpec { private static final PskKeyExchangeModesSpec DEFAULT = new PskKeyExchangeModesSpec(new byte[] { PskKeyExchangeMode.PSK_DHE_KE.id}); final byte[] modes; PskKeyExchangeModesSpec(byte[] modes) { this.modes = modes; } PskKeyExchangeModesSpec(ByteBuffer m) throws IOException { if (m.remaining() < 2) { throw new SSLProtocolException( "Invalid psk_key_exchange_modes extension: " + "insufficient data"); } this.modes = Record.getBytes8(m); } boolean contains(PskKeyExchangeMode mode) { if (modes != null) { for (byte m : modes) { if (mode.id == m) { return true; } } } return false; } @Override public String toString() { MessageFormat messageFormat = new MessageFormat( "\"ke_modes\": '['{0}']'", Locale.ENGLISH); if (modes == null || modes.length == 0) { Object[] messageFields = { "<no PSK key exchange modes specified>" }; return messageFormat.format(messageFields); } else { StringBuilder builder = new StringBuilder(64); boolean isFirst = true; for (byte mode : modes) { if (isFirst) { isFirst = false; } else { builder.append(", "); } builder.append(PskKeyExchangeMode.nameOf(mode)); } Object[] messageFields = { builder.toString() }; return messageFormat.format(messageFields); } } } private static final class PskKeyExchangeModesStringizer implements SSLStringizer { @Override public String toString(ByteBuffer buffer) { try { return (new PskKeyExchangeModesSpec(buffer)).toString(); } catch (IOException ioe) { // For debug logging only, so please swallow exceptions. return ioe.getMessage(); } } }
Network data consumer of a "psk_key_exchange_modes" extension in the ClientHello handshake message.
/** * Network data consumer of a "psk_key_exchange_modes" extension in * the ClientHello handshake message. */
private static final class PskKeyExchangeModesConsumer implements ExtensionConsumer { // Prevent instantiation of this class. private PskKeyExchangeModesConsumer() { // 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( SSLExtension.PSK_KEY_EXCHANGE_MODES)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable psk_key_exchange_modes extension"); } // No session resumption is allowed. if (shc.isResumption && shc.resumingSession != null) { shc.isResumption = false; shc.resumingSession = null; } return; // ignore the extension } // Parse the extension. PskKeyExchangeModesSpec spec; try { spec = new PskKeyExchangeModesSpec(buffer); } catch (IOException ioe) { throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); } // Update the context. shc.handshakeExtensions.put( SSLExtension.PSK_KEY_EXCHANGE_MODES, spec); // Impact on session resumption. // // Do the requested modes support session resumption? if (shc.isResumption) { // resumingSession may not be set // Note: psk_dhe_ke is the only supported mode now. If the // psk_ke mode is supported in the future, may need an update // here. if (!spec.contains(PskKeyExchangeMode.PSK_DHE_KE)) { shc.isResumption = false; shc.resumingSession = null; if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "no supported psk_dhe_ke PSK key exchange mode"); } } } } }
Network data producer of a "psk_key_exchange_modes" extension in the ClientHello handshake message.
/** * Network data producer of a "psk_key_exchange_modes" extension in the * ClientHello handshake message. */
private static final class PskKeyExchangeModesProducer implements HandshakeProducer { // Prevent instantiation of this class. private PskKeyExchangeModesProducer() { // 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( SSLExtension.PSK_KEY_EXCHANGE_MODES)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unavailable psk_key_exchange_modes extension"); } return null; } byte[] extData = new byte[] {0x01, 0x01}; // psk_dhe_ke // Update the context. chc.handshakeExtensions.put( SSLExtension.PSK_KEY_EXCHANGE_MODES, PskKeyExchangeModesSpec.DEFAULT); return extData; } }
The absence processing if a "psk_key_exchange_modes" extension is not present in the ClientHello handshake message.
/** * The absence processing if a "psk_key_exchange_modes" extension is * not present in the ClientHello handshake message. */
private static final class PskKeyExchangeModesOnLoadAbsence implements HandshakeAbsence { // Prevent instantiation of this class. private PskKeyExchangeModesOnLoadAbsence() { // blank } @Override public void absent(ConnectionContext context, HandshakeMessage message) throws IOException { // The consuming happens in server side only. ServerHandshakeContext shc = (ServerHandshakeContext)context; // No session resumptio is allowed. if (shc.isResumption) { // resumingSession may not be set shc.isResumption = false; shc.resumingSession = null; if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "no supported psk_dhe_ke PSK key exchange mode"); } } } }
The absence processing if a "signature_algorithms" extension is not present in the ClientHello handshake message.
/** * The absence processing if a "signature_algorithms" extension is * not present in the ClientHello handshake message. */
private static final class PskKeyExchangeModesOnTradeAbsence implements HandshakeAbsence { // Prevent instantiation of this class. private PskKeyExchangeModesOnTradeAbsence() { // blank } @Override public void absent(ConnectionContext context, HandshakeMessage message) throws IOException { // The consuming happens in server side only. ServerHandshakeContext shc = (ServerHandshakeContext)context; // A client MUST provide a "psk_key_exchange_modes" extension if // it offers a "pre_shared_key" extension. If clients offer // "pre_shared_key" without a "psk_key_exchange_modes" extension, // servers MUST abort the handshake. SSLExtensionSpec spec = shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY); if (spec != null) { throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "pre_shared_key key extension is offered " + "without a psk_key_exchange_modes extension"); } } } }