|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.io.IOException; |
|
import java.nio.ByteBuffer; |
|
import javax.net.ssl.SSLProtocolException; |
|
import static sun.security.ssl.SSLExtension.CH_MAX_FRAGMENT_LENGTH; |
|
import static sun.security.ssl.SSLExtension.EE_MAX_FRAGMENT_LENGTH; |
|
import sun.security.ssl.SSLExtension.ExtensionConsumer; |
|
import static sun.security.ssl.SSLExtension.SH_MAX_FRAGMENT_LENGTH; |
|
import sun.security.ssl.SSLExtension.SSLExtensionSpec; |
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
|
|
|
|
|
|
*/ |
|
final class MaxFragExtension { |
|
static final HandshakeProducer chNetworkProducer = |
|
new CHMaxFragmentLengthProducer(); |
|
static final ExtensionConsumer chOnLoadConsumer = |
|
new CHMaxFragmentLengthConsumer(); |
|
|
|
static final HandshakeProducer shNetworkProducer = |
|
new SHMaxFragmentLengthProducer(); |
|
static final ExtensionConsumer shOnLoadConsumer = |
|
new SHMaxFragmentLengthConsumer(); |
|
static final HandshakeConsumer shOnTradeConsumer = |
|
new SHMaxFragmentLengthUpdate(); |
|
|
|
static final HandshakeProducer eeNetworkProducer = |
|
new EEMaxFragmentLengthProducer(); |
|
static final ExtensionConsumer eeOnLoadConsumer = |
|
new EEMaxFragmentLengthConsumer(); |
|
static final HandshakeConsumer eeOnTradeConsumer = |
|
new EEMaxFragmentLengthUpdate(); |
|
|
|
static final SSLStringizer maxFragLenStringizer = |
|
new MaxFragLenStringizer(); |
|
|
|
|
|
|
|
*/ |
|
static final class MaxFragLenSpec implements SSLExtensionSpec { |
|
byte id; |
|
|
|
private MaxFragLenSpec(byte id) { |
|
this.id = id; |
|
} |
|
|
|
private MaxFragLenSpec(ByteBuffer buffer) throws IOException { |
|
if (buffer.remaining() != 1) { |
|
throw new SSLProtocolException( |
|
"Invalid max_fragment_length extension data"); |
|
} |
|
|
|
this.id = buffer.get(); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return MaxFragLenEnum.nameOf(id); |
|
} |
|
} |
|
|
|
private static final class MaxFragLenStringizer implements SSLStringizer { |
|
@Override |
|
public String toString(ByteBuffer buffer) { |
|
try { |
|
return (new MaxFragLenSpec(buffer)).toString(); |
|
} catch (IOException ioe) { |
|
|
|
return ioe.getMessage(); |
|
} |
|
} |
|
} |
|
|
|
static enum MaxFragLenEnum { |
|
MFL_512 ((byte)0x01, 512, "2^9"), |
|
MFL_1024 ((byte)0x02, 1024, "2^10"), |
|
MFL_2048 ((byte)0x03, 2048, "2^11"), |
|
MFL_4096 ((byte)0x04, 4096, "2^12"); |
|
|
|
final byte id; |
|
final int fragmentSize; |
|
final String description; |
|
|
|
private MaxFragLenEnum(byte id, int fragmentSize, String description) { |
|
this.id = id; |
|
this.fragmentSize = fragmentSize; |
|
this.description = description; |
|
} |
|
|
|
private static MaxFragLenEnum valueOf(byte id) { |
|
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) { |
|
if (mfl.id == id) { |
|
return mfl; |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
private static String nameOf(byte id) { |
|
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) { |
|
if (mfl.id == id) { |
|
return mfl.description; |
|
} |
|
} |
|
|
|
return "UNDEFINED-MAX-FRAGMENT-LENGTH(" + id + ")"; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
static MaxFragLenEnum valueOf(int fragmentSize) { |
|
if (fragmentSize <= 0) { |
|
return null; |
|
} else if (fragmentSize < 1024) { |
|
return MFL_512; |
|
} else if (fragmentSize < 2048) { |
|
return MFL_1024; |
|
} else if (fragmentSize < 4096) { |
|
return MFL_2048; |
|
} else if (fragmentSize == 4096) { |
|
return MFL_4096; |
|
} |
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class CHMaxFragmentLengthProducer implements HandshakeProducer { |
|
|
|
private CHMaxFragmentLengthProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
if (!chc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable max_fragment_length extension"); |
|
} |
|
return null; |
|
} |
|
|
|
|
|
int requestedMFLength; |
|
if (chc.isResumption && (chc.resumingSession != null)) { |
|
|
|
requestedMFLength = |
|
chc.resumingSession.getNegotiatedMaxFragSize(); |
|
} else if (chc.sslConfig.maximumPacketSize != 0) { |
|
// Maybe we can calculate the fragment size more accurate |
|
|
|
requestedMFLength = chc.sslConfig.maximumPacketSize; |
|
if (chc.sslContext.isDTLS()) { |
|
requestedMFLength -= DTLSRecord.maxPlaintextPlusSize; |
|
} else { |
|
requestedMFLength -= SSLRecord.maxPlaintextPlusSize; |
|
} |
|
} else { |
|
|
|
requestedMFLength = -1; |
|
} |
|
|
|
MaxFragLenEnum mfl = MaxFragLenEnum.valueOf(requestedMFLength); |
|
if (mfl != null) { |
|
|
|
chc.handshakeExtensions.put( |
|
CH_MAX_FRAGMENT_LENGTH, new MaxFragLenSpec(mfl.id)); |
|
|
|
return new byte[] { mfl.id }; |
|
} else { |
|
|
|
chc.maxFragmentLength = -1; |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"No available max_fragment_length extension can " + |
|
"be used for fragment size of " + |
|
requestedMFLength + "bytes"); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class CHMaxFragmentLengthConsumer implements ExtensionConsumer { |
|
|
|
private CHMaxFragmentLengthConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
if (!shc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Ignore unavailable max_fragment_length extension"); |
|
} |
|
return; |
|
} |
|
|
|
|
|
MaxFragLenSpec spec; |
|
try { |
|
spec = new MaxFragLenSpec(buffer); |
|
} catch (IOException ioe) { |
|
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
return; |
|
} |
|
|
|
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id); |
|
if (mfle == null) { |
|
shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"the requested maximum fragment length is other " + |
|
"than the allowed values"); |
|
} |
|
|
|
|
|
shc.maxFragmentLength = mfle.fragmentSize; |
|
shc.handshakeExtensions.put(CH_MAX_FRAGMENT_LENGTH, spec); |
|
|
|
// No impact on session resumption. |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class SHMaxFragmentLengthProducer implements HandshakeProducer { |
|
|
|
private SHMaxFragmentLengthProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
MaxFragLenSpec spec = (MaxFragLenSpec) |
|
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); |
|
if (spec == null) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.finest( |
|
"Ignore unavailable max_fragment_length extension"); |
|
} |
|
return null; |
|
} |
|
|
|
if ((shc.maxFragmentLength > 0) && |
|
(shc.sslConfig.maximumPacketSize != 0)) { |
|
int estimatedMaxFragSize = |
|
shc.negotiatedCipherSuite.calculatePacketSize( |
|
shc.maxFragmentLength, shc.negotiatedProtocol, |
|
shc.sslContext.isDTLS()); |
|
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) { |
|
// For better interoperability, abort the maximum |
|
// fragment length negotiation, rather than terminate |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Abort the maximum fragment length negotiation, " + |
|
"may overflow the maximum packet size limit."); |
|
} |
|
shc.maxFragmentLength = -1; |
|
} |
|
} |
|
|
|
|
|
if (shc.maxFragmentLength > 0) { |
|
shc.handshakeSession.setNegotiatedMaxFragSize( |
|
shc.maxFragmentLength); |
|
shc.conContext.inputRecord.changeFragmentSize( |
|
shc.maxFragmentLength); |
|
shc.conContext.outputRecord.changeFragmentSize( |
|
shc.maxFragmentLength); |
|
|
|
|
|
shc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec); |
|
return new byte[] { spec.id }; |
|
} |
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class SHMaxFragmentLengthConsumer implements ExtensionConsumer { |
|
|
|
private SHMaxFragmentLengthConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
|
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
MaxFragLenSpec requestedSpec = (MaxFragLenSpec) |
|
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); |
|
if (requestedSpec == null) { |
|
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, |
|
"Unexpected max_fragment_length extension in ServerHello"); |
|
} |
|
|
|
|
|
MaxFragLenSpec spec; |
|
try { |
|
spec = new MaxFragLenSpec(buffer); |
|
} catch (IOException ioe) { |
|
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
return; |
|
} |
|
|
|
if (spec.id != requestedSpec.id) { |
|
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"The maximum fragment length response is not requested"); |
|
} |
|
|
|
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id); |
|
if (mfle == null) { |
|
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"the requested maximum fragment length is other " + |
|
"than the allowed values"); |
|
} |
|
|
|
|
|
chc.maxFragmentLength = mfle.fragmentSize; |
|
chc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final class SHMaxFragmentLengthUpdate |
|
implements HandshakeConsumer { |
|
|
|
|
|
private SHMaxFragmentLengthUpdate() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
MaxFragLenSpec spec = (MaxFragLenSpec) |
|
chc.handshakeExtensions.get(SH_MAX_FRAGMENT_LENGTH); |
|
if (spec == null) { |
|
|
|
return; |
|
} |
|
|
|
if ((chc.maxFragmentLength > 0) && |
|
(chc.sslConfig.maximumPacketSize != 0)) { |
|
int estimatedMaxFragSize = |
|
chc.negotiatedCipherSuite.calculatePacketSize( |
|
chc.maxFragmentLength, chc.negotiatedProtocol, |
|
chc.sslContext.isDTLS()); |
|
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) { |
|
// For better interoperability, abort the maximum |
|
// fragment length negotiation, rather than terminate |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Abort the maximum fragment length negotiation, " + |
|
"may overflow the maximum packet size limit."); |
|
} |
|
chc.maxFragmentLength = -1; |
|
} |
|
} |
|
|
|
|
|
if (chc.maxFragmentLength > 0) { |
|
chc.handshakeSession.setNegotiatedMaxFragSize( |
|
chc.maxFragmentLength); |
|
chc.conContext.inputRecord.changeFragmentSize( |
|
chc.maxFragmentLength); |
|
chc.conContext.outputRecord.changeFragmentSize( |
|
chc.maxFragmentLength); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class EEMaxFragmentLengthProducer implements HandshakeProducer { |
|
|
|
private EEMaxFragmentLengthProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
MaxFragLenSpec spec = (MaxFragLenSpec) |
|
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); |
|
if (spec == null) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.finest( |
|
"Ignore unavailable max_fragment_length extension"); |
|
} |
|
return null; |
|
} |
|
|
|
if ((shc.maxFragmentLength > 0) && |
|
(shc.sslConfig.maximumPacketSize != 0)) { |
|
int estimatedMaxFragSize = |
|
shc.negotiatedCipherSuite.calculatePacketSize( |
|
shc.maxFragmentLength, shc.negotiatedProtocol, |
|
shc.sslContext.isDTLS()); |
|
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) { |
|
// For better interoperability, abort the maximum |
|
// fragment length negotiation, rather than terminate |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Abort the maximum fragment length negotiation, " + |
|
"may overflow the maximum packet size limit."); |
|
} |
|
shc.maxFragmentLength = -1; |
|
} |
|
} |
|
|
|
|
|
if (shc.maxFragmentLength > 0) { |
|
shc.handshakeSession.setNegotiatedMaxFragSize( |
|
shc.maxFragmentLength); |
|
shc.conContext.inputRecord.changeFragmentSize( |
|
shc.maxFragmentLength); |
|
shc.conContext.outputRecord.changeFragmentSize( |
|
shc.maxFragmentLength); |
|
|
|
|
|
shc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec); |
|
return new byte[] { spec.id }; |
|
} |
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class EEMaxFragmentLengthConsumer implements ExtensionConsumer { |
|
|
|
private EEMaxFragmentLengthConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message, ByteBuffer buffer) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
MaxFragLenSpec requestedSpec = (MaxFragLenSpec) |
|
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); |
|
if (requestedSpec == null) { |
|
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, |
|
"Unexpected max_fragment_length extension in ServerHello"); |
|
} |
|
|
|
|
|
MaxFragLenSpec spec; |
|
try { |
|
spec = new MaxFragLenSpec(buffer); |
|
} catch (IOException ioe) { |
|
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
return; |
|
} |
|
|
|
if (spec.id != requestedSpec.id) { |
|
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"The maximum fragment length response is not requested"); |
|
} |
|
|
|
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id); |
|
if (mfle == null) { |
|
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"the requested maximum fragment length is other " + |
|
"than the allowed values"); |
|
} |
|
|
|
|
|
chc.maxFragmentLength = mfle.fragmentSize; |
|
chc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class EEMaxFragmentLengthUpdate implements HandshakeConsumer { |
|
|
|
private EEMaxFragmentLengthUpdate() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
MaxFragLenSpec spec = (MaxFragLenSpec) |
|
chc.handshakeExtensions.get(EE_MAX_FRAGMENT_LENGTH); |
|
if (spec == null) { |
|
|
|
return; |
|
} |
|
|
|
if ((chc.maxFragmentLength > 0) && |
|
(chc.sslConfig.maximumPacketSize != 0)) { |
|
int estimatedMaxFragSize = |
|
chc.negotiatedCipherSuite.calculatePacketSize( |
|
chc.maxFragmentLength, chc.negotiatedProtocol, |
|
chc.sslContext.isDTLS()); |
|
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) { |
|
// For better interoperability, abort the maximum |
|
// fragment length negotiation, rather than terminate |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Abort the maximum fragment length negotiation, " + |
|
"may overflow the maximum packet size limit."); |
|
} |
|
chc.maxFragmentLength = -1; |
|
} |
|
} |
|
|
|
|
|
if (chc.maxFragmentLength > 0) { |
|
chc.handshakeSession.setNegotiatedMaxFragSize( |
|
chc.maxFragmentLength); |
|
chc.conContext.inputRecord.changeFragmentSize( |
|
chc.maxFragmentLength); |
|
chc.conContext.outputRecord.changeFragmentSize( |
|
chc.maxFragmentLength); |
|
} |
|
} |
|
} |
|
} |