|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.io.IOException; |
|
import java.nio.ByteBuffer; |
|
import java.text.MessageFormat; |
|
import java.util.Locale; |
|
import javax.net.ssl.SSLProtocolException; |
|
import static sun.security.ssl.SSLExtension.CH_SUPPORTED_VERSIONS; |
|
import sun.security.ssl.SSLExtension.ExtensionConsumer; |
|
import static sun.security.ssl.SSLExtension.HRR_SUPPORTED_VERSIONS; |
|
import static sun.security.ssl.SSLExtension.SH_SUPPORTED_VERSIONS; |
|
import sun.security.ssl.SSLExtension.SSLExtensionSpec; |
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
|
|
|
|
|
|
*/ |
|
final class SupportedVersionsExtension { |
|
static final HandshakeProducer chNetworkProducer = |
|
new CHSupportedVersionsProducer(); |
|
static final ExtensionConsumer chOnLoadConsumer = |
|
new CHSupportedVersionsConsumer(); |
|
static final SSLStringizer chStringizer = |
|
new CHSupportedVersionsStringizer(); |
|
|
|
static final HandshakeProducer shNetworkProducer = |
|
new SHSupportedVersionsProducer(); |
|
static final ExtensionConsumer shOnLoadConsumer = |
|
new SHSupportedVersionsConsumer(); |
|
static final SSLStringizer shStringizer = |
|
new SHSupportedVersionsStringizer(); |
|
|
|
static final HandshakeProducer hrrNetworkProducer = |
|
new HRRSupportedVersionsProducer(); |
|
static final ExtensionConsumer hrrOnLoadConsumer = |
|
new HRRSupportedVersionsConsumer(); |
|
static final HandshakeProducer hrrReproducer = |
|
new HRRSupportedVersionsReproducer(); |
|
static final SSLStringizer hrrStringizer = |
|
new SHSupportedVersionsStringizer(); |
|
|
|
|
|
*/ |
|
static final class CHSupportedVersionsSpec implements SSLExtensionSpec { |
|
final int[] requestedProtocols; |
|
|
|
private CHSupportedVersionsSpec(int[] requestedProtocols) { |
|
this.requestedProtocols = requestedProtocols; |
|
} |
|
|
|
private CHSupportedVersionsSpec(ByteBuffer m) throws IOException { |
|
if (m.remaining() < 3) { // 1: the length of the list |
|
|
|
throw new SSLProtocolException( |
|
"Invalid supported_versions extension: insufficient data"); |
|
} |
|
|
|
byte[] vbs = Record.getBytes8(m); |
|
if (m.hasRemaining()) { |
|
throw new SSLProtocolException( |
|
"Invalid supported_versions extension: unknown extra data"); |
|
} |
|
|
|
if (vbs == null || vbs.length == 0 || (vbs.length & 0x01) != 0) { |
|
throw new SSLProtocolException( |
|
"Invalid supported_versions extension: incomplete data"); |
|
} |
|
|
|
int[] protocols = new int[vbs.length >> 1]; |
|
for (int i = 0, j = 0; i < vbs.length;) { |
|
byte major = vbs[i++]; |
|
byte minor = vbs[i++]; |
|
protocols[j++] = ((major & 0xFF) << 8) | (minor & 0xFF); |
|
} |
|
|
|
this.requestedProtocols = protocols; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"versions\": '['{0}']'", Locale.ENGLISH); |
|
|
|
if (requestedProtocols == null || requestedProtocols.length == 0) { |
|
Object[] messageFields = { |
|
"<no supported version specified>" |
|
}; |
|
return messageFormat.format(messageFields); |
|
} else { |
|
StringBuilder builder = new StringBuilder(512); |
|
boolean isFirst = true; |
|
for (int pv : requestedProtocols) { |
|
if (isFirst) { |
|
isFirst = false; |
|
} else { |
|
builder.append(", "); |
|
} |
|
|
|
builder.append(ProtocolVersion.nameOf(pv)); |
|
} |
|
|
|
Object[] messageFields = { |
|
builder.toString() |
|
}; |
|
|
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
} |
|
|
|
private static final |
|
class CHSupportedVersionsStringizer implements SSLStringizer { |
|
@Override |
|
public String toString(ByteBuffer buffer) { |
|
try { |
|
return (new CHSupportedVersionsSpec(buffer)).toString(); |
|
} catch (IOException ioe) { |
|
|
|
return ioe.getMessage(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class CHSupportedVersionsProducer implements HandshakeProducer { |
|
|
|
private CHSupportedVersionsProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
if (!chc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable extension: " + |
|
CH_SUPPORTED_VERSIONS.name); |
|
} |
|
return null; |
|
} |
|
|
|
// Produce the extension. |
|
// |
|
|
|
int[] protocols = new int[chc.activeProtocols.size()]; |
|
int verLen = protocols.length * 2; |
|
byte[] extData = new byte[verLen + 1]; |
|
extData[0] = (byte)(verLen & 0xFF); |
|
int i = 0, j = 1; |
|
for (ProtocolVersion pv : chc.activeProtocols) { |
|
protocols[i++] = pv.id; |
|
extData[j++] = pv.major; |
|
extData[j++] = pv.minor; |
|
} |
|
|
|
|
|
chc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, |
|
new CHSupportedVersionsSpec(protocols)); |
|
|
|
return extData; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class CHSupportedVersionsConsumer implements ExtensionConsumer { |
|
|
|
private CHSupportedVersionsConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
if (!shc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable extension: " + |
|
CH_SUPPORTED_VERSIONS.name); |
|
} |
|
return; |
|
} |
|
|
|
|
|
CHSupportedVersionsSpec spec; |
|
try { |
|
spec = new CHSupportedVersionsSpec(buffer); |
|
} catch (IOException ioe) { |
|
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
} |
|
|
|
|
|
shc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, spec); |
|
|
|
// No impact on session resumption. |
|
// |
|
// Note that the protocol version negotiation happens before the |
|
// session resumption negotiation. And the session resumption |
|
// negotiation depends on the negotiated protocol version. |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
static final class SHSupportedVersionsSpec implements SSLExtensionSpec { |
|
final int selectedVersion; |
|
|
|
private SHSupportedVersionsSpec(ProtocolVersion selectedVersion) { |
|
this.selectedVersion = selectedVersion.id; |
|
} |
|
|
|
private SHSupportedVersionsSpec(ByteBuffer m) throws IOException { |
|
if (m.remaining() != 2) { |
|
throw new SSLProtocolException( |
|
"Invalid supported_versions: insufficient data"); |
|
} |
|
|
|
byte major = m.get(); |
|
byte minor = m.get(); |
|
this.selectedVersion = ((major & 0xFF) << 8) | (minor & 0xFF); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"selected version\": '['{0}']'", Locale.ENGLISH); |
|
|
|
Object[] messageFields = { |
|
ProtocolVersion.nameOf(selectedVersion) |
|
}; |
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
|
|
private static final |
|
class SHSupportedVersionsStringizer implements SSLStringizer { |
|
@Override |
|
public String toString(ByteBuffer buffer) { |
|
try { |
|
return (new SHSupportedVersionsSpec(buffer)).toString(); |
|
} catch (IOException ioe) { |
|
|
|
return ioe.getMessage(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class SHSupportedVersionsProducer implements HandshakeProducer { |
|
|
|
private SHSupportedVersionsProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
CHSupportedVersionsSpec svs = (CHSupportedVersionsSpec) |
|
shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS); |
|
if (svs == null) { |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.warning( |
|
"Ignore unavailable supported_versions extension"); |
|
} |
|
return null; |
|
} |
|
|
|
|
|
if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable extension: " + |
|
SH_SUPPORTED_VERSIONS.name); |
|
} |
|
return null; |
|
} |
|
|
|
|
|
byte[] extData = new byte[2]; |
|
extData[0] = shc.negotiatedProtocol.major; |
|
extData[1] = shc.negotiatedProtocol.minor; |
|
|
|
|
|
shc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, |
|
new SHSupportedVersionsSpec(shc.negotiatedProtocol)); |
|
|
|
return extData; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class SHSupportedVersionsConsumer implements ExtensionConsumer { |
|
|
|
private SHSupportedVersionsConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
if (!chc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable extension: " + |
|
SH_SUPPORTED_VERSIONS.name); |
|
} |
|
return; |
|
} |
|
|
|
|
|
SHSupportedVersionsSpec spec; |
|
try { |
|
spec = new SHSupportedVersionsSpec(buffer); |
|
} catch (IOException ioe) { |
|
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
} |
|
|
|
|
|
chc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, spec); |
|
|
|
// No impact on session resumption. |
|
// |
|
// Note that the protocol version negotiation happens before the |
|
// session resumption negotiation. And the session resumption |
|
// negotiation depends on the negotiated protocol version. |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class HRRSupportedVersionsProducer implements HandshakeProducer { |
|
|
|
|
|
private HRRSupportedVersionsProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable extension: " + |
|
HRR_SUPPORTED_VERSIONS.name); |
|
} |
|
return null; |
|
} |
|
|
|
|
|
byte[] extData = new byte[2]; |
|
extData[0] = shc.negotiatedProtocol.major; |
|
extData[1] = shc.negotiatedProtocol.minor; |
|
|
|
|
|
shc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, |
|
new SHSupportedVersionsSpec(shc.negotiatedProtocol)); |
|
|
|
return extData; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class HRRSupportedVersionsConsumer implements ExtensionConsumer { |
|
|
|
|
|
private HRRSupportedVersionsConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
|
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
if (!chc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable extension: " + |
|
HRR_SUPPORTED_VERSIONS.name); |
|
} |
|
return; |
|
} |
|
|
|
|
|
SHSupportedVersionsSpec spec; |
|
try { |
|
spec = new SHSupportedVersionsSpec(buffer); |
|
} catch (IOException ioe) { |
|
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
} |
|
|
|
|
|
chc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, spec); |
|
|
|
// No impact on session resumption. |
|
// |
|
// Note that the protocol version negotiation happens before the |
|
// session resumption negotiation. And the session resumption |
|
// negotiation depends on the negotiated protocol version. |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class HRRSupportedVersionsReproducer implements HandshakeProducer { |
|
|
|
private HRRSupportedVersionsReproducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"[Reproduce] Ignore unavailable extension: " + |
|
HRR_SUPPORTED_VERSIONS.name); |
|
} |
|
return null; |
|
} |
|
|
|
|
|
byte[] extData = new byte[2]; |
|
extData[0] = shc.negotiatedProtocol.major; |
|
extData[1] = shc.negotiatedProtocol.minor; |
|
|
|
return extData; |
|
} |
|
} |
|
} |