|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.io.IOException; |
|
import java.nio.ByteBuffer; |
|
import java.text.MessageFormat; |
|
import java.util.Locale; |
|
import sun.security.ssl.ClientHello.ClientHelloMessage; |
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
|
|
|
|
|
|
*/ |
|
final class HelloVerifyRequest { |
|
static final SSLConsumer handshakeConsumer = |
|
new HelloVerifyRequestConsumer(); |
|
static final HandshakeProducer handshakeProducer = |
|
new HelloVerifyRequestProducer(); |
|
|
|
|
|
|
|
*/ |
|
static final class HelloVerifyRequestMessage extends HandshakeMessage { |
|
final int serverVersion; |
|
final byte[] cookie; |
|
|
|
HelloVerifyRequestMessage(HandshakeContext context, |
|
HandshakeMessage message) throws IOException { |
|
super(context); |
|
|
|
ServerHandshakeContext shc = |
|
(ServerHandshakeContext)context; |
|
ClientHelloMessage clientHello = (ClientHelloMessage)message; |
|
|
|
HelloCookieManager hcMgr = |
|
shc.sslContext.getHelloCookieManager(ProtocolVersion.DTLS10); |
|
this.serverVersion = shc.clientHelloVersion; |
|
this.cookie = hcMgr.createCookie(shc, clientHello); |
|
} |
|
|
|
HelloVerifyRequestMessage(HandshakeContext context, |
|
ByteBuffer m) throws IOException { |
|
super(context); |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
// struct { |
|
// ProtocolVersion server_version; |
|
// opaque cookie<0..2^8-1>; |
|
|
|
if (m.remaining() < 3) { |
|
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Invalid HelloVerifyRequest: no sufficient data"); |
|
} |
|
|
|
byte major = m.get(); |
|
byte minor = m.get(); |
|
this.serverVersion = ((major & 0xFF) << 8) | (minor & 0xFF); |
|
this.cookie = Record.getBytes8(m); |
|
} |
|
|
|
@Override |
|
public SSLHandshake handshakeType() { |
|
return SSLHandshake.HELLO_VERIFY_REQUEST; |
|
} |
|
|
|
@Override |
|
public int messageLength() { |
|
return 3 + cookie.length; |
|
// +1: the cookie length |
|
} |
|
|
|
@Override |
|
public void send(HandshakeOutStream hos) throws IOException { |
|
hos.putInt8((byte)((serverVersion >>> 8) & 0xFF)); |
|
hos.putInt8((byte)(serverVersion & 0xFF)); |
|
hos.putBytes8(cookie); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"HelloVerifyRequest\": '{'\n" + |
|
" \"server version\" : \"{0}\",\n" + |
|
" \"cookie\" : \"{1}\",\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
Object[] messageFields = { |
|
ProtocolVersion.nameOf(serverVersion), |
|
Utilities.toHexString(cookie), |
|
}; |
|
|
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class HelloVerifyRequestProducer implements HandshakeProducer { |
|
|
|
private HelloVerifyRequestProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
|
|
shc.handshakeProducers.remove(SSLHandshake.HELLO_VERIFY_REQUEST.id); |
|
|
|
HelloVerifyRequestMessage hvrm = |
|
new HelloVerifyRequestMessage(shc, message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Produced HelloVerifyRequest handshake message", hvrm); |
|
} |
|
|
|
|
|
hvrm.write(shc.handshakeOutput); |
|
shc.handshakeOutput.flush(); |
|
|
|
// update the context |
|
|
|
// Stateless, clean up the handshake context as well? |
|
shc.handshakeHash.finish(); |
|
shc.handshakeExtensions.clear(); |
|
|
|
|
|
shc.handshakeConsumers.put( |
|
SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO); |
|
|
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final class HelloVerifyRequestConsumer |
|
implements SSLConsumer { |
|
|
|
|
|
private HelloVerifyRequestConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
ByteBuffer message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
|
|
chc.handshakeConsumers.remove(SSLHandshake.HELLO_VERIFY_REQUEST.id); |
|
if (!chc.handshakeConsumers.isEmpty()) { |
|
chc.handshakeConsumers.remove(SSLHandshake.SERVER_HELLO.id); |
|
} |
|
if (!chc.handshakeConsumers.isEmpty()) { |
|
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, |
|
"No more message expected before " + |
|
"HelloVerifyRequest is processed"); |
|
} |
|
|
|
// Refresh handshake hash. |
|
chc.handshakeHash.finish(); |
|
|
|
HelloVerifyRequestMessage hvrm = |
|
new HelloVerifyRequestMessage(chc, message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Consuming HelloVerifyRequest handshake message", hvrm); |
|
} |
|
|
|
// Note that HelloVerifyRequest.server_version is used solely to |
|
// indicate packet formatting, and not as part of version |
|
// negotiation. Need not to check version values match for |
|
|
|
chc.initialClientHelloMsg.setHelloCookie(hvrm.cookie); |
|
|
|
// |
|
// produce response handshake message |
|
|
|
SSLHandshake.CLIENT_HELLO.produce(context, hvrm); |
|
} |
|
} |
|
} |
|
|