Refactoring externalization of SSLSessions (#383)
This is an implementation to #381. This change attempts to provide more
consistency to the session that is returned to the caller by `ConscryptEngine`/`ConscryptFileDescriptorSocket`.
Main changes:
- New interface ConscryptSession adds a few methods currently only defined by ActiveSession
- New interface SessionDecorator that defines getDelegate()
- New class ProvidedSessionDecorator delegates to an external provider of the "current" session. The provider implementations are in ConscryptEngine and ConscryptFileDescriptorSocket.
- New class SessionSnapshot that takes a snapshot of any ConscryptSession.
- Changed ActiveSession and SSLNullSession to implement ConscryptSession.
- Updated ConscryptEngine/ConscryptFileDescriptorSocket to create a SessionSnapshot when closing.
Additional cleanup:
- Split out Java7SessionWrapper into two classes: Java7ExtendedSSLSession and Java8ExtendedSSLSession. The Java 8 version no longer requires reflection and is more consistent with platform-specific code elsewhere. Both classes implement SessionDecorator.
- Renamed SslWrapper->NativeSsl and SslSessionWrapper->NativeSslSession for clarity, since the term "wrapper" was being overloaded.
Fixes #379
diff --git a/android/lint.xml b/android/lint.xml
index f1af8de..bc79476 100644
--- a/android/lint.xml
+++ b/android/lint.xml
@@ -2,7 +2,8 @@
<lint>
<!-- ExtendedSSLSession only gets instantiated in new APIs on Android. -->
<issue id="NewApi">
- <ignore path="**/org/conscrypt/Java7SessionWrapper.java" />
+ <ignore path="**/org/conscrypt/Java7ExtendedSSLSession.java" />
+ <ignore path="**/org/conscrypt/Java8ExtendedSSLSession.java" />
<ignore path="**/org/conscrypt/Java8EngineWrapper.java" />
<ignore path="**/org/conscrypt/Java8EngineSocket.java" />
<ignore path="**/org/conscrypt/Java8FileDescriptorSocket.java" />
diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java
index c2c6c8d..f17ba5e 100644
--- a/android/src/main/java/org/conscrypt/Platform.java
+++ b/android/src/main/java/org/conscrypt/Platform.java
@@ -116,7 +116,7 @@
}
}
- /*
+ /**
* Call Os.setsockoptTimeval via reflection.
*/
public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
@@ -849,24 +849,18 @@
return oid;
}
- /*
- * Pre-Java 8 backward compatibility.
+ /**
+ * Provides extended capabilities for the session if supported by the platform.
*/
-
- public static SSLSession wrapSSLSession(ActiveSession sslSession) {
- if (Build.VERSION.SDK_INT <= 23) {
- return sslSession;
+ public static SSLSession wrapSSLSession(ConscryptSession sslSession) {
+ if (Build.VERSION.SDK_INT >= 24) {
+ return new Java8ExtendedSSLSession(sslSession);
+ }
+ if (Build.VERSION.SDK_INT >= 19) {
+ return new Java7ExtendedSSLSession(sslSession);
}
- return new Java7SessionWrapper(sslSession);
- }
-
- public static SSLSession unwrapSSLSession(SSLSession sslSession) {
- if (Build.VERSION.SDK_INT <= 23) {
- return sslSession;
- }
-
- return Java7SessionWrapper.getDelegate(sslSession);
+ return sslSession;
}
public static String getOriginalHostNameFromInetAddress(InetAddress addr) {
diff --git a/common/src/main/java/org/conscrypt/AbstractSessionContext.java b/common/src/main/java/org/conscrypt/AbstractSessionContext.java
index 0cba09d..25a6b90 100644
--- a/common/src/main/java/org/conscrypt/AbstractSessionContext.java
+++ b/common/src/main/java/org/conscrypt/AbstractSessionContext.java
@@ -42,11 +42,11 @@
final long sslCtxNativePointer = NativeCrypto.SSL_CTX_new();
@SuppressWarnings("serial")
- private final Map<ByteArray, SslSessionWrapper> sessions =
- new LinkedHashMap<ByteArray, SslSessionWrapper>() {
+ private final Map<ByteArray, NativeSslSession> sessions =
+ new LinkedHashMap<ByteArray, NativeSslSession>() {
@Override
protected boolean removeEldestEntry(
- Map.Entry<ByteArray, SslSessionWrapper> eldest) {
+ Map.Entry<ByteArray, NativeSslSession> eldest) {
// NOTE: does not take into account any session that may have become
// invalid.
if (maximumSize > 0 && size() > maximumSize) {
@@ -74,13 +74,13 @@
@Override
public final Enumeration<byte[]> getIds() {
// Make a copy of the IDs.
- final Iterator<SslSessionWrapper> iter;
+ final Iterator<NativeSslSession> iter;
synchronized (sessions) {
- iter = Arrays.asList(sessions.values().toArray(new SslSessionWrapper[sessions.size()]))
+ iter = Arrays.asList(sessions.values().toArray(new NativeSslSession[sessions.size()]))
.iterator();
}
return new Enumeration<byte[]>() {
- private SslSessionWrapper next;
+ private NativeSslSession next;
@Override
public boolean hasMoreElements() {
@@ -88,7 +88,7 @@
return true;
}
while (iter.hasNext()) {
- SslSessionWrapper session = iter.next();
+ NativeSslSession session = iter.next();
if (session.isValid()) {
next = session;
return true;
@@ -120,7 +120,7 @@
throw new NullPointerException("sessionId");
}
ByteArray key = new ByteArray(sessionId);
- SslSessionWrapper session;
+ NativeSslSession session;
synchronized (sessions) {
session = sessions.get(key);
}
@@ -158,9 +158,9 @@
NativeCrypto.SSL_CTX_set_timeout(sslCtxNativePointer, Integer.MAX_VALUE);
}
- Iterator<SslSessionWrapper> i = sessions.values().iterator();
+ Iterator<NativeSslSession> i = sessions.values().iterator();
while (i.hasNext()) {
- SslSessionWrapper session = i.next();
+ NativeSslSession session = i.next();
// SSLSession's know their context and consult the
// timeout as part of their validity condition.
if (!session.isValid()) {
@@ -199,7 +199,7 @@
/**
* Adds the given session to the cache.
*/
- final void cacheSession(SslSessionWrapper session) {
+ final void cacheSession(NativeSslSession session) {
byte[] id = session.getId();
if (id == null || id.length == 0) {
return;
@@ -218,13 +218,13 @@
* Called for server sessions only. Retrieves the session by its ID. Overridden by
* {@link ServerSessionContext} to
*/
- final SslSessionWrapper getSessionFromCache(byte[] sessionId) {
+ final NativeSslSession getSessionFromCache(byte[] sessionId) {
if (sessionId == null) {
return null;
}
// First, look in the in-memory cache.
- SslSessionWrapper session;
+ NativeSslSession session;
synchronized (sessions) {
session = sessions.get(new ByteArray(sessionId));
}
@@ -242,7 +242,7 @@
*
* <p>Visible for extension only, not intended to be called directly.
*/
- abstract void onBeforeAddSession(SslSessionWrapper session);
+ abstract void onBeforeAddSession(NativeSslSession session);
/**
* Called when a session is about to be removed. Used by {@link ClientSessionContext}
@@ -250,14 +250,14 @@
*
* <p>Visible for extension only, not intended to be called directly.
*/
- abstract void onBeforeRemoveSession(SslSessionWrapper session);
+ abstract void onBeforeRemoveSession(NativeSslSession session);
/**
* Called for server sessions only. Retrieves the session by ID from the persistent cache.
*
* <p>Visible for extension only, not intended to be called directly.
*/
- abstract SslSessionWrapper getSessionFromPersistentCache(byte[] sessionId);
+ abstract NativeSslSession getSessionFromPersistentCache(byte[] sessionId);
/**
* Makes sure cache size is < maximumSize.
@@ -267,9 +267,9 @@
int size = sessions.size();
if (size > maximumSize) {
int removals = size - maximumSize;
- Iterator<SslSessionWrapper> i = sessions.values().iterator();
+ Iterator<NativeSslSession> i = sessions.values().iterator();
while (removals-- > 0) {
- SslSessionWrapper session = i.next();
+ NativeSslSession session = i.next();
onBeforeRemoveSession(session);
i.remove();
}
diff --git a/common/src/main/java/org/conscrypt/ActiveSession.java b/common/src/main/java/org/conscrypt/ActiveSession.java
index 67edfee..e9b695a 100644
--- a/common/src/main/java/org/conscrypt/ActiveSession.java
+++ b/common/src/main/java/org/conscrypt/ActiveSession.java
@@ -27,7 +27,6 @@
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionBindingEvent;
import javax.net.ssl.SSLSessionBindingListener;
import javax.net.ssl.SSLSessionContext;
@@ -36,8 +35,8 @@
* A session that is dedicated a single connection and operates directly on the underlying
* {@code SSL}.
*/
-final class ActiveSession implements SSLSession {
- private final SslWrapper ssl;
+final class ActiveSession implements ConscryptSession {
+ private final NativeSsl ssl;
private AbstractSessionContext sessionContext;
private byte[] id;
private long creationTime;
@@ -54,7 +53,7 @@
// lazy init for memory reasons
private Map<String, Object> values;
- ActiveSession(SslWrapper ssl, AbstractSessionContext sessionContext) {
+ ActiveSession(NativeSsl ssl, AbstractSessionContext sessionContext) {
this.ssl = checkNotNull(ssl, "ssl");
this.sessionContext = checkNotNull(sessionContext, "sessionContext");
}
@@ -119,8 +118,7 @@
* @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
* @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
*/
- /* @Override */
- @SuppressWarnings("MissingOverride") // For Pre-Java9 compatibility.
+ @Override
public List<byte[]> getStatusResponses() {
if (peerCertificateOcspData == null) {
return Collections.<byte[]>emptyList();
@@ -135,14 +133,16 @@
*
* @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
*/
- byte[] getPeerSignedCertificateTimestamp() {
+ @Override
+ public byte[] getPeerSignedCertificateTimestamp() {
if (peerTlsSctData == null) {
return null;
}
return peerTlsSctData.clone();
}
- String getRequestedServerName() {
+ @Override
+ public String getRequestedServerName() {
synchronized (ssl) {
return ssl.getRequestedServerName();
}
diff --git a/common/src/main/java/org/conscrypt/ClientSessionContext.java b/common/src/main/java/org/conscrypt/ClientSessionContext.java
index 66007f9..761223f 100644
--- a/common/src/main/java/org/conscrypt/ClientSessionContext.java
+++ b/common/src/main/java/org/conscrypt/ClientSessionContext.java
@@ -33,7 +33,7 @@
* access by holding a lock on sessionsByHostAndPort.
*/
@SuppressWarnings("serial")
- private final Map<HostAndPort, SslSessionWrapper> sessionsByHostAndPort = new HashMap<HostAndPort, SslSessionWrapper>();
+ private final Map<HostAndPort, NativeSslSession> sessionsByHostAndPort = new HashMap<HostAndPort, NativeSslSession>();
private SSLClientSessionCache persistentCache;
@@ -52,12 +52,12 @@
/**
* Gets the suitable session reference from the session cache container.
*/
- SslSessionWrapper getCachedSession(String hostName, int port, SSLParametersImpl sslParameters) {
+ NativeSslSession getCachedSession(String hostName, int port, SSLParametersImpl sslParameters) {
if (hostName == null) {
return null;
}
- SslSessionWrapper session = getSession(hostName, port);
+ NativeSslSession session = getSession(hostName, port);
if (session == null) {
return null;
}
@@ -100,13 +100,13 @@
* @param port of server
* @return cached session or null if none found
*/
- private SslSessionWrapper getSession(String host, int port) {
+ private NativeSslSession getSession(String host, int port) {
if (host == null) {
return null;
}
HostAndPort key = new HostAndPort(host, port);
- SslSessionWrapper session;
+ NativeSslSession session;
synchronized (sessionsByHostAndPort) {
session = sessionsByHostAndPort.get(key);
}
@@ -118,7 +118,7 @@
if (persistentCache != null) {
byte[] data = persistentCache.getSessionData(host, port);
if (data != null) {
- session = SslSessionWrapper.newInstance(this, data, host, port);
+ session = NativeSslSession.newInstance(this, data, host, port);
if (session != null && session.isValid()) {
synchronized (sessionsByHostAndPort) {
sessionsByHostAndPort.put(key, session);
@@ -132,7 +132,7 @@
}
@Override
- void onBeforeAddSession(SslSessionWrapper session) {
+ void onBeforeAddSession(NativeSslSession session) {
String host = session.getPeerHost();
int port = session.getPeerPort();
if (host == null) {
@@ -154,7 +154,7 @@
}
@Override
- void onBeforeRemoveSession(SslSessionWrapper session) {
+ void onBeforeRemoveSession(NativeSslSession session) {
String host = session.getPeerHost();
if (host == null) {
return;
@@ -167,7 +167,7 @@
}
@Override
- SslSessionWrapper getSessionFromPersistentCache(byte[] sessionId) {
+ NativeSslSession getSessionFromPersistentCache(byte[] sessionId) {
// Not implemented for clients.
return null;
}
diff --git a/common/src/main/java/org/conscrypt/ConscryptEngine.java b/common/src/main/java/org/conscrypt/ConscryptEngine.java
index 6c96b47..11c3d1f 100644
--- a/common/src/main/java/org/conscrypt/ConscryptEngine.java
+++ b/common/src/main/java/org/conscrypt/ConscryptEngine.java
@@ -91,7 +91,8 @@
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import org.conscrypt.NativeRef.SSL_SESSION;
-import org.conscrypt.SslWrapper.BioWrapper;
+import org.conscrypt.NativeSsl.BioWrapper;
+import org.conscrypt.ProvidedSessionDecorator.Provider;
/**
* Implements the {@link SSLEngine} API using OpenSSL's non-blocking interfaces.
@@ -132,7 +133,7 @@
/**
* Wrapper around the underlying SSL object.
*/
- private final SslWrapper ssl;
+ private final NativeSsl ssl;
/**
* The BIO used for reading/writing encrypted bytes.
@@ -143,7 +144,23 @@
/**
* Set during startHandshake.
*/
- private final ActiveSession sslSession;
+ private final ActiveSession activeSession;
+
+ /**
+ * A snapshot of the active session when the engine was closed.
+ */
+ private SessionSnapshot closedSession;
+
+ /**
+ * The session object exposed externally from this class.
+ */
+ private final SSLSession externalSession =
+ Platform.wrapSSLSession(new ProvidedSessionDecorator(new Provider() {
+ @Override
+ public ConscryptSession provideSession() {
+ return ConscryptEngine.this.provideSession();
+ }
+ }));
/**
* Private key for the TLS Channel ID extension. This field is client-side only. Set during
@@ -166,7 +183,7 @@
peerInfoProvider = PeerInfoProvider.nullProvider();
this.ssl = newSsl(sslParameters, this);
this.networkBio = ssl.newBio();
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
ConscryptEngine(String host, int port, SSLParametersImpl sslParameters) {
@@ -174,7 +191,7 @@
this.peerInfoProvider = PeerInfoProvider.forHostAndPort(host, port);
this.ssl = newSsl(sslParameters, this);
this.networkBio = ssl.newBio();
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
ConscryptEngine(SSLParametersImpl sslParameters, PeerInfoProvider peerInfoProvider) {
@@ -182,12 +199,12 @@
this.peerInfoProvider = checkNotNull(peerInfoProvider, "peerInfoProvider");
this.ssl = newSsl(sslParameters, this);
this.networkBio = ssl.newBio();
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
- private static SslWrapper newSsl(SSLParametersImpl sslParameters, ConscryptEngine engine) {
+ private static NativeSsl newSsl(SSLParametersImpl sslParameters, ConscryptEngine engine) {
try {
- return SslWrapper.newInstance(sslParameters, engine, engine, engine);
+ return NativeSsl.newInstance(sslParameters, engine, engine, engine);
} catch (SSLException e) {
throw new RuntimeException(e);
}
@@ -387,7 +404,7 @@
return;
}
- state = STATE_HANDSHAKE_STARTED;
+ transitionTo(STATE_HANDSHAKE_STARTED);
boolean releaseResources = true;
try {
@@ -397,7 +414,7 @@
// For clients, offer to resume a previously cached session to avoid the
// full TLS handshake.
if (getUseClientMode()) {
- SslSessionWrapper cachedSession = clientSessionContext().getCachedSession(
+ NativeSslSession cachedSession = clientSessionContext().getCachedSession(
getHostname(), getPeerPort(), sslParameters);
if (cachedSession != null) {
cachedSession.offerToResume(ssl);
@@ -430,9 +447,9 @@
return;
}
if (isOutboundDone()) {
- state = STATE_CLOSED;
+ transitionTo(STATE_CLOSED);
} else {
- state = STATE_CLOSED_INBOUND;
+ transitionTo(STATE_CLOSED_INBOUND);
}
}
}
@@ -448,7 +465,7 @@
if (isInboundDone()) {
closeAndFreeResources();
} else {
- state = STATE_CLOSED_OUTBOUND;
+ transitionTo(STATE_CLOSED_OUTBOUND);
}
} else {
// Never started the handshake. Just close now.
@@ -545,18 +562,40 @@
@Override
SSLSession handshakeSession() {
synchronized (ssl) {
- return state == STATE_HANDSHAKE_STARTED ? sslSession : null;
+ if (state == STATE_HANDSHAKE_STARTED) {
+ return Platform.wrapSSLSession(new ProvidedSessionDecorator(new Provider() {
+ @Override
+ public ConscryptSession provideSession() {
+ return ConscryptEngine.this.provideHandshakeSession();
+ }
+ }));
+ }
+ return null;
}
}
@Override
public SSLSession getSession() {
+ return externalSession;
+ }
+
+ private ConscryptSession provideSession() {
synchronized (ssl) {
+ if (state == STATE_CLOSED) {
+ return closedSession != null ? closedSession : SSLNullSession.getNullSession();
+ }
if (state < STATE_HANDSHAKE_COMPLETED) {
// Return an invalid session with invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
return SSLNullSession.getNullSession();
}
- return Platform.wrapSSLSession(sslSession);
+ return activeSession;
+ }
+ }
+
+ private ConscryptSession provideHandshakeSession() {
+ synchronized (ssl) {
+ return state == STATE_HANDSHAKE_STARTED ? activeSession
+ : SSLNullSession.getNullSession();
}
}
@@ -622,7 +661,7 @@
throw new IllegalArgumentException(
"Can not change mode after handshake: state == " + state);
}
- state = STATE_MODE_SET;
+ transitionTo(STATE_MODE_SET);
sslParameters.setUseClientMode(mode);
}
}
@@ -955,7 +994,7 @@
// The handshake has completed successfully...
// Update the session from the current state of the SSL object.
- sslSession.onPeerCertificateAvailable(getPeerHost(), getPeerPort());
+ activeSession.onPeerCertificateAvailable(getPeerHost(), getPeerPort());
finishHandshake();
return FINISHED;
@@ -1528,8 +1567,7 @@
case SSL_CB_HANDSHAKE_START: {
// For clients, this will allow the NEED_UNWRAP status to be
// returned.
- state = STATE_HANDSHAKE_STARTED;
- handshakeFinished = false;
+ transitionTo(STATE_HANDSHAKE_STARTED);
break;
}
case SSL_CB_HANDSHAKE_DONE: {
@@ -1538,7 +1576,7 @@
throw new IllegalStateException(
"Completed handshake while in mode " + state);
}
- state = STATE_HANDSHAKE_COMPLETED;
+ transitionTo(STATE_HANDSHAKE_COMPLETED);
break;
}
default:
@@ -1558,11 +1596,11 @@
// BoringSSL guarantees will not happen.
NativeRef.SSL_SESSION ref = new SSL_SESSION(sslSessionNativePtr);
- SslSessionWrapper sessionWrapper = SslSessionWrapper.newInstance(ref, sslSession);
+ NativeSslSession nativeSession = NativeSslSession.newInstance(ref, activeSession);
// Cache the newly established session.
AbstractSessionContext ctx = sessionContext();
- ctx.cacheSession(sessionWrapper);
+ ctx.cacheSession(nativeSession);
} catch (Exception ignored) {
// Ignore.
}
@@ -1589,7 +1627,7 @@
}
// Update the peer information on the session.
- sslSession.onPeerCertificatesReceived(getPeerHost(), getPeerPort(), peerCertChain);
+ activeSession.onPeerCertificatesReceived(getPeerHost(), getPeerPort(), peerCertChain);
if (getUseClientMode()) {
Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
@@ -1620,7 +1658,7 @@
}
private void closeAndFreeResources() {
- state = STATE_CLOSED;
+ transitionTo(STATE_CLOSED);
if (!ssl.isClosed()) {
ssl.close();
networkBio.close();
@@ -1747,4 +1785,25 @@
private AbstractSessionContext sessionContext() {
return sslParameters.getSessionContext();
}
+
+ private void transitionTo(int newState) {
+ switch (newState) {
+ case STATE_HANDSHAKE_STARTED: {
+ handshakeFinished = false;
+ break;
+ }
+ case STATE_CLOSED: {
+ if (!ssl.isClosed() && state >= STATE_HANDSHAKE_STARTED && state < STATE_CLOSED ) {
+ closedSession = new SessionSnapshot(activeSession);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ // Update the state
+ this.state = newState;
+ }
}
diff --git a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java
index 7864f4e..99f9f98 100644
--- a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java
+++ b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java
@@ -45,6 +45,7 @@
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import org.conscrypt.NativeRef.SSL_SESSION;
+import org.conscrypt.ProvidedSessionDecorator.Provider;
/**
* Implementation of the class OpenSSLSocketImpl based on OpenSSL.
@@ -67,7 +68,7 @@
/**
* Wrapper around the underlying SSL object.
*/
- private final SslWrapper ssl;
+ private final NativeSsl ssl;
/**
* Protected by synchronizing on ssl. Starts as null, set by
@@ -96,7 +97,21 @@
*/
private OpenSSLKey channelIdPrivateKey;
- private final ActiveSession sslSession;
+ private final ActiveSession activeSession;
+ /**
+ * A snapshot of the active session when the engine was closed.
+ */
+ private SessionSnapshot closedSession;
+ /**
+ * The session object exposed externally from this class.
+ */
+ private final SSLSession externalSession =
+ Platform.wrapSSLSession(new ProvidedSessionDecorator(new Provider() {
+ @Override
+ public ConscryptSession provideSession() {
+ return ConscryptFileDescriptorSocket.this.provideSession();
+ }
+ }));
private int writeTimeoutMilliseconds = 0;
private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite
@@ -106,7 +121,7 @@
ConscryptFileDescriptorSocket(SSLParametersImpl sslParameters) throws IOException {
this.sslParameters = sslParameters;
this.ssl = newSsl(sslParameters, this);
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
ConscryptFileDescriptorSocket(String hostname, int port, SSLParametersImpl sslParameters)
@@ -114,7 +129,7 @@
super(hostname, port);
this.sslParameters = sslParameters;
this.ssl = newSsl(sslParameters, this);
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
ConscryptFileDescriptorSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
@@ -122,7 +137,7 @@
super(address, port);
this.sslParameters = sslParameters;
this.ssl = newSsl(sslParameters, this);
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
ConscryptFileDescriptorSocket(String hostname, int port, InetAddress clientAddress,
@@ -130,7 +145,7 @@
super(hostname, port, clientAddress, clientPort);
this.sslParameters = sslParameters;
this.ssl = newSsl(sslParameters, this);
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
ConscryptFileDescriptorSocket(InetAddress address, int port, InetAddress clientAddress,
@@ -138,7 +153,7 @@
super(address, port, clientAddress, clientPort);
this.sslParameters = sslParameters;
this.ssl = newSsl(sslParameters, this);
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
ConscryptFileDescriptorSocket(Socket socket, String hostname, int port, boolean autoClose,
@@ -146,13 +161,13 @@
super(socket, hostname, port, autoClose);
this.sslParameters = sslParameters;
this.ssl = newSsl(sslParameters, this);
- sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
- private static SslWrapper newSsl(SSLParametersImpl sslParameters,
+ private static NativeSsl newSsl(SSLParametersImpl sslParameters,
ConscryptFileDescriptorSocket engine) {
try {
- return SslWrapper.newInstance(sslParameters, engine, engine, engine);
+ return NativeSsl.newInstance(sslParameters, engine, engine, engine);
} catch (SSLException e) {
throw new RuntimeException(e);
}
@@ -170,7 +185,7 @@
checkOpen();
synchronized (ssl) {
if (state == STATE_NEW) {
- state = STATE_HANDSHAKE_STARTED;
+ transitionTo(STATE_HANDSHAKE_STARTED);
} else {
// We've either started the handshake already or have been closed.
// Do nothing in both cases.
@@ -188,7 +203,7 @@
// For clients, offer to resume a previously cached session to avoid the
// full TLS handshake.
if (getUseClientMode()) {
- SslSessionWrapper cachedSession = clientSessionContext().getCachedSession(
+ NativeSslSession cachedSession = clientSessionContext().getCachedSession(
getHostnameOrIP(), getPort(), sslParameters);
if (cachedSession != null) {
cachedSession.offerToResume(ssl);
@@ -213,7 +228,7 @@
ssl.doHandshake(Platform.getFileDescriptor(socket), getSoTimeout());
// Update the session from the current state of the SSL object.
- sslSession.onPeerCertificateAvailable(getHostnameOrIP(), getPort());
+ activeSession.onPeerCertificateAvailable(getHostnameOrIP(), getPort());
} catch (CertificateException e) {
SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
wrapper.initCause(e);
@@ -260,9 +275,9 @@
releaseResources = (state == STATE_CLOSED);
if (state == STATE_HANDSHAKE_STARTED) {
- state = STATE_READY_HANDSHAKE_CUT_THROUGH;
+ transitionTo(STATE_READY_HANDSHAKE_CUT_THROUGH);
} else {
- state = STATE_READY;
+ transitionTo(STATE_READY);
}
if (!releaseResources) {
@@ -282,7 +297,7 @@
//
// The state will already be set to closed if we reach this as a result of
// an early return or an interruption due to a concurrent call to close().
- state = STATE_CLOSED;
+ transitionTo(STATE_CLOSED);
ssl.notifyAll();
}
@@ -333,7 +348,7 @@
// Now that we've fixed up our state, we can tell waiting threads that
// we're ready.
- state = STATE_READY;
+ transitionTo(STATE_READY);
}
// Let listeners know we are finally done
@@ -357,11 +372,11 @@
// BoringSSL guarantees will not happen.
NativeRef.SSL_SESSION ref = new SSL_SESSION(sslSessionNativePtr);
- SslSessionWrapper sessionWrapper = SslSessionWrapper.newInstance(ref, sslSession);
+ NativeSslSession nativeSession = NativeSslSession.newInstance(ref, activeSession);
// Cache the newly established session.
AbstractSessionContext ctx = sessionContext();
- ctx.cacheSession(sessionWrapper);
+ ctx.cacheSession(nativeSession);
} catch (Exception ignored) {
// Ignore.
}
@@ -387,7 +402,7 @@
throw new CertificateException("No X.509 TrustManager");
}
// Update the peer information on the session.
- sslSession.onPeerCertificatesReceived(getHostnameOrIP(), getPort(), peerCertChain);
+ activeSession.onPeerCertificatesReceived(getHostnameOrIP(), getPort(), peerCertChain);
if (getUseClientMode()) {
Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
@@ -635,8 +650,16 @@
@Override
public final SSLSession getSession() {
+ return externalSession;
+ }
+
+ private ConscryptSession provideSession() {
boolean handshakeCompleted = false;
synchronized (ssl) {
+ if (state == STATE_CLOSED) {
+ return closedSession != null ? closedSession : SSLNullSession.getNullSession();
+ }
+
try {
handshakeCompleted = state >= STATE_READY;
if (!handshakeCompleted && isConnected()) {
@@ -654,18 +677,33 @@
return SSLNullSession.getNullSession();
}
- return Platform.wrapSSLSession(sslSession);
+ return activeSession;
+ }
+
+ private ConscryptSession provideHandshakeSession() {
+ synchronized (ssl) {
+ return state >= STATE_HANDSHAKE_STARTED && state < STATE_READY ? activeSession
+ : SSLNullSession.getNullSession();
+ }
}
@Override
final SSLSession getActiveSession() {
- return sslSession;
+ return activeSession;
}
@Override
public final SSLSession getHandshakeSession() {
synchronized (ssl) {
- return state >= STATE_HANDSHAKE_STARTED && state < STATE_READY ? sslSession : null;
+ if (state >= STATE_HANDSHAKE_STARTED && state < STATE_READY) {
+ return Platform.wrapSSLSession(new ProvidedSessionDecorator(new Provider() {
+ @Override
+ public ConscryptSession provideSession() {
+ return ConscryptFileDescriptorSocket.this.provideHandshakeSession();
+ }
+ }));
+ }
+ return null;
}
}
@@ -911,7 +949,7 @@
}
int oldState = state;
- state = STATE_CLOSED;
+ transitionTo(STATE_CLOSED);
if (oldState == STATE_NEW) {
// The handshake hasn't been started yet, so there's no OpenSSL related
@@ -1100,4 +1138,21 @@
private AbstractSessionContext sessionContext() {
return sslParameters.getSessionContext();
}
+
+ private void transitionTo(int newState) {
+ switch (newState) {
+ case STATE_CLOSED: {
+ if (!ssl.isClosed() && state >= STATE_HANDSHAKE_STARTED && state < STATE_CLOSED ) {
+ closedSession = new SessionSnapshot(activeSession);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ // Update the state
+ this.state = newState;
+ }
}
diff --git a/common/src/main/java/org/conscrypt/ConscryptSession.java b/common/src/main/java/org/conscrypt/ConscryptSession.java
new file mode 100644
index 0000000..e59c192
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/ConscryptSession.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Extends the default interface for {@link SSLSession} to provide additional properties exposed
+ * by Conscrypt.
+ */
+interface ConscryptSession extends SSLSession {
+
+ String getRequestedServerName();
+
+ /**
+ * Returns the OCSP stapled response. Returns a copy of the internal arrays.
+ *
+ * The method signature matches
+ * <a
+ * href="http://download.java.net/java/jdk9/docs/api/javax/net/ssl/ExtendedSSLSession.html#getStatusResponses--">Java
+ * 9</a>.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
+ * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
+ */
+ List<byte[]> getStatusResponses();
+
+ /**
+ * Returns the signed certificate timestamp (SCT) received from the peer. Returns a
+ * copy of the internal array.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
+ */
+ byte[] getPeerSignedCertificateTimestamp();
+
+ @Override
+ X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException;
+}
diff --git a/common/src/main/java/org/conscrypt/Java7ExtendedSSLSession.java b/common/src/main/java/org/conscrypt/Java7ExtendedSSLSession.java
new file mode 100644
index 0000000..8ecb359
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/Java7ExtendedSSLSession.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.conscrypt;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.List;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.X509Certificate;
+
+/**
+ * This is an adapter that wraps the active session with {@link ExtendedSSLSession}, if running
+ * on Java 7+.
+ */
+class Java7ExtendedSSLSession extends ExtendedSSLSession implements SessionDecorator {
+ // TODO: use BoringSSL API to actually fetch the real data
+ private static final String[] LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS = new String[] {
+ "SHA512withRSA", "SHA512withECDSA", "SHA384withRSA", "SHA384withECDSA", "SHA256withRSA",
+ "SHA256withECDSA", "SHA224withRSA", "SHA224withECDSA", "SHA1withRSA", "SHA1withECDSA",
+ };
+ // TODO: use BoringSSL API to actually fetch the real data
+ private static final String[] PEER_SUPPORTED_SIGNATURE_ALGORITHMS =
+ new String[] {"SHA1withRSA", "SHA1withECDSA"};
+ private final ConscryptSession delegate;
+
+ Java7ExtendedSSLSession(ConscryptSession delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public final ConscryptSession getDelegate() {
+ return delegate;
+ }
+
+ /* @Override */
+ @SuppressWarnings("MissingOverride") // For Android backward-compatibility.
+ public final String[] getLocalSupportedSignatureAlgorithms() {
+ return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
+ }
+
+ /* @Override */
+ @SuppressWarnings("MissingOverride") // For Android backward-compatibility.
+ public final String[] getPeerSupportedSignatureAlgorithms() {
+ return PEER_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
+ }
+
+ @Override
+ public final String getRequestedServerName() {
+ return getDelegate().getRequestedServerName();
+ }
+
+ /**
+ * Provides forward-compatibility with Java 9.
+ */
+ @Override
+ public final List<byte[]> getStatusResponses() {
+ return getDelegate().getStatusResponses();
+ }
+
+ @Override
+ public final byte[] getPeerSignedCertificateTimestamp() {
+ return getDelegate().getPeerSignedCertificateTimestamp();
+ }
+
+ @Override
+ public final byte[] getId() {
+ return getDelegate().getId();
+ }
+
+ @Override
+ public final SSLSessionContext getSessionContext() {
+ return getDelegate().getSessionContext();
+ }
+
+ @Override
+ public final long getCreationTime() {
+ return getDelegate().getCreationTime();
+ }
+
+ @Override
+ public final long getLastAccessedTime() {
+ return getDelegate().getLastAccessedTime();
+ }
+
+ @Override
+ public final void invalidate() {
+ getDelegate().invalidate();
+ }
+
+ @Override
+ public final boolean isValid() {
+ return getDelegate().isValid();
+ }
+
+ @Override
+ public final void putValue(String s, Object o) {
+ getDelegate().putValue(s, o);
+ }
+
+ @Override
+ public final Object getValue(String s) {
+ return getDelegate().getValue(s);
+ }
+
+ @Override
+ public final void removeValue(String s) {
+ getDelegate().removeValue(s);
+ }
+
+ @Override
+ public final String[] getValueNames() {
+ return getDelegate().getValueNames();
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getPeerCertificates()
+ throws SSLPeerUnverifiedException {
+ return getDelegate().getPeerCertificates();
+ }
+
+ @Override
+ public final Certificate[] getLocalCertificates() {
+ return getDelegate().getLocalCertificates();
+ }
+
+ @Override
+ public final X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
+ return getDelegate().getPeerCertificateChain();
+ }
+
+ @Override
+ public final Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ return getDelegate().getPeerPrincipal();
+ }
+
+ @Override
+ public final Principal getLocalPrincipal() {
+ return getDelegate().getLocalPrincipal();
+ }
+
+ @Override
+ public final String getCipherSuite() {
+ return getDelegate().getCipherSuite();
+ }
+
+ @Override
+ public final String getProtocol() {
+ return getDelegate().getProtocol();
+ }
+
+ @Override
+ public final String getPeerHost() {
+ return getDelegate().getPeerHost();
+ }
+
+ @Override
+ public final int getPeerPort() {
+ return getDelegate().getPeerPort();
+ }
+
+ @Override
+ public final int getPacketBufferSize() {
+ return getDelegate().getPacketBufferSize();
+ }
+
+ @Override
+ public final int getApplicationBufferSize() {
+ return getDelegate().getApplicationBufferSize();
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/Java7SessionWrapper.java b/common/src/main/java/org/conscrypt/Java7SessionWrapper.java
deleted file mode 100644
index 2883f88..0000000
--- a/common/src/main/java/org/conscrypt/Java7SessionWrapper.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.conscrypt;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.security.Principal;
-import java.security.cert.Certificate;
-import java.util.Collections;
-import java.util.List;
-import javax.net.ssl.ExtendedSSLSession;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSessionContext;
-import javax.security.cert.X509Certificate;
-
-/**
- * This is an adapter that wraps the active session with {@link ExtendedSSLSession}, if running
- * on Java 7+.
- */
-final class Java7SessionWrapper extends ExtendedSSLSession {
- // TODO: use BoringSSL API to actually fetch the real data
- private static final String[] LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS = new String[] {
- "SHA512withRSA", "SHA512withECDSA", "SHA384withRSA", "SHA384withECDSA", "SHA256withRSA",
- "SHA256withECDSA", "SHA224withRSA", "SHA224withECDSA", "SHA1withRSA", "SHA1withECDSA",
- };
- // TODO: use BoringSSL API to actually fetch the real data
- private static final String[] PEER_SUPPORTED_SIGNATURE_ALGORITHMS =
- new String[] {"SHA1withRSA", "SHA1withECDSA"};
- // private static Class<?> EXTENDED_SSL_SESSION_CLASS = getExtendedSslSessionClass();
- private final ActiveSession delegate;
-
- Java7SessionWrapper(ActiveSession delegate) {
- this.delegate = delegate;
- }
-
- /**
- * If the given session is a wrapper, returns the delegate session.
- */
- static SSLSession getDelegate(SSLSession session) {
- if (session instanceof Java7SessionWrapper) {
- return ((Java7SessionWrapper) session).delegate;
- }
- return session;
- }
-
- /* @Override */
- @SuppressWarnings("MissingOverride") // For Android backward-compatibility.
- public String[] getLocalSupportedSignatureAlgorithms() {
- return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
- }
-
- /* @Override */
- @SuppressWarnings("MissingOverride") // For Android backward-compatibility.
- public String[] getPeerSupportedSignatureAlgorithms() {
- return PEER_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
- }
-
- /* @Override */
- // For Android backward-compatibility.
- @SuppressWarnings({"MissingOverride", "unchecked", "rawtypes"})
- public List getRequestedServerNames() {
- try {
- String requestedServerName = delegate.getRequestedServerName();
- if (requestedServerName == null) {
- return null;
- }
-
- Constructor sniHostNameConstructor =
- Class.forName("javax.net.ssl.SNIHostName").getConstructor(String.class);
- return Collections.singletonList(
- sniHostNameConstructor.newInstance(requestedServerName));
-
- } catch (NoSuchMethodException ignore) {
- } catch (InvocationTargetException ignore) {
- } catch (IllegalAccessException ignore) {
- } catch (ClassNotFoundException ignore) {
- } catch (InstantiationException ignore) {
- }
- return null;
- }
-
- @Override
- public byte[] getId() {
- return delegate.getId();
- }
-
- @Override
- public SSLSessionContext getSessionContext() {
- return delegate.getSessionContext();
- }
-
- @Override
- public long getCreationTime() {
- return delegate.getCreationTime();
- }
-
- @Override
- public long getLastAccessedTime() {
- return delegate.getLastAccessedTime();
- }
-
- @Override
- public void invalidate() {
- delegate.invalidate();
- }
-
- @Override
- public boolean isValid() {
- return delegate.isValid();
- }
-
- @Override
- public void putValue(String name, Object value) {
- delegate.putValue(name, value);
- }
-
- @Override
- public Object getValue(String name) {
- return delegate.getValue(name);
- }
-
- @Override
- public void removeValue(String name) {
- delegate.removeValue(name);
- }
-
- @Override
- public String[] getValueNames() {
- return delegate.getValueNames();
- }
-
- @Override
- public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
- return delegate.getPeerCertificates();
- }
-
- @Override
- public Certificate[] getLocalCertificates() {
- return delegate.getLocalCertificates();
- }
-
- @Override
- public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
- return delegate.getPeerCertificateChain();
- }
-
- @Override
- public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
- return delegate.getPeerPrincipal();
- }
-
- @Override
- public Principal getLocalPrincipal() {
- return delegate.getLocalPrincipal();
- }
-
- @Override
- public String getCipherSuite() {
- return delegate.getCipherSuite();
- }
-
- @Override
- public String getProtocol() {
- return delegate.getProtocol();
- }
-
- @Override
- public String getPeerHost() {
- return delegate.getPeerHost();
- }
-
- @Override
- public int getPeerPort() {
- return delegate.getPeerPort();
- }
-
- @Override
- public int getPacketBufferSize() {
- return delegate.getPacketBufferSize();
- }
-
- @Override
- public int getApplicationBufferSize() {
- return delegate.getApplicationBufferSize();
- }
-}
diff --git a/common/src/main/java/org/conscrypt/Java8ExtendedSSLSession.java b/common/src/main/java/org/conscrypt/Java8ExtendedSSLSession.java
new file mode 100644
index 0000000..b5f93de
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/Java8ExtendedSSLSession.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+
+/**
+ * This is an adapter that wraps the active session with {@link ExtendedSSLSession}, if running
+ * on Java 8+.
+ */
+class Java8ExtendedSSLSession extends Java7ExtendedSSLSession {
+
+ public Java8ExtendedSSLSession(ConscryptSession delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public final List<SNIServerName> getRequestedServerNames() {
+ String requestedServerName = getDelegate().getRequestedServerName();
+ if (requestedServerName == null) {
+ return null;
+ }
+
+ return Collections.singletonList((SNIServerName) new SNIHostName(requestedServerName));
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/SslWrapper.java b/common/src/main/java/org/conscrypt/NativeSsl.java
similarity index 98%
rename from common/src/main/java/org/conscrypt/SslWrapper.java
rename to common/src/main/java/org/conscrypt/NativeSsl.java
index 2c5bca8..a76dcca 100644
--- a/common/src/main/java/org/conscrypt/SslWrapper.java
+++ b/common/src/main/java/org/conscrypt/NativeSsl.java
@@ -50,7 +50,7 @@
/**
* A utility wrapper that abstracts operations on the underlying native SSL instance.
*/
-final class SslWrapper {
+final class NativeSsl {
private final SSLParametersImpl parameters;
private final SSLHandshakeCallbacks handshakeCallbacks;
private final AliasChooser aliasChooser;
@@ -58,15 +58,7 @@
private X509Certificate[] localCertificates;
private volatile long ssl;
- static SslWrapper newInstance(SSLParametersImpl parameters,
- SSLHandshakeCallbacks handshakeCallbacks, AliasChooser chooser,
- PSKCallbacks pskCallbacks) throws SSLException {
- long ctx = parameters.getSessionContext().sslCtxNativePointer;
- long ssl = NativeCrypto.SSL_new(ctx);
- return new SslWrapper(ssl, parameters, handshakeCallbacks, chooser, pskCallbacks);
- }
-
- private SslWrapper(long ssl, SSLParametersImpl parameters,
+ private NativeSsl(long ssl, SSLParametersImpl parameters,
SSLHandshakeCallbacks handshakeCallbacks, AliasChooser aliasChooser,
PSKCallbacks pskCallbacks) {
this.ssl = ssl;
@@ -76,6 +68,14 @@
this.pskCallbacks = pskCallbacks;
}
+ static NativeSsl newInstance(SSLParametersImpl parameters,
+ SSLHandshakeCallbacks handshakeCallbacks, AliasChooser chooser,
+ PSKCallbacks pskCallbacks) throws SSLException {
+ long ctx = parameters.getSessionContext().sslCtxNativePointer;
+ long ssl = NativeCrypto.SSL_new(ctx);
+ return new NativeSsl(ssl, parameters, handshakeCallbacks, chooser, pskCallbacks);
+ }
+
long ssl() {
return ssl;
}
diff --git a/common/src/main/java/org/conscrypt/SslSessionWrapper.java b/common/src/main/java/org/conscrypt/NativeSslSession.java
similarity index 93%
rename from common/src/main/java/org/conscrypt/SslSessionWrapper.java
rename to common/src/main/java/org/conscrypt/NativeSslSession.java
index 7ff1a54..1a3ab80 100644
--- a/common/src/main/java/org/conscrypt/SslSessionWrapper.java
+++ b/common/src/main/java/org/conscrypt/NativeSslSession.java
@@ -42,29 +42,29 @@
*
* This is abstract only to support mocking for tests.
*/
-abstract class SslSessionWrapper {
- private static final Logger logger = Logger.getLogger(SslSessionWrapper.class.getName());
+abstract class NativeSslSession {
+ private static final Logger logger = Logger.getLogger(NativeSslSession.class.getName());
/**
* Creates a new instance. Since BoringSSL does not provide an API to get access to all
* session information via the SSL_SESSION, we get some values (e.g. peer certs) from
- * the active session instead (i.e. the SSL object).
+ * the {@link ConscryptSession} instead (i.e. the SSL object).
*/
- static SslSessionWrapper newInstance(NativeRef.SSL_SESSION ref, ActiveSession activeSession)
+ static NativeSslSession newInstance(NativeRef.SSL_SESSION ref, ConscryptSession session)
throws SSLPeerUnverifiedException {
- AbstractSessionContext context = (AbstractSessionContext) activeSession.getSessionContext();
+ AbstractSessionContext context = (AbstractSessionContext) session.getSessionContext();
if (context instanceof ClientSessionContext) {
- return new Impl(context, ref, activeSession.getPeerHost(), activeSession.getPeerPort(),
- activeSession.getPeerCertificates(), getOcspResponse(activeSession),
- activeSession.getPeerSignedCertificateTimestamp());
+ return new Impl(context, ref, session.getPeerHost(), session.getPeerPort(),
+ session.getPeerCertificates(), getOcspResponse(session),
+ session.getPeerSignedCertificateTimestamp());
}
// Server's will be cached by ID and won't have any of the extra fields.
return new Impl(context, ref, null, -1, null, null, null);
}
- private static byte[] getOcspResponse(ActiveSession activeSession) {
- List<byte[]> ocspResponseList = activeSession.getStatusResponses();
+ private static byte[] getOcspResponse(ConscryptSession session) {
+ List<byte[]> ocspResponseList = session.getStatusResponses();
if (ocspResponseList.size() >= 1) {
return ocspResponseList.get(0);
}
@@ -72,13 +72,13 @@
}
/**
- * Creates a new {@link SslSessionWrapper} instance from the provided serialized bytes, which
+ * Creates a new {@link NativeSslSession} instance from the provided serialized bytes, which
* were generated by {@link #toBytes()}.
*
* @return The new instance if successful. If unable to parse the bytes for any reason, returns
* {@code null}.
*/
- static SslSessionWrapper newInstance(
+ static NativeSslSession newInstance(
AbstractSessionContext context, byte[] data, String host, int port) {
ByteBuffer buf = ByteBuffer.wrap(data);
try {
@@ -166,7 +166,7 @@
abstract boolean isValid();
- abstract void offerToResume(SslWrapper ssl) throws SSLException;
+ abstract void offerToResume(NativeSsl ssl) throws SSLException;
abstract String getCipherSuite();
@@ -209,7 +209,7 @@
/**
* The session wrapper implementation.
*/
- private static final class Impl extends SslSessionWrapper {
+ private static final class Impl extends NativeSslSession {
private final NativeRef.SSL_SESSION ref;
// BoringSSL offers no API to obtain these values directly from the SSL_SESSION.
@@ -258,7 +258,7 @@
}
@Override
- void offerToResume(SslWrapper ssl) throws SSLException {
+ void offerToResume(NativeSsl ssl) throws SSLException {
ssl.offerToResumeSession(ref.context);
}
diff --git a/common/src/main/java/org/conscrypt/ProvidedSessionDecorator.java b/common/src/main/java/org/conscrypt/ProvidedSessionDecorator.java
new file mode 100644
index 0000000..e0257a0
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/ProvidedSessionDecorator.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.X509Certificate;
+
+/**
+ * A {@link SessionDecorator} that externalizes the provider of the delegate session. This allows
+ * the underlying session to be changed externally.
+ */
+final class ProvidedSessionDecorator implements SessionDecorator {
+
+ private final Provider provider;
+
+ public ProvidedSessionDecorator(Provider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public ConscryptSession getDelegate() {
+ return provider.provideSession();
+ }
+
+ @Override
+ public String getRequestedServerName() {
+ return getDelegate().getRequestedServerName();
+ }
+
+ @Override
+ public List<byte[]> getStatusResponses() {
+ return getDelegate().getStatusResponses();
+ }
+
+ @Override
+ public byte[] getPeerSignedCertificateTimestamp() {
+ return getDelegate().getPeerSignedCertificateTimestamp();
+ }
+
+ @Override
+ public byte[] getId() {
+ return getDelegate().getId();
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ return getDelegate().getSessionContext();
+ }
+
+ @Override
+ public long getCreationTime() {
+ return getDelegate().getCreationTime();
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ return getDelegate().getLastAccessedTime();
+ }
+
+ @Override
+ public void invalidate() {
+ getDelegate().invalidate();
+ }
+
+ @Override
+ public boolean isValid() {
+ return getDelegate().isValid();
+ }
+
+ @Override
+ public void putValue(String s, Object o) {
+ getDelegate().putValue(s, o);
+ }
+
+ @Override
+ public Object getValue(String s) {
+ return getDelegate().getValue(s);
+ }
+
+ @Override
+ public void removeValue(String s) {
+ getDelegate().removeValue(s);
+ }
+
+ @Override
+ public String[] getValueNames() {
+ return getDelegate().getValueNames();
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getPeerCertificates()
+ throws SSLPeerUnverifiedException {
+ return getDelegate().getPeerCertificates();
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ return getDelegate().getLocalCertificates();
+ }
+
+ @Override
+ public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
+ return getDelegate().getPeerCertificateChain();
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ return getDelegate().getPeerPrincipal();
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ return getDelegate().getLocalPrincipal();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ return getDelegate().getCipherSuite();
+ }
+
+ @Override
+ public String getProtocol() {
+ return getDelegate().getProtocol();
+ }
+
+ @Override
+ public String getPeerHost() {
+ return getDelegate().getPeerHost();
+ }
+
+ @Override
+ public int getPeerPort() {
+ return getDelegate().getPeerPort();
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ return getDelegate().getPacketBufferSize();
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ return getDelegate().getApplicationBufferSize();
+ }
+
+ /**
+ * The provider of the current delegate session.
+ */
+ interface Provider {
+ ConscryptSession provideSession();
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/SSLNullSession.java b/common/src/main/java/org/conscrypt/SSLNullSession.java
index 6289a98..34dc207 100644
--- a/common/src/main/java/org/conscrypt/SSLNullSession.java
+++ b/common/src/main/java/org/conscrypt/SSLNullSession.java
@@ -19,7 +19,10 @@
import java.security.Principal;
import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionBindingEvent;
@@ -32,7 +35,7 @@
* javax.net.ssl.SSLSocket#getSession()} before {@link javax.net.ssl.SSLSocket#startHandshake()} is
* called.
*/
-final class SSLNullSession implements SSLSession, Cloneable {
+final class SSLNullSession implements ConscryptSession, Cloneable {
static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
/*
@@ -48,12 +51,12 @@
private long creationTime;
private long lastAccessedTime;
- static SSLSession getNullSession() {
+ static ConscryptSession getNullSession() {
return DefaultHolder.NULL_SESSION;
}
static boolean isNullSession(SSLSession session) {
- return session == DefaultHolder.NULL_SESSION;
+ return SSLUtils.unwrapSession(session) == DefaultHolder.NULL_SESSION;
}
private SSLNullSession() {
@@ -62,6 +65,21 @@
}
@Override
+ public String getRequestedServerName() {
+ return null;
+ }
+
+ @Override
+ public List<byte[]> getStatusResponses() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public byte[] getPeerSignedCertificateTimestamp() {
+ return EmptyArray.BYTE;
+ }
+
+ @Override
public int getApplicationBufferSize() {
return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
}
@@ -108,7 +126,7 @@
}
@Override
- public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ public X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
throw new SSLPeerUnverifiedException("No peer certificate");
}
diff --git a/common/src/main/java/org/conscrypt/SSLUtils.java b/common/src/main/java/org/conscrypt/SSLUtils.java
index 583ebcc..2f6a621 100644
--- a/common/src/main/java/org/conscrypt/SSLUtils.java
+++ b/common/src/main/java/org/conscrypt/SSLUtils.java
@@ -52,6 +52,7 @@
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
import javax.security.cert.CertificateException;
/**
@@ -177,6 +178,17 @@
/** Key type: Elliptic Curve certificate. */
private static final String KEY_TYPE_EC = "EC";
+ /**
+ * If the given session is a {@link SessionDecorator}, unwraps the session and returns the
+ * underlying (non-decorated) session. Otherwise, returns the provided session.
+ */
+ static SSLSession unwrapSession(SSLSession session) {
+ while (session instanceof SessionDecorator) {
+ session = ((SessionDecorator) session).getDelegate();
+ }
+ return session;
+ }
+
static X509Certificate[] decodeX509CertificateChain(byte[][] certChain)
throws java.security.cert.CertificateException {
CertificateFactory certificateFactory = getCertificateFactory();
diff --git a/common/src/main/java/org/conscrypt/ServerSessionContext.java b/common/src/main/java/org/conscrypt/ServerSessionContext.java
index 331f2b2..4d144f6 100644
--- a/common/src/main/java/org/conscrypt/ServerSessionContext.java
+++ b/common/src/main/java/org/conscrypt/ServerSessionContext.java
@@ -56,11 +56,11 @@
}
@Override
- SslSessionWrapper getSessionFromPersistentCache(byte[] sessionId) {
+ NativeSslSession getSessionFromPersistentCache(byte[] sessionId) {
if (persistentCache != null) {
byte[] data = persistentCache.getSessionData(sessionId);
if (data != null) {
- SslSessionWrapper session = SslSessionWrapper.newInstance(this, data, null, -1);
+ NativeSslSession session = NativeSslSession.newInstance(this, data, null, -1);
if (session != null && session.isValid()) {
cacheSession(session);
return session;
@@ -72,7 +72,7 @@
}
@Override
- void onBeforeAddSession(SslSessionWrapper session) {
+ void onBeforeAddSession(NativeSslSession session) {
// TODO: Do this in background thread.
if (persistentCache != null) {
byte[] data = session.toBytes();
@@ -83,7 +83,7 @@
}
@Override
- void onBeforeRemoveSession(SslSessionWrapper session) {
+ void onBeforeRemoveSession(NativeSslSession session) {
// Do nothing.
}
}
diff --git a/common/src/main/java/org/conscrypt/SessionDecorator.java b/common/src/main/java/org/conscrypt/SessionDecorator.java
new file mode 100644
index 0000000..e36e1b8
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/SessionDecorator.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+/**
+ * A session that decorates another {@link ConscryptSession}.
+ */
+interface SessionDecorator extends ConscryptSession {
+
+ /**
+ * Gets the underlying {@link ConscryptSession}.
+ */
+ ConscryptSession getDelegate();
+}
diff --git a/common/src/main/java/org/conscrypt/SessionSnapshot.java b/common/src/main/java/org/conscrypt/SessionSnapshot.java
new file mode 100644
index 0000000..5ae755a
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/SessionSnapshot.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * A snapshot of the content of another {@link ConscryptSession}. This copies everything over
+ * except for the certificates.
+ */
+final class SessionSnapshot implements ConscryptSession {
+ private final SSLSessionContext sessionContext;
+ private final byte[] id;
+ private final String requestedServerName;
+ private final List<byte[]> statusResponses;
+ private final byte[] peerTlsSctData;
+ private final long creationTime;
+ private final long lastAccessedTime;
+ private final String cipherSuite;
+ private final String protocol;
+ private final String peerHost;
+ private final int peerPort;
+
+ SessionSnapshot(ConscryptSession session) {
+ sessionContext = session.getSessionContext();
+ id = session.getId();
+ requestedServerName = session.getRequestedServerName();
+ statusResponses = session.getStatusResponses();
+ peerTlsSctData = session.getPeerSignedCertificateTimestamp();
+ creationTime = session.getCreationTime();
+ lastAccessedTime = session.getLastAccessedTime();
+ cipherSuite = session.getCipherSuite();
+ protocol = session.getProtocol();
+ peerHost = session.getPeerHost();
+ peerPort = session.getPeerPort();
+ }
+
+ @Override
+ public String getRequestedServerName() {
+ return requestedServerName;
+ }
+
+ @Override
+ public List<byte[]> getStatusResponses() {
+ List<byte[]> ret = new ArrayList<byte[]>(statusResponses.size());
+ for (byte[] resp : statusResponses) {
+ ret.add(resp.clone());
+ }
+ return ret;
+ }
+
+ @Override
+ public byte[] getPeerSignedCertificateTimestamp() {
+ return peerTlsSctData != null ? peerTlsSctData.clone() : null;
+ }
+
+ @Override
+ public byte[] getId() {
+ return id;
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ return sessionContext;
+ }
+
+ @Override
+ public long getCreationTime() {
+ return creationTime;
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ return lastAccessedTime;
+ }
+
+ @Override
+ public void invalidate() {
+ // Do nothing.
+ }
+
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+
+ @Override
+ public void putValue(String s, Object o) {
+ // Do nothing.
+ }
+
+ @Override
+ public Object getValue(String s) {
+ return null;
+ }
+
+ @Override
+ public void removeValue(String s) {
+ // Do nothing.
+ }
+
+ @Override
+ public String[] getValueNames() {
+ return EmptyArray.STRING;
+ }
+
+ @Override
+ public X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ throw new SSLPeerUnverifiedException("No peer certificates");
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ return null;
+ }
+
+ @Override
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ throw new SSLPeerUnverifiedException("No peer certificates");
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ throw new SSLPeerUnverifiedException("No peer certificates");
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ return null;
+ }
+
+ @Override
+ public String getCipherSuite() {
+ return cipherSuite;
+ }
+
+ @Override
+ public String getProtocol() {
+ return protocol;
+ }
+
+ @Override
+ public String getPeerHost() {
+ return peerHost;
+ }
+
+ @Override
+ public int getPeerPort() {
+ return peerPort;
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
+ }
+}
diff --git a/openjdk/src/main/java/org/conscrypt/Java7PlatformUtil.java b/openjdk/src/main/java/org/conscrypt/Java7PlatformUtil.java
index 2e43e19..f2eed33 100644
--- a/openjdk/src/main/java/org/conscrypt/Java7PlatformUtil.java
+++ b/openjdk/src/main/java/org/conscrypt/Java7PlatformUtil.java
@@ -121,12 +121,8 @@
return file.canExecute();
}
- static SSLSession wrapSSLSession(ActiveSession sslSession) {
- return new Java7SessionWrapper(sslSession);
- }
-
- static SSLSession unwrapSSLSession(SSLSession sslSession) {
- return Java7SessionWrapper.getDelegate(sslSession);
+ static SSLSession wrapSSLSession(ConscryptSession sslSession) {
+ return new Java7ExtendedSSLSession(sslSession);
}
private Java7PlatformUtil() {}
diff --git a/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java b/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java
index 297d782..ebb8e67 100644
--- a/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java
+++ b/openjdk/src/main/java/org/conscrypt/Java8PlatformUtil.java
@@ -24,6 +24,7 @@
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
/**
* Utility methods supported on Java 8+.
@@ -88,5 +89,9 @@
return Java8EngineWrapper.getDelegate(engine);
}
+ static SSLSession wrapSSLSession(ConscryptSession sslSession) {
+ return new Java8ExtendedSSLSession(sslSession);
+ }
+
private Java8PlatformUtil() {}
}
diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java
index 23ad56c..4ed0dfb 100644
--- a/openjdk/src/main/java/org/conscrypt/Platform.java
+++ b/openjdk/src/main/java/org/conscrypt/Platform.java
@@ -529,21 +529,16 @@
*/
@SuppressWarnings("unused")
- static SSLSession wrapSSLSession(ActiveSession sslSession) {
+ static SSLSession wrapSSLSession(ConscryptSession sslSession) {
+ if (JAVA_VERSION >= 8) {
+ return Java8PlatformUtil.wrapSSLSession(sslSession);
+ }
if (JAVA_VERSION >= 7) {
return Java7PlatformUtil.wrapSSLSession(sslSession);
}
return sslSession;
}
- @SuppressWarnings("unused")
- static SSLSession unwrapSSLSession(SSLSession sslSession) {
- if (JAVA_VERSION >= 7) {
- return Java7PlatformUtil.unwrapSSLSession(sslSession);
- }
- return sslSession;
- }
-
public static String getOriginalHostNameFromInetAddress(InetAddress addr) {
try {
Method getHolder = InetAddress.class.getDeclaredMethod("holder");
diff --git a/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java
index 1cda6f9..68b7f14 100644
--- a/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java
+++ b/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java
@@ -40,12 +40,16 @@
abstract T newContext();
abstract int size(T context);
- abstract SslSessionWrapper getCachedSession(T context, SslSessionWrapper s);
+ private static NativeSslSession[] toArray(NativeSslSession... sessions) {
+ return sessions;
+ }
+
+ abstract NativeSslSession getCachedSession(T context, NativeSslSession s);
@Test
public void testSimpleAddition() {
- SslSessionWrapper a = newSession("a");
- SslSessionWrapper b = newSession("b");
+ NativeSslSession a = newSession("a");
+ NativeSslSession b = newSession("b");
context.cacheSession(a);
assertSessionContextContents(toArray(a), toArray(b));
@@ -56,10 +60,10 @@
@Test
public void testTrimToSize() {
- SslSessionWrapper a = newSession("a");
- SslSessionWrapper b = newSession("b");
- SslSessionWrapper c = newSession("c");
- SslSessionWrapper d = newSession("d");
+ NativeSslSession a = newSession("a");
+ NativeSslSession b = newSession("b");
+ NativeSslSession c = newSession("c");
+ NativeSslSession d = newSession("d");
context.cacheSession(a);
context.cacheSession(b);
@@ -74,10 +78,10 @@
@Test
public void testImplicitRemovalOfOldest() {
context.setSessionCacheSize(2);
- SslSessionWrapper a = newSession("a");
- SslSessionWrapper b = newSession("b");
- SslSessionWrapper c = newSession("c");
- SslSessionWrapper d = newSession("d");
+ NativeSslSession a = newSession("a");
+ NativeSslSession b = newSession("b");
+ NativeSslSession c = newSession("c");
+ NativeSslSession d = newSession("d");
context.cacheSession(a);
assertSessionContextContents(toArray(a), toArray(b, c, d));
@@ -98,7 +102,7 @@
when(mockCert.getEncoded()).thenReturn(new byte[] {0x05, 0x06, 0x07, 0x10});
byte[] encodedBytes = new byte[] {0x01, 0x02, 0x03};
- SslSessionWrapper session = new MockSessionBuilder()
+ NativeSslSession session = new MockSessionBuilder()
.id(new byte[] {0x11, 0x09, 0x03, 0x20})
.host("ssl.example.com")
.encodedBytes(encodedBytes)
@@ -113,22 +117,18 @@
}
private void assertSessionContextContents(
- SslSessionWrapper[] contains, SslSessionWrapper[] exludes) {
+ NativeSslSession[] contains, NativeSslSession[] exludes) {
assertEquals(contains.length, size(context));
- for (SslSessionWrapper s : contains) {
+ for (NativeSslSession s : contains) {
assertSame(s.getPeerHost(), s, getCachedSession(context, s));
}
- for (SslSessionWrapper s : exludes) {
+ for (NativeSslSession s : exludes) {
assertNull(s.getPeerHost(), getCachedSession(context, s));
}
}
- private static SslSessionWrapper[] toArray(SslSessionWrapper... sessions) {
- return sessions;
- }
-
- private SslSessionWrapper newSession(String host) {
+ private NativeSslSession newSession(String host) {
return new MockSessionBuilder().host(host).build();
}
}
diff --git a/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java
index 709b893..6a20740 100644
--- a/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java
@@ -31,7 +31,7 @@
}
@Override
- SslSessionWrapper getCachedSession(ClientSessionContext context, SslSessionWrapper s) {
+ NativeSslSession getCachedSession(ClientSessionContext context, NativeSslSession s) {
return context.getCachedSession(s.getPeerHost(), DEFAULT_PORT,
getDefaultSSLParameters());
}
diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
index d536830..71ccff7 100644
--- a/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
@@ -44,6 +44,7 @@
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
import libcore.java.security.TestKeyStore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -360,6 +361,20 @@
exchangeMessage(newMessage(MESSAGE_SIZE), serverEngine, clientEngine);
}
+ @Test
+ public void savedSessionWorksAfterClose() throws Exception {
+ setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+ doHandshake(true);
+
+ SSLSession session = clientEngine.getSession();
+ String cipherSuite = session.getCipherSuite();
+
+ clientEngine.closeOutbound();
+ clientEngine.closeInbound();
+
+ assertEquals(cipherSuite, session.getCipherSuite());
+ }
+
private void doMutualAuthHandshake(
TestKeyStore clientKs, TestKeyStore serverKs, ClientAuth clientAuth) throws Exception {
setupEngines(clientKs, serverKs);
diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
index cffb5a4..385c58c 100644
--- a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
@@ -48,6 +48,7 @@
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
@@ -610,6 +611,19 @@
assertFalse(connection.serverHooks.isHandshakeCompleted);
}
+ @Test
+ public void savedSessionWorksAfterClose() throws Exception {
+ TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+ connection.doHandshake();
+
+ SSLSession session = connection.client.getSession();
+ String cipherSuite = session.getCipherSuite();
+
+ connection.client.close();
+
+ assertEquals(cipherSuite, session.getCipherSuite());
+ }
+
private static ServerSocket newServerSocket() throws IOException {
return new ServerSocket(0, 50, TestUtils.getLoopbackAddress());
}
diff --git a/openjdk/src/test/java/org/conscrypt/MockSessionBuilder.java b/openjdk/src/test/java/org/conscrypt/MockSessionBuilder.java
index 7cff17a..4883bac 100644
--- a/openjdk/src/test/java/org/conscrypt/MockSessionBuilder.java
+++ b/openjdk/src/test/java/org/conscrypt/MockSessionBuilder.java
@@ -63,8 +63,8 @@
return this;
}
- SslSessionWrapper build() {
- SslSessionWrapper session = mock(SslSessionWrapper.class);
+ NativeSslSession build() {
+ NativeSslSession session = mock(NativeSslSession.class);
byte[] id = this.id == null ? host.getBytes(UTF_8) : this.id;
when(session.getId()).thenReturn(id);
when(session.isValid()).thenReturn(valid);
diff --git a/openjdk/src/test/java/org/conscrypt/SslSessionWrapperTest.java b/openjdk/src/test/java/org/conscrypt/NativeSslSessionTest.java
similarity index 98%
rename from openjdk/src/test/java/org/conscrypt/SslSessionWrapperTest.java
rename to openjdk/src/test/java/org/conscrypt/NativeSslSessionTest.java
index dc600d4..c308d71 100644
--- a/openjdk/src/test/java/org/conscrypt/SslSessionWrapperTest.java
+++ b/openjdk/src/test/java/org/conscrypt/NativeSslSessionTest.java
@@ -28,7 +28,7 @@
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
-public class SslSessionWrapperTest {
+public class NativeSslSessionTest {
/*
* Taken from external/boringssl/src/ssl/ssl_test.cc: kOpenSSLSession is a
* serialized SSL_SESSION.
@@ -433,14 +433,8 @@
assertValidSession(getType3().build());
}
- private void assertTruncatedSessionFails(byte[] validSession) {
- for (int i = 0; i < validSession.length - 1; i++) {
- byte[] truncatedSession = new byte[i];
- System.arraycopy(validSession, 0, truncatedSession, 0, i);
- assertNull("Truncating to " + i + " bytes of " + validSession.length
- + " should not succeed",
- SslSessionWrapper.newInstance(null, truncatedSession, "www.google.com", 443));
- }
+ private static void assertValidSession(byte[] data) {
+ assertNotNull(NativeSslSession.newInstance(null, data, "www.google.com", 443));
}
@Test
@@ -448,12 +442,31 @@
assertTruncatedSessionFails(getType3().build());
}
- private static void assertValidSession(byte[] data) {
- assertNotNull(SslSessionWrapper.newInstance(null, data, "www.google.com", 443));
+ private static void assertInvalidSession(byte[] data) {
+ assertNull(NativeSslSession.newInstance(null, data, "www.google.com", 443));
}
- private static void assertInvalidSession(byte[] data) {
- assertNull(SslSessionWrapper.newInstance(null, data, "www.google.com", 443));
+ private static void check_reserializableFromByteArray_roundTrip(
+ byte[] data, byte[] expectedTrailingBytesAfterReserialization) throws Exception {
+ NativeSslSession session =
+ NativeSslSession.newInstance(null, data, "www.example.com", 12345);
+ byte[] sessionBytes = session.toBytes();
+
+ NativeSslSession session2 =
+ NativeSslSession.newInstance(null, sessionBytes, "www.example.com", 12345);
+ byte[] sessionBytes2 = session2.toBytes();
+
+ assertSSLSessionEquals(session, session2);
+ assertByteArrayEquals(sessionBytes, sessionBytes2);
+
+ assertEquals("www.example.com", session.getPeerHost());
+ assertEquals(12345, session.getPeerPort());
+ assertTrue(sessionBytes.length >= data.length);
+
+ byte[] expectedReserializedData = concat(data, expectedTrailingBytesAfterReserialization);
+ // AbstractSessionContext.toBytes() always writes type 3 == OPEN_SSL_WITH_TLS_SCT
+ expectedReserializedData[3] = 3;
+ assertByteArrayEquals(expectedReserializedData, sessionBytes);
}
@Test
@@ -581,27 +594,13 @@
check_reserializableFromByteArray_roundTrip(getType3().build(), new byte[0]);
}
- private static void check_reserializableFromByteArray_roundTrip(
- byte[] data, byte[] expectedTrailingBytesAfterReserialization) throws Exception {
- SslSessionWrapper session =
- SslSessionWrapper.newInstance(null, data, "www.example.com", 12345);
- byte[] sessionBytes = session.toBytes();
-
- SslSessionWrapper session2 =
- SslSessionWrapper.newInstance(null, sessionBytes, "www.example.com", 12345);
- byte[] sessionBytes2 = session2.toBytes();
-
- assertSSLSessionEquals(session, session2);
- assertByteArrayEquals(sessionBytes, sessionBytes2);
-
- assertEquals("www.example.com", session.getPeerHost());
- assertEquals(12345, session.getPeerPort());
- assertTrue(sessionBytes.length >= data.length);
-
- byte[] expectedReserializedData = concat(data, expectedTrailingBytesAfterReserialization);
- // AbstractSessionContext.toBytes() always writes type 3 == OPEN_SSL_WITH_TLS_SCT
- expectedReserializedData[3] = 3;
- assertByteArrayEquals(expectedReserializedData, sessionBytes);
+ private static void assertSSLSessionEquals(NativeSslSession a, NativeSslSession b)
+ throws Exception {
+ assertEquals(a.getCipherSuite(), b.getCipherSuite());
+ assertByteArrayEquals(a.getId(), b.getId());
+ assertEquals(a.getPeerHost(), b.getPeerHost());
+ assertEquals(a.getPeerPort(), b.getPeerPort());
+ assertEquals(a.getProtocol(), b.getProtocol());
}
private static byte[] concat(byte[] a, byte[] b) {
@@ -611,13 +610,14 @@
return result;
}
- private static void assertSSLSessionEquals(SslSessionWrapper a, SslSessionWrapper b)
- throws Exception {
- assertEquals(a.getCipherSuite(), b.getCipherSuite());
- assertByteArrayEquals(a.getId(), b.getId());
- assertEquals(a.getPeerHost(), b.getPeerHost());
- assertEquals(a.getPeerPort(), b.getPeerPort());
- assertEquals(a.getProtocol(), b.getProtocol());
+ private void assertTruncatedSessionFails(byte[] validSession) {
+ for (int i = 0; i < validSession.length - 1; i++) {
+ byte[] truncatedSession = new byte[i];
+ System.arraycopy(validSession, 0, truncatedSession, 0, i);
+ assertNull("Truncating to " + i + " bytes of " + validSession.length
+ + " should not succeed",
+ NativeSslSession.newInstance(null, truncatedSession, "www.google.com", 443));
+ }
}
private static void assertByteArrayEquals(byte[] expected, byte[] actual) {
diff --git a/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java
index 7d1a8ce..1b1ed0b 100644
--- a/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java
@@ -29,7 +29,7 @@
}
@Override
- SslSessionWrapper getCachedSession(ServerSessionContext context, SslSessionWrapper s) {
+ NativeSslSession getCachedSession(ServerSessionContext context, NativeSslSession s) {
return context.getSessionFromCache(s.getId());
}
diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java
index d16393f..3c76ef8 100644
--- a/platform/src/main/java/org/conscrypt/Platform.java
+++ b/platform/src/main/java/org/conscrypt/Platform.java
@@ -426,16 +426,11 @@
}
}
- /*
- * Pre-Java 8 backward compatibility.
+ /**
+ * Provides extended capabilities for the session if supported by the platform.
*/
-
- static SSLSession wrapSSLSession(ActiveSession sslSession) {
- return new Java7SessionWrapper(sslSession);
- }
-
- static SSLSession unwrapSSLSession(SSLSession sslSession) {
- return Java7SessionWrapper.getDelegate(sslSession);
+ static SSLSession wrapSSLSession(ConscryptSession sslSession) {
+ return new Java8ExtendedSSLSession(sslSession);
}
public static String getOriginalHostNameFromInetAddress(InetAddress addr) {
diff --git a/platform/src/main/java/org/conscrypt/TrustManagerImpl.java b/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
index 95f7f48..7ef4d07 100644
--- a/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
+++ b/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
@@ -420,8 +420,8 @@
private byte[] getOcspDataFromSession(SSLSession session) {
List<byte[]> ocspResponses = null;
- if (session instanceof ActiveSession) {
- ActiveSession opensslSession = (ActiveSession) session;
+ if (session instanceof ConscryptSession) {
+ ConscryptSession opensslSession = (ConscryptSession) session;
ocspResponses = opensslSession.getStatusResponses();
} else {
Method m_getResponses;
@@ -447,8 +447,8 @@
}
private byte[] getTlsSctDataFromSession(SSLSession session) {
- if (session instanceof ActiveSession) {
- ActiveSession opensslSession = (ActiveSession) session;
+ if (session instanceof ConscryptSession) {
+ ConscryptSession opensslSession = (ConscryptSession) session;
return opensslSession.getPeerSignedCertificateTimestamp();
}
diff --git a/test_logging.properties b/test_logging.properties
index 89be04d..1204c2d 100644
--- a/test_logging.properties
+++ b/test_logging.properties
@@ -8,4 +8,4 @@
org.conscrypt.handler=java.util.logging.ConsoleHandler
# Avoid nuisance logs in tests.
-org.conscrypt.SslSessionWrapper.level=SEVERE
+org.conscrypt.NativeSslSession.level=SEVERE