|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.io.IOException; |
|
import java.nio.ByteBuffer; |
|
import java.security.GeneralSecurityException; |
|
import java.text.MessageFormat; |
|
import java.util.Locale; |
|
|
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
import sun.security.ssl.SSLCipher.SSLReadCipher; |
|
import sun.security.ssl.SSLCipher.SSLWriteCipher; |
|
|
|
import javax.crypto.SecretKey; |
|
import javax.crypto.spec.IvParameterSpec; |
|
|
|
|
|
|
|
*/ |
|
final class KeyUpdate { |
|
static final SSLProducer kickstartProducer = |
|
new KeyUpdateKickstartProducer(); |
|
|
|
static final SSLConsumer handshakeConsumer = |
|
new KeyUpdateConsumer(); |
|
static final HandshakeProducer handshakeProducer = |
|
new KeyUpdateProducer(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final class KeyUpdateMessage extends HandshakeMessage { |
|
private final KeyUpdateRequest status; |
|
|
|
KeyUpdateMessage(PostHandshakeContext context, |
|
KeyUpdateRequest status) { |
|
super(context); |
|
this.status = status; |
|
} |
|
|
|
KeyUpdateMessage(PostHandshakeContext context, |
|
ByteBuffer m) throws IOException { |
|
super(context); |
|
|
|
if (m.remaining() != 1) { |
|
context.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"KeyUpdate has an unexpected length of "+ |
|
m.remaining()); |
|
} |
|
|
|
byte request = m.get(); |
|
this.status = KeyUpdateRequest.valueOf(request); |
|
if (status == null) { |
|
context.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Invalid KeyUpdate message value: " + |
|
KeyUpdateRequest.nameOf(request)); |
|
} |
|
} |
|
|
|
@Override |
|
public SSLHandshake handshakeType() { |
|
return SSLHandshake.KEY_UPDATE; |
|
} |
|
|
|
@Override |
|
public int messageLength() { |
|
|
|
return 1; |
|
} |
|
|
|
@Override |
|
public void send(HandshakeOutStream s) throws IOException { |
|
s.putInt8(status.id); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"KeyUpdate\": '{'\n" + |
|
" \"request_update\": {0}\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
|
|
Object[] messageFields = { |
|
status.name |
|
}; |
|
|
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
|
|
enum KeyUpdateRequest { |
|
NOTREQUESTED ((byte)0, "update_not_requested"), |
|
REQUESTED ((byte)1, "update_requested"); |
|
|
|
final byte id; |
|
final String name; |
|
|
|
private KeyUpdateRequest(byte id, String name) { |
|
this.id = id; |
|
this.name = name; |
|
} |
|
|
|
static KeyUpdateRequest valueOf(byte id) { |
|
for (KeyUpdateRequest kur : KeyUpdateRequest.values()) { |
|
if (kur.id == id) { |
|
return kur; |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
static String nameOf(byte id) { |
|
for (KeyUpdateRequest kur : KeyUpdateRequest.values()) { |
|
if (kur.id == id) { |
|
return kur.name; |
|
} |
|
} |
|
|
|
return "<UNKNOWN KeyUpdateRequest TYPE: " + (id & 0x0FF) + ">"; |
|
} |
|
} |
|
|
|
private static final |
|
class KeyUpdateKickstartProducer implements SSLProducer { |
|
|
|
private KeyUpdateKickstartProducer() { |
|
// blank |
|
} |
|
|
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context) throws IOException { |
|
PostHandshakeContext hc = (PostHandshakeContext)context; |
|
return handshakeProducer.produce(context, |
|
new KeyUpdateMessage(hc, KeyUpdateRequest.REQUESTED)); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final class KeyUpdateConsumer implements SSLConsumer { |
|
|
|
private KeyUpdateConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
ByteBuffer message) throws IOException { |
|
|
|
PostHandshakeContext hc = (PostHandshakeContext)context; |
|
KeyUpdateMessage km = new KeyUpdateMessage(hc, message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Consuming KeyUpdate post-handshake message", km); |
|
} |
|
|
|
|
|
SSLTrafficKeyDerivation kdg = |
|
SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion); |
|
if (kdg == null) { |
|
|
|
hc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Not supported key derivation: " + |
|
hc.conContext.protocolVersion); |
|
return; |
|
} |
|
|
|
SSLKeyDerivation skd = kdg.createKeyDerivation(hc, |
|
hc.conContext.inputRecord.readCipher.baseSecret); |
|
if (skd == null) { |
|
|
|
hc.conContext.fatal(Alert.INTERNAL_ERROR, "no key derivation"); |
|
return; |
|
} |
|
|
|
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null); |
|
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1); |
|
SecretKey key = kd.deriveKey("TlsKey", null); |
|
IvParameterSpec ivSpec = new IvParameterSpec( |
|
kd.deriveKey("TlsIv", null).getEncoded()); |
|
try { |
|
SSLReadCipher rc = |
|
hc.negotiatedCipherSuite.bulkCipher.createReadCipher( |
|
Authenticator.valueOf(hc.conContext.protocolVersion), |
|
hc.conContext.protocolVersion, key, ivSpec, |
|
hc.sslContext.getSecureRandom()); |
|
rc.baseSecret = nplus1; |
|
hc.conContext.inputRecord.changeReadCiphers(rc); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
SSLLogger.fine("KeyUpdate: read key updated"); |
|
} |
|
} catch (GeneralSecurityException gse) { |
|
hc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Failure to derive read secrets", gse); |
|
return; |
|
} |
|
|
|
if (km.status == KeyUpdateRequest.REQUESTED) { |
|
|
|
handshakeProducer.produce(hc, |
|
new KeyUpdateMessage(hc, KeyUpdateRequest.NOTREQUESTED)); |
|
return; |
|
} |
|
|
|
|
|
hc.conContext.finishPostHandshake(); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final class KeyUpdateProducer implements HandshakeProducer { |
|
|
|
private KeyUpdateProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
PostHandshakeContext hc = (PostHandshakeContext)context; |
|
KeyUpdateMessage km = (KeyUpdateMessage)message; |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Produced KeyUpdate post-handshake message", km); |
|
} |
|
|
|
|
|
SSLTrafficKeyDerivation kdg = |
|
SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion); |
|
if (kdg == null) { |
|
|
|
hc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Not supported key derivation: " + |
|
hc.conContext.protocolVersion); |
|
return null; |
|
} |
|
|
|
SSLKeyDerivation skd = kdg.createKeyDerivation(hc, |
|
hc.conContext.outputRecord.writeCipher.baseSecret); |
|
if (skd == null) { |
|
|
|
hc.conContext.fatal(Alert.INTERNAL_ERROR, "no key derivation"); |
|
return null; |
|
} |
|
|
|
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null); |
|
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1); |
|
SecretKey key = kd.deriveKey("TlsKey", null); |
|
IvParameterSpec ivSpec = new IvParameterSpec( |
|
kd.deriveKey("TlsIv", null).getEncoded()); |
|
|
|
SSLWriteCipher wc; |
|
try { |
|
wc = hc.negotiatedCipherSuite.bulkCipher.createWriteCipher( |
|
Authenticator.valueOf(hc.conContext.protocolVersion), |
|
hc.conContext.protocolVersion, key, ivSpec, |
|
hc.sslContext.getSecureRandom()); |
|
} catch (GeneralSecurityException gse) { |
|
hc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Failure to derive write secrets", gse); |
|
return null; |
|
} |
|
|
|
// Output the handshake message and change the write cipher. |
|
// |
|
// The KeyUpdate handshake message SHALL be delivered in the |
|
|
|
wc.baseSecret = nplus1; |
|
hc.conContext.outputRecord.changeWriteCiphers(wc, km.status.id); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
SSLLogger.fine("KeyUpdate: write key updated"); |
|
} |
|
|
|
|
|
hc.conContext.finishPostHandshake(); |
|
|
|
|
|
return null; |
|
} |
|
} |
|
} |