8206929: Check session context for TLS 1.3 session resumption

Additional checks to prevent TLS 1.3 sessions from being resumed when they shouldn't

Reviewed-by: xuelei
This commit is contained in:
Adam Petcher 2018-07-17 13:04:40 -04:00
parent 2c82c9e1bd
commit 108461949f
5 changed files with 676 additions and 18 deletions

View file

@ -27,6 +27,7 @@ package sun.security.ssl;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
@ -46,6 +47,9 @@ final class PostHandshakeContext extends HandshakeContext {
"Post-handshake not supported in " + negotiatedProtocol.name);
}
this.localSupportedSignAlgs = new ArrayList<SignatureScheme>(
context.conSession.getLocalSupportedSignatureSchemes());
handshakeConsumers = new LinkedHashMap<>(consumers);
handshakeFinished = true;
}

View file

@ -33,8 +33,11 @@ import java.util.ArrayList;
import java.util.Locale;
import java.util.Arrays;
import java.util.Optional;
import java.util.Collection;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLPeerUnverifiedException;
import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
import sun.security.ssl.ClientHello.ClientHelloMessage;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
@ -167,7 +170,7 @@ final class PreSharedKeyExtension {
int getIdsEncodedLength() {
int idEncodedLength = 0;
for(PskIdentity curId : identities) {
for (PskIdentity curId : identities) {
idEncodedLength += curId.getEncodedLength();
}
@ -190,7 +193,7 @@ final class PreSharedKeyExtension {
byte[] buffer = new byte[encodedLength];
ByteBuffer m = ByteBuffer.wrap(buffer);
Record.putInt16(m, idsEncodedLength);
for(PskIdentity curId : identities) {
for (PskIdentity curId : identities) {
curId.writeEncoded(m);
}
Record.putInt16(m, bindersEncodedLength);
@ -220,7 +223,7 @@ final class PreSharedKeyExtension {
String identitiesString() {
StringBuilder result = new StringBuilder();
for(PskIdentity curId : identities) {
for (PskIdentity curId : identities) {
result.append(curId.toString() + "\n");
}
@ -229,7 +232,7 @@ final class PreSharedKeyExtension {
String bindersString() {
StringBuilder result = new StringBuilder();
for(byte[] curBinder : binders) {
for (byte[] curBinder : binders) {
result.append("{" + Utilities.toHexString(curBinder) + "}\n");
}
@ -328,6 +331,7 @@ final class PreSharedKeyExtension {
public void consume(ConnectionContext context,
HandshakeMessage message,
ByteBuffer buffer) throws IOException {
ClientHelloMessage clientHello = (ClientHelloMessage) message;
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) {
@ -367,8 +371,7 @@ final class PreSharedKeyExtension {
int idIndex = 0;
for (PskIdentity requestedId : pskSpec.identities) {
SSLSessionImpl s = sessionCache.get(requestedId.identity);
if (s != null && s.isRejoinable() &&
s.getPreSharedKey().isPresent()) {
if (s != null && canRejoin(clientHello, shc, s)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Resuming session: ", s);
}
@ -392,10 +395,68 @@ final class PreSharedKeyExtension {
// update the context
shc.handshakeExtensions.put(
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
}
}
private static boolean canRejoin(ClientHelloMessage clientHello,
ServerHandshakeContext shc, SSLSessionImpl s) {
boolean result = s.isRejoinable() && s.getPreSharedKey().isPresent();
// Check protocol version
if (result && s.getProtocolVersion() != shc.negotiatedProtocol) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest("Can't resume, incorrect protocol version");
}
result = false;
}
// Validate the required client authentication.
if (result &&
(shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) {
try {
s.getPeerPrincipal();
} catch (SSLPeerUnverifiedException e) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest(
"Can't resume, " +
"client authentication is required");
}
result = false;
}
// Make sure the list of supported signature algorithms matches
Collection<SignatureScheme> sessionSigAlgs =
s.getLocalSupportedSignatureSchemes();
if (result &&
!shc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Can't resume. Session uses different " +
"signature algorithms");
}
result = false;
}
}
// Ensure cipher suite can be negotiated
if (result && (!shc.isNegotiable(s.getSuite()) ||
!clientHello.cipherSuites.contains(s.getSuite()))) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest(
"Can't resume, unavailable session cipher suite");
}
result = false;
}
return result;
}
private static final
class CHPreSharedKeyUpdate implements HandshakeConsumer {
// Prevent instantiation of this class.
@ -547,6 +608,18 @@ final class PreSharedKeyExtension {
return null;
}
// Make sure the list of supported signature algorithms matches
Collection<SignatureScheme> sessionSigAlgs =
chc.resumingSession.getLocalSupportedSignatureSchemes();
if (!chc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Existing session uses different " +
"signature algorithms");
}
return null;
}
// The session must have a pre-shared key
Optional<SecretKey> pskOpt = chc.resumingSession.getPreSharedKey();
if (!pskOpt.isPresent()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@ -658,7 +731,7 @@ final class PreSharedKeyExtension {
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
throw new IOException(ex);
}
} catch(GeneralSecurityException ex) {
} catch (GeneralSecurityException ex) {
throw new IOException(ex);
}
}

View file

@ -96,7 +96,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
private boolean invalidated;
private X509Certificate[] localCerts;
private PrivateKey localPrivateKey;
private final String[] localSupportedSignAlgs;
private final Collection<SignatureScheme> localSupportedSignAlgs;
private String[] peerSupportedSignAlgs; // for certificate
private boolean useDefaultPeerSignAlgs = false;
private List<byte[]> statusResponses;
@ -144,7 +144,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
this.sessionId = new SessionId(false, null);
this.host = null;
this.port = -1;
this.localSupportedSignAlgs = new String[0];
this.localSupportedSignAlgs = Collections.emptySet();
this.serverNameIndication = null;
this.requestedServerNames = Collections.<SNIServerName>emptyList();
this.useExtendedMasterSecret = false;
@ -179,8 +179,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
this.sessionId = id;
this.host = hc.conContext.transport.getPeerHost();
this.port = hc.conContext.transport.getPeerPort();
this.localSupportedSignAlgs =
SignatureScheme.getAlgorithmNames(hc.localSupportedSignAlgs);
this.localSupportedSignAlgs = hc.localSupportedSignAlgs == null ?
Collections.emptySet() :
Collections.unmodifiableCollection(hc.localSupportedSignAlgs);
this.serverNameIndication = hc.negotiatedServerName;
this.requestedServerNames = Collections.<SNIServerName>unmodifiableList(
hc.getRequestedServerNames());
@ -969,16 +970,20 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
/**
* Gets an array of supported signature algorithms that the local side is
* willing to verify.
* Gets an array of supported signature algorithm names that the local
* side is willing to verify.
*/
@Override
public String[] getLocalSupportedSignatureAlgorithms() {
if (localSupportedSignAlgs != null) {
return localSupportedSignAlgs.clone();
}
return SignatureScheme.getAlgorithmNames(localSupportedSignAlgs);
}
return new String[0];
/**
* Gets an array of supported signature schemes that the local side is
* willing to verify.
*/
public Collection<SignatureScheme> getLocalSupportedSignatureSchemes() {
return localSupportedSignAlgs;
}
/**