|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
|
|
*/ |
|
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) { |
|
|
|
return ioe.getMessage(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class PskKeyExchangeModesConsumer implements ExtensionConsumer { |
|
|
|
private PskKeyExchangeModesConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
|
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
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"); |
|
} |
|
|
|
|
|
if (shc.isResumption && shc.resumingSession != null) { |
|
shc.isResumption = false; |
|
shc.resumingSession = null; |
|
} |
|
|
|
return; |
|
} |
|
|
|
|
|
PskKeyExchangeModesSpec spec; |
|
try { |
|
spec = new PskKeyExchangeModesSpec(buffer); |
|
} catch (IOException ioe) { |
|
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
} |
|
|
|
|
|
shc.handshakeExtensions.put( |
|
SSLExtension.PSK_KEY_EXCHANGE_MODES, spec); |
|
|
|
// Impact on 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 |
|
|
|
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"); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class PskKeyExchangeModesProducer implements HandshakeProducer { |
|
|
|
|
|
private PskKeyExchangeModesProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
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}; |
|
|
|
|
|
chc.handshakeExtensions.put( |
|
SSLExtension.PSK_KEY_EXCHANGE_MODES, |
|
PskKeyExchangeModesSpec.DEFAULT); |
|
|
|
return extData; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class PskKeyExchangeModesOnLoadAbsence implements HandshakeAbsence { |
|
|
|
|
|
private PskKeyExchangeModesOnLoadAbsence() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void absent(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
if (shc.isResumption) { |
|
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"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class PskKeyExchangeModesOnTradeAbsence implements HandshakeAbsence { |
|
|
|
|
|
private PskKeyExchangeModesOnTradeAbsence() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void absent(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
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, |
|
|
|
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"); |
|
} |
|
} |
|
} |
|
} |